yawErrorDensityAnalyst.py 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. import os
  2. import pandas as pd
  3. import numpy as np
  4. import plotly.graph_objects as go
  5. from algorithmContract.confBusiness import *
  6. from algorithmContract.contract import Contract
  7. from behavior.analystWithGoodPoint import AnalystWithGoodPoint
  8. from scipy.stats import binned_statistic_2d
  9. from scipy.stats import skew, kurtosis
  10. class YawErrorDensityAnalyst(AnalystWithGoodPoint):
  11. """
  12. 风电机组动态偏航策略分析
  13. """
  14. def typeAnalyst(self):
  15. return "yaw_error_density"
  16. def selectColumns(self):
  17. return [Field_DeviceCode,Field_Time,Field_WindSpeed,Field_ActiverPower,Field_YawError]
  18. def calculateYawError(self, dataFrame: pd.DataFrame):
  19. dataFrame = dataFrame.dropna(
  20. subset=[Field_NameOfTurbine, Field_YawError, Field_ActiverPower,Field_WindSpeed])
  21. filtered_dataFrame = dataFrame[(dataFrame[Field_YawError].abs() <= 30)&(dataFrame[Field_WindSpeed] >= 0)&(dataFrame[Field_WindSpeed] <= 25)]
  22. x=filtered_dataFrame[Field_YawError]
  23. y=filtered_dataFrame[Field_WindSpeed]
  24. # data = np.column_stack((x, y)) # 合并为两列数组
  25. # 使用 binned_statistic_2d 来计算散点的密度分布
  26. binSize_x = 60
  27. binSize_y = 50
  28. counts, x_edges, y_edges, binnumber = binned_statistic_2d(x, y, values=None, statistic='count', bins=[binSize_x, binSize_y])
  29. # 将数据密度转化为颜色值
  30. binX = np.digitize(x, x_edges) - 1
  31. binY = np.digitize(y, y_edges) - 1
  32. # 删除超出范围的下标
  33. validIdx = (binX >= 0) & (binX < binSize_x) & (binY >= 0) & (binY < binSize_y)
  34. # 获取有效下标的密度
  35. density = np.zeros(len(x))
  36. density[validIdx] = counts[binX[validIdx], binY[validIdx]]
  37. # 将结果保存到 result 中
  38. result = {
  39. 'x': x,
  40. 'y': y,
  41. 'density': density
  42. }
  43. # 将 result 转换为 DataFrame
  44. result_df = pd.DataFrame(result)
  45. return result_df
  46. def turbinesAnalysis(self, outputAnalysisDir, conf: Contract, turbineCodes):
  47. dictionary = self.processTurbineData(turbineCodes,conf,self.selectColumns())
  48. dataFrameMerge = self.userDataFrame(dictionary,conf.dataContract.configAnalysis,self)
  49. # dataFrameMerge.to_csv('D:\dashengkeji\project\WTOAAM\debug_tset_20241008\dataFrameJHS_WOG00935.csv',index=False)
  50. return self.yawErrorAnalysis(dataFrameMerge, outputAnalysisDir, conf)
  51. def yawErrorAnalysis(self, dataFrameMerge: pd.DataFrame, outputAnalysisDir, conf: Contract):
  52. # 检查所需列是否存在
  53. required_columns = {Field_ActiverPower, Field_YawError,Field_WindSpeed}
  54. if not required_columns.issubset(dataFrameMerge.columns):
  55. raise ValueError(f"DataFrame缺少必要的列。需要的列有: {required_columns}")
  56. result_rows = []
  57. grouped = dataFrameMerge.groupby(
  58. [Field_NameOfTurbine, Field_CodeOfTurbine])
  59. # 定义固定的颜色映射列表
  60. fixed_colors = [
  61. "#3E409C",
  62. "#476CB9",
  63. "#3586BF",
  64. "#4FA4B5",
  65. "#52A3AE",
  66. "#60C5A3",
  67. "#85D0AE",
  68. "#A8DCA2",
  69. "#CFEE9E",
  70. "#E4F39E",
  71. "#EEF9A7",
  72. "#FBFFBE",
  73. "#FDF1A9",
  74. "#FFE286",
  75. "#FFC475",
  76. "#FCB06C",
  77. "#F78F4F",
  78. "#F96F4A",
  79. "#E4574C",
  80. "#CA3756",
  81. "#AF254F"
  82. ]
  83. # 将 fixed_colors 转换为 Plotly 的 colorscale 格式
  84. fixed_colorscale = [
  85. [i / (len(fixed_colors) - 1), color] for i, color in enumerate(fixed_colors)
  86. ]
  87. for name, group in grouped:
  88. df = self.calculateYawError(group)
  89. df.dropna(inplace=True)
  90. counts = df['density'].value_counts()
  91. count_0 = counts.get(0, 0) # 获取 density 为 0 的数量,如果没有 0 则返回 0
  92. count_1 = counts.get(1, 0) # 获取 density 为 1 的数量,如果没有 1 则返回 0
  93. # print(f"Density 为 0 的数量: {count_0}")
  94. # print(f"Density 为 1 的数量: {count_1}")
  95. df = df[df["density"]>0]
  96. mean_X = np.mean(df["x"])
  97. std_X = np.std(df["x"])
  98. mediann_X= np.median(df["x"])
  99. skewness_X = skew(df["x"])
  100. kurtosis_X = kurtosis(df["x"])
  101. max_X = np.max(df["x"])
  102. min_X = np.min(df["x"])
  103. result={
  104. 'mean_X':[mean_X],
  105. 'std_X': [std_X],
  106. 'mediann_X':[mediann_X],
  107. 'skewness_X':[skewness_X],
  108. 'kurtosis_X':[kurtosis_X],
  109. 'max_X':[max_X],
  110. 'min_X':[min_X]
  111. }
  112. result = pd.DataFrame(result)
  113. print(f'{name[0]}指标:',result)
  114. # 用密度作为颜色绘制散点图,并限制横坐标范围为 -20 到 20
  115. fig = go.Figure()
  116. fig.add_trace(go.Scattergl(
  117. x=df["x"],
  118. y=df["y"],
  119. mode='markers',
  120. marker=dict(
  121. size=3,
  122. opacity=0.7,
  123. color=df["density"],
  124. colorscale=fixed_colorscale,
  125. showscale=True,
  126. )
  127. ))
  128. fig.update_layout(
  129. xaxis_title='对风角度',
  130. yaxis_title='风速',
  131. title=f'动态偏航误差分析-{name[0]}',
  132. xaxis=dict(range=[-20, 20]), # 限制横坐标范围为 -20 到 20
  133. yaxis=dict(range=[0, 25])
  134. )
  135. # Save to file
  136. filePathOfImage = os.path.join(outputAnalysisDir, f"{name[0]}.png")
  137. fig.write_image(filePathOfImage, scale=3)
  138. filePathOfHtml = os.path.join(outputAnalysisDir, f"{name[0]}.html")
  139. fig.write_html(filePathOfHtml)
  140. result_rows.append({
  141. Field_Return_TypeAnalyst: self.typeAnalyst(),
  142. Field_PowerFarmCode: conf.dataContract.dataFilter.powerFarmID,
  143. Field_Return_BatchCode: conf.dataContract.dataFilter.dataBatchNum,
  144. Field_CodeOfTurbine: name[1],
  145. Field_Return_FilePath: filePathOfImage,
  146. Field_Return_IsSaveDatabase: False
  147. })
  148. result_rows.append({
  149. Field_Return_TypeAnalyst: self.typeAnalyst(),
  150. Field_PowerFarmCode: conf.dataContract.dataFilter.powerFarmID,
  151. Field_Return_BatchCode: conf.dataContract.dataFilter.dataBatchNum,
  152. Field_CodeOfTurbine: name[1],
  153. Field_Return_FilePath: filePathOfHtml,
  154. Field_Return_IsSaveDatabase: True
  155. })
  156. result_df = pd.DataFrame(result_rows)
  157. return result_df