minPitchAnalyst.py 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  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. # 创建筛选条件,桨距角小于10
  64. condition = group[Field_PitchAngel1] < 10
  65. # 生成筛选后的子数据集(避免修改原始 group)
  66. group = group[condition].copy()
  67. # Creating bins of 0.2 intervals for pitch angles(分仓步长从0.2改为0.1)
  68. bins = pd.interval_range(start=group[Field_PitchAngel1].min(),
  69. end=group[Field_PitchAngel1].max(
  70. ) + 0.1, freq=0.1, closed='right')
  71. group[fieldPitchAngleBin] = pd.cut(
  72. group[Field_PitchAngel1], bins=bins)
  73. group[fieldPitchAngleBin] = group[fieldPitchAngleBin].apply(
  74. lambda x: x.left) # 提取每个区间的左端点作为值
  75. # Calculate the pitch angle rate within each day
  76. # df = group.groupby([Field_YearMonthDay, Field_PitchAngel1]
  77. # ).size().reset_index(name='count')
  78. df = group.groupby([Field_YearMonthDay, fieldPitchAngleBin]
  79. ).size().reset_index(name='count')
  80. total_counts = group.groupby(Field_YearMonthDay
  81. ).size().reset_index(name='total_count')
  82. df = df.merge(total_counts, on=Field_YearMonthDay)
  83. # df[pitchAngleRate] = df['count'] / df['total_count'] * 100
  84. # df[pitchAngleRate] = (df['count'] / df['total_count']).apply(lambda x: x ** 0.5)*100
  85. df[pitchAngleRate] = (df['count'] / df['total_count']) * 100
  86. # Plotting using plotly
  87. fig = px.scatter(df,
  88. x=Field_YearMonthDay,
  89. # y=Field_PitchAngel1, # 桨距角不分仓方式
  90. y=fieldPitchAngleBin, # 桨距角分仓方式
  91. size='count',
  92. color=pitchAngleRate,
  93. # color_continuous_scale='Blues',
  94. color_continuous_scale=custom_color_scale
  95. )
  96. # Set date format on x-axis
  97. fig.update_xaxes(
  98. title='时间', tickformat='%Y-%m-%d', tickangle=-45)
  99. fig.update_yaxes(title='桨距角',
  100. dtick=self.axisStepPitchAngle,
  101. range=[self.axisLowerLimitPitchAngle, 10],
  102. )
  103. # Customizing legend
  104. fig.update_layout(
  105. title={
  106. "text": f' {name[0]}的最小桨距角分布',
  107. "x": 0.5
  108. },
  109. coloraxis_colorbar=dict(title='百分比'),
  110. margin=dict(t=50, b=10), # t为顶部(top)间距,b为底部(bottom)间距
  111. # plot_bgcolor='rgb(240, 240, 240)'
  112. )
  113. # Set marker size if fixed size is needed
  114. # Fixed size for all points
  115. fig.update_traces(marker=dict(size=3, opacity=0.9))
  116. # 确保从 Series 中提取的是具体的值
  117. engineTypeCode = turbineModelInfo.get(Field_MillTypeCode, "")
  118. if isinstance(engineTypeCode, pd.Series):
  119. engineTypeCode = engineTypeCode.iloc[0]
  120. engineTypeName = turbineModelInfo.get(Field_MachineTypeCode, "")
  121. if isinstance(engineTypeName, pd.Series):
  122. engineTypeName = engineTypeName.iloc[0]
  123. # 构建最终的JSON对象
  124. json_output = {
  125. "analysisTypeCode": "最小桨距角分析",
  126. "engineCode": engineTypeCode,
  127. "engineTypeName": engineTypeName,
  128. "xaixs": "时间",
  129. "yaixs": "桨距角(°)",
  130. "data": [{
  131. "engineName": name[0],
  132. "engineCode": name[1],
  133. "title":f' {name[0]}的最小桨距角分布',
  134. "xData": df[Field_YearMonthDay].dt.strftime('%Y-%m-%d').tolist(),
  135. "yData": df[fieldPitchAngleBin].tolist(),
  136. "colorbar": df[pitchAngleRate].tolist(),
  137. "colorbartitle": "百分比(%)"
  138. }]
  139. }
  140. # Save plot
  141. filePathOfImage = os.path.join(outputAnalysisDir, f"{name[0]}.png")
  142. fig.write_image(filePathOfImage, scale=3)
  143. # filePathOfHtml = os.path.join(outputAnalysisDir, f"{name[0]}.html")
  144. # fig.write_html(filePathOfHtml)
  145. # 将JSON对象保存到文件
  146. output_json_path = os.path.join(outputAnalysisDir, f"min_pitch{name[0]}.json")
  147. with open(output_json_path, 'w', encoding='utf-8') as f:
  148. import json
  149. json.dump(json_output, f, ensure_ascii=False, indent=4)
  150. # 如果需要返回DataFrame,可以包含文件路径
  151. result_rows.append({
  152. Field_Return_TypeAnalyst: self.typeAnalyst(),
  153. Field_PowerFarmCode: conf.dataContract.dataFilter.powerFarmID,
  154. Field_Return_BatchCode: conf.dataContract.dataFilter.dataBatchNum,
  155. Field_CodeOfTurbine: name[1],
  156. Field_Return_FilePath: output_json_path,
  157. Field_Return_IsSaveDatabase: True
  158. })
  159. result_rows.append({
  160. Field_Return_TypeAnalyst: self.typeAnalyst(),
  161. Field_PowerFarmCode: conf.dataContract.dataFilter.powerFarmID,
  162. Field_Return_BatchCode: conf.dataContract.dataFilter.dataBatchNum,
  163. Field_CodeOfTurbine: name[1],
  164. Field_Return_FilePath: filePathOfImage,
  165. Field_Return_IsSaveDatabase: False
  166. })
  167. # result_rows.append({
  168. # Field_Return_TypeAnalyst: self.typeAnalyst(),
  169. # Field_PowerFarmCode: conf.dataContract.dataFilter.powerFarmID,
  170. # Field_Return_BatchCode: conf.dataContract.dataFilter.dataBatchNum,
  171. # Field_CodeOfTurbine: name[1],
  172. # Field_Return_FilePath: filePathOfHtml,
  173. # Field_Return_IsSaveDatabase: True
  174. # })
  175. result_df = pd.DataFrame(result_rows)
  176. return result_df