minPitchAnalyst.py 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. import os
  2. import pandas as pd
  3. import plotly.express as px
  4. from algorithmContract.confBusiness import *
  5. from algorithmContract.contract import Contract
  6. from behavior.analystWithGoodBadPoint import AnalystWithGoodBadPoint
  7. class MinPitchAnalyst(AnalystWithGoodBadPoint):
  8. """
  9. 风电机组最小桨距角分析
  10. """
  11. def typeAnalyst(self):
  12. return "min_pitch"
  13. def selectColums(self):
  14. return [Field_DeviceCode,Field_Time,Field_WindSpeed,Field_ActiverPower,Field_PitchAngel1]
  15. def processDateTime(self, dataFrame: pd.DataFrame, fieldTime:str = None):
  16. super().processDateTime(dataFrame,Field_YearMonthDay)
  17. def turbinesAnalysis(self, outputAnalysisDir, conf: Contract, turbineCodes):
  18. dictionary = self.processTurbineData(turbineCodes,conf,self.selectColums())
  19. turbineInfos = self.common.getTurbineInfos(conf.dataContract.dataFilter.powerFarmID, turbineCodes, self.turbineInfo)
  20. dataFrameMerge = self.userDataFrame(dictionary,conf.dataContract.configAnalysis,self)
  21. return self.drawTrendGraph(dataFrameMerge,turbineInfos, outputAnalysisDir, conf)
  22. def drawTrendGraph(self, dataFrameMerge: pd.DataFrame, turbineModelInfo: pd.Series,outputAnalysisDir, conf: Contract):
  23. """
  24. Generates pitch angle distribution scatter plots for turbines in a wind farm using plotly.
  25. Parameters:
  26. - dataFrameMerge: pd.DataFrame, DataFrame containing turbine data.
  27. - outputAnalysisDir: str, path to save the output plots.
  28. - conf: Contract, configuration object containing field names.
  29. """
  30. # 检查所需列是否存在
  31. required_columns = {Field_YearMonthDay, Field_PitchAngel1}
  32. if not required_columns.issubset(dataFrameMerge.columns):
  33. raise ValueError(f"DataFrame缺少必要的列。需要的列有: {required_columns}")
  34. pitchAngleRate = 'pitch_angle_rate'
  35. fieldPitchAngleBin = 'pitch_angle_bin'
  36. # Custom color scale: Blue (high pitch_angle_rate) to Light Grey (low pitch_angle_rate)
  37. # custom_color_scale = [
  38. # (0.0, "rgb(255, 255, 255)"), # white for the lowest values
  39. # (0.05, "rgb(240, 240, 240)"), # Light grey for the lowest values
  40. # (0.5, "rgba(55.0, 135.0, 192.33333333333334, 1.0)"), # Medium blue-grey
  41. # # (0.75, "rgba(55.0, 135.0, 192.33333333333334, 1.0)"), # Medium blue-grey
  42. # # Dark blue for the highest values
  43. # (1.0, "rgba(55.0, 135.0, 192.33333333333334, 1.0)")
  44. # ]
  45. custom_color_scale = [
  46. (0.0, "rgb(255, 255, 255)"), # White for the lowest values (0%)
  47. # (0.05, "rgb(135, 206, 235)"),# Sky blue for low but non-zero values
  48. (0.05, "rgb(30, 144, 255)"), # Dodger blue for for low but non-zero values
  49. (0.5, "rgb(0, 0, 255)"), # Blue for higher values
  50. (1.0, "rgb(0, 0, 139)") # Dark blue (Dark slate blue)
  51. ]
  52. # Group data by turbine identifier
  53. # dataFrameMerge = dataFrameMerge[dataFrameMerge[Field_PitchAngel1] <= 5]
  54. grouped = dataFrameMerge.groupby(
  55. [Field_NameOfTurbine, Field_CodeOfTurbine])
  56. # 创建一个列表来存储各个风电机组的数据
  57. turbine_data_list = []
  58. result_rows = []
  59. for name, group in grouped:
  60. # Convert the date column to datetime type
  61. group[Field_YearMonthDay] = pd.to_datetime(
  62. group[Field_YearMonthDay])
  63. # Creating bins of 0.2 intervals for pitch angles
  64. bins = pd.interval_range(start=group[Field_PitchAngel1].min(),
  65. end=group[Field_PitchAngel1].max(
  66. ) + 0.1, freq=0.2, closed='right')
  67. group[fieldPitchAngleBin] = pd.cut(
  68. group[Field_PitchAngel1], bins=bins)
  69. group[fieldPitchAngleBin] = group[fieldPitchAngleBin].apply(
  70. lambda x: x.left) # 提取每个区间的左端点作为值
  71. # Calculate the pitch angle rate within each day
  72. # df = group.groupby([Field_YearMonthDay, Field_PitchAngel1]
  73. # ).size().reset_index(name='count')
  74. df = group.groupby([Field_YearMonthDay, fieldPitchAngleBin]
  75. ).size().reset_index(name='count')
  76. total_counts = group.groupby(Field_YearMonthDay
  77. ).size().reset_index(name='total_count')
  78. df = df.merge(total_counts, on=Field_YearMonthDay)
  79. # df[pitchAngleRate] = df['count'] / df['total_count'] * 100
  80. # df[pitchAngleRate] = (df['count'] / df['total_count']).apply(lambda x: x ** 0.5)*100
  81. df[pitchAngleRate] = (df['count'] / df['total_count']) * 100
  82. # Plotting using plotly
  83. fig = px.scatter(df,
  84. x=Field_YearMonthDay,
  85. # y=Field_PitchAngel1, # 桨距角不分仓方式
  86. y=fieldPitchAngleBin, # 桨距角分仓方式
  87. size='count',
  88. color=pitchAngleRate,
  89. # color_continuous_scale='Blues',
  90. color_continuous_scale=custom_color_scale
  91. )
  92. # Set date format on x-axis
  93. fig.update_xaxes(
  94. title='时间', tickformat='%Y-%m-%d', tickangle=-45)
  95. fig.update_yaxes(title='桨距角',
  96. dtick=self.axisStepPitchAngle,
  97. range=[self.axisLowerLimitPitchAngle, 10],
  98. )
  99. # Customizing legend
  100. fig.update_layout(
  101. title={
  102. "text": f' {name[0]}的最小桨距角分布',
  103. "x": 0.5
  104. },
  105. coloraxis_colorbar=dict(title='百分比'),
  106. margin=dict(t=50, b=10), # t为顶部(top)间距,b为底部(bottom)间距
  107. # plot_bgcolor='rgb(240, 240, 240)'
  108. )
  109. # Set marker size if fixed size is needed
  110. # Fixed size for all points
  111. fig.update_traces(marker=dict(size=3, opacity=0.9))
  112. # 确保从 Series 中提取的是具体的值
  113. engineTypeCode = turbineModelInfo.get(Field_MillTypeCode, "")
  114. if isinstance(engineTypeCode, pd.Series):
  115. engineTypeCode = engineTypeCode.iloc[0]
  116. engineTypeName = turbineModelInfo.get(Field_MachineTypeCode, "")
  117. if isinstance(engineTypeName, pd.Series):
  118. engineTypeName = engineTypeName.iloc[0]
  119. # 构建最终的JSON对象
  120. json_output = {
  121. "analysisTypeCode": "最小桨距角分析",
  122. "engineCode": engineTypeCode,
  123. "engineTypeName": engineTypeName,
  124. "xaixs": "时间",
  125. "yaixs": "桨距角(度)",
  126. "data": [{
  127. "engineName": name[0],
  128. "engineCode": name[1],
  129. "title":f' {name[0]}的最小桨距角分布',
  130. "xData": df[Field_YearMonthDay].dt.strftime('%Y-%m-%d').tolist(),
  131. "yData": df[fieldPitchAngleBin].tolist(),
  132. "colorbar": df[pitchAngleRate].tolist(),
  133. "colorbartitle": "百分比(%)"
  134. }]
  135. }
  136. # Save plot
  137. filePathOfImage = os.path.join(outputAnalysisDir, f"{name[0]}.png")
  138. fig.write_image(filePathOfImage, scale=3)
  139. # filePathOfHtml = os.path.join(outputAnalysisDir, f"{name[0]}.html")
  140. # fig.write_html(filePathOfHtml)
  141. # 将JSON对象保存到文件
  142. output_json_path = os.path.join(outputAnalysisDir, f"min_pitch{name[0]}.json")
  143. with open(output_json_path, 'w', encoding='utf-8') as f:
  144. import json
  145. json.dump(json_output, f, ensure_ascii=False, indent=4)
  146. # 如果需要返回DataFrame,可以包含文件路径
  147. result_rows.append({
  148. Field_Return_TypeAnalyst: self.typeAnalyst(),
  149. Field_PowerFarmCode: conf.dataContract.dataFilter.powerFarmID,
  150. Field_Return_BatchCode: conf.dataContract.dataFilter.dataBatchNum,
  151. Field_CodeOfTurbine: name[1],
  152. Field_Return_FilePath: output_json_path,
  153. Field_Return_IsSaveDatabase: True
  154. })
  155. result_rows.append({
  156. Field_Return_TypeAnalyst: self.typeAnalyst(),
  157. Field_PowerFarmCode: conf.dataContract.dataFilter.powerFarmID,
  158. Field_Return_BatchCode: conf.dataContract.dataFilter.dataBatchNum,
  159. Field_CodeOfTurbine: name[1],
  160. Field_Return_FilePath: filePathOfImage,
  161. Field_Return_IsSaveDatabase: False
  162. })
  163. # result_rows.append({
  164. # Field_Return_TypeAnalyst: self.typeAnalyst(),
  165. # Field_PowerFarmCode: conf.dataContract.dataFilter.powerFarmID,
  166. # Field_Return_BatchCode: conf.dataContract.dataFilter.dataBatchNum,
  167. # Field_CodeOfTurbine: name[1],
  168. # Field_Return_FilePath: filePathOfHtml,
  169. # Field_Return_IsSaveDatabase: True
  170. # })
  171. result_df = pd.DataFrame(result_rows)
  172. return result_df