minPitchAnalyst.py 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  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. dataFrameMerge = self.userDataFrame(dictionary,conf.dataContract.configAnalysis,self)
  20. return self.drawTrendGraph(dataFrameMerge, outputAnalysisDir, conf)
  21. def drawTrendGraph(self, dataFrameMerge: pd.DataFrame, outputAnalysisDir, conf: Contract):
  22. """
  23. Generates pitch angle distribution scatter plots for turbines in a wind farm using plotly.
  24. Parameters:
  25. - dataFrameMerge: pd.DataFrame, DataFrame containing turbine data.
  26. - outputAnalysisDir: str, path to save the output plots.
  27. - conf: Contract, configuration object containing field names.
  28. """
  29. # 检查所需列是否存在
  30. required_columns = {Field_YearMonthDay, Field_PitchAngel1}
  31. if not required_columns.issubset(dataFrameMerge.columns):
  32. raise ValueError(f"DataFrame缺少必要的列。需要的列有: {required_columns}")
  33. pitchAngleRate = 'pitch_angle_rate'
  34. fieldPitchAngleBin = 'pitch_angle_bin'
  35. # Custom color scale: Blue (high pitch_angle_rate) to Light Grey (low pitch_angle_rate)
  36. # custom_color_scale = [
  37. # (0.0, "rgb(255, 255, 255)"), # white for the lowest values
  38. # (0.05, "rgb(240, 240, 240)"), # Light grey for the lowest values
  39. # (0.5, "rgba(55.0, 135.0, 192.33333333333334, 1.0)"), # Medium blue-grey
  40. # # (0.75, "rgba(55.0, 135.0, 192.33333333333334, 1.0)"), # Medium blue-grey
  41. # # Dark blue for the highest values
  42. # (1.0, "rgba(55.0, 135.0, 192.33333333333334, 1.0)")
  43. # ]
  44. custom_color_scale = [
  45. (0.0, "rgb(255, 255, 255)"), # White for the lowest values (0%)
  46. # (0.05, "rgb(135, 206, 235)"),# Sky blue for low but non-zero values
  47. (0.05, "rgb(30, 144, 255)"), # Dodger blue for for low but non-zero values
  48. (0.5, "rgb(0, 0, 255)"), # Blue for higher values
  49. (1.0, "rgb(0, 0, 139)") # Dark blue (Dark slate blue)
  50. ]
  51. # Group data by turbine identifier
  52. # dataFrameMerge = dataFrameMerge[dataFrameMerge[Field_PitchAngel1] <= 5]
  53. grouped = dataFrameMerge.groupby(
  54. [Field_NameOfTurbine, Field_CodeOfTurbine])
  55. result_rows = []
  56. for name, group in grouped:
  57. # Convert the date column to datetime type
  58. group[Field_YearMonthDay] = pd.to_datetime(
  59. group[Field_YearMonthDay])
  60. # Creating bins of 0.2 intervals for pitch angles
  61. bins = pd.interval_range(start=group[Field_PitchAngel1].min(),
  62. end=group[Field_PitchAngel1].max(
  63. ) + 0.1, freq=0.2, closed='right')
  64. group[fieldPitchAngleBin] = pd.cut(
  65. group[Field_PitchAngel1], bins=bins)
  66. group[fieldPitchAngleBin] = group[fieldPitchAngleBin].apply(
  67. lambda x: x.left) # 提取每个区间的左端点作为值
  68. # Calculate the pitch angle rate within each day
  69. # df = group.groupby([Field_YearMonthDay, Field_PitchAngel1]
  70. # ).size().reset_index(name='count')
  71. df = group.groupby([Field_YearMonthDay, fieldPitchAngleBin]
  72. ).size().reset_index(name='count')
  73. total_counts = group.groupby(Field_YearMonthDay
  74. ).size().reset_index(name='total_count')
  75. df = df.merge(total_counts, on=Field_YearMonthDay)
  76. # df[pitchAngleRate] = df['count'] / df['total_count'] * 100
  77. # df[pitchAngleRate] = (df['count'] / df['total_count']).apply(lambda x: x ** 0.5)*100
  78. df[pitchAngleRate] = (df['count'] / df['total_count']) * 100
  79. # Plotting using plotly
  80. fig = px.scatter(df,
  81. x=Field_YearMonthDay,
  82. # y=Field_PitchAngel1, # 桨距角不分仓方式
  83. y=fieldPitchAngleBin, # 桨距角分仓方式
  84. size='count',
  85. color=pitchAngleRate,
  86. # color_continuous_scale='Blues',
  87. color_continuous_scale=custom_color_scale
  88. )
  89. # Set date format on x-axis
  90. fig.update_xaxes(
  91. title='时间', tickformat='%Y-%m-%d', tickangle=-45)
  92. fig.update_yaxes(title='桨距角',
  93. dtick=self.axisStepPitchAngle,
  94. range=[self.axisLowerLimitPitchAngle, 10],
  95. )
  96. # Customizing legend
  97. fig.update_layout(
  98. title={
  99. "text": f' {name[0]}的最小桨距角分布',
  100. "x": 0.5
  101. },
  102. coloraxis_colorbar=dict(title='百分比'),
  103. margin=dict(t=50, b=10), # t为顶部(top)间距,b为底部(bottom)间距
  104. # plot_bgcolor='rgb(240, 240, 240)'
  105. )
  106. # Set marker size if fixed size is needed
  107. # Fixed size for all points
  108. fig.update_traces(marker=dict(size=3, opacity=0.9))
  109. # Save plot
  110. filePathOfImage = os.path.join(outputAnalysisDir, f"{name[0]}.png")
  111. fig.write_image(filePathOfImage, scale=3)
  112. filePathOfHtml = os.path.join(outputAnalysisDir, f"{name[0]}.html")
  113. fig.write_html(filePathOfHtml)
  114. result_rows.append({
  115. Field_Return_TypeAnalyst: self.typeAnalyst(),
  116. Field_PowerFarmCode: conf.dataContract.dataFilter.powerFarmID,
  117. Field_Return_BatchCode: conf.dataContract.dataFilter.dataBatchNum,
  118. Field_CodeOfTurbine: name[1],
  119. Field_Return_FilePath: filePathOfImage,
  120. Field_Return_IsSaveDatabase: False
  121. })
  122. result_rows.append({
  123. Field_Return_TypeAnalyst: self.typeAnalyst(),
  124. Field_PowerFarmCode: conf.dataContract.dataFilter.powerFarmID,
  125. Field_Return_BatchCode: conf.dataContract.dataFilter.dataBatchNum,
  126. Field_CodeOfTurbine: name[1],
  127. Field_Return_FilePath: filePathOfHtml,
  128. Field_Return_IsSaveDatabase: True
  129. })
  130. result_df = pd.DataFrame(result_rows)
  131. return result_df