powerScatterAnalyst.py 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. import os
  2. import pandas as pd
  3. import plotly.graph_objects as go
  4. from algorithmContract.confBusiness import *
  5. from algorithmContract.contract import Contract
  6. from behavior.analystWithGoodBadPoint import AnalystWithGoodBadPoint
  7. class PowerScatterAnalyst(AnalystWithGoodBadPoint):
  8. """
  9. 风电机组功率曲线散点分析。
  10. 秒级scada数据运算太慢,建议使用分钟级scada数据
  11. """
  12. def typeAnalyst(self):
  13. return "power_scatter"
  14. def selectColumns(self):
  15. return [Field_DeviceCode, Field_Time, Field_WindSpeed, Field_ActiverPower]
  16. def processDateTime(self, dataFrame: pd.DataFrame, fieldTime:str = None):
  17. super().processDateTime(dataFrame,Field_YearMonth)
  18. def turbinesAnalysis(self, outputAnalysisDir, conf: Contract, turbineCodes):
  19. dictionary = self.processTurbineData(turbineCodes, conf, self.selectColumns())
  20. dataFrame = self.userDataFrame(dictionary, conf.dataContract.configAnalysis, self)
  21. if len(dataFrame) <= 0:
  22. print("After screening for blade pitch angle less than the configured value, plot power curve scatter points without data")
  23. return
  24. # dataFrameGuaranteePowerCurve=self.powerFarmInfo[Field_PowerContractURL]
  25. # grouped=self.dataFrameContractOfTurbine.groupby([Field_PowerFarmCode, Field_MillTypeCode])
  26. # for groupByKey,contractPowerCurveOfMillType in grouped:
  27. # break
  28. return self.drawOfPowerCurveScatter(dataFrame, outputAnalysisDir, conf)
  29. """
  30. def contractGuaranteePowerCurveData(self, csvPowerCurveFilePath):
  31. dataFrameGuaranteePowerCurve = pd.read_csv(
  32. csvPowerCurveFilePath, encoding=charset_unify)
  33. return dataFrameGuaranteePowerCurve
  34. """
  35. def drawOfPowerCurveScatter(self, dataFrame: pd.DataFrame, outputAnalysisDir, conf: Contract):
  36. """
  37. 绘制风速-功率分布图并保存为文件。
  38. 参数:
  39. dataFrameMerge (pd.DataFrame): 包含数据的DataFrame,需要包含设备名、风速和功率列。
  40. csvPowerCurveFilePath (str): 功率曲线文件路径。
  41. outputAnalysisDir (str): 分析输出目录。
  42. conf (ConfBusiness): 配置
  43. """
  44. # 按设备名分组数据
  45. # colorsList = ['#1f77b4', '#ff7f0e', '#2ca02c', '#d62728', '#9467bd',
  46. # '#8c564b', '#e377c2', '#7f7f7f', '#bcbd22', '#17becf', '#aec7e8', '#ffbb78']
  47. colorsList = [
  48. "#3E409C",
  49. "#3586BF",
  50. "#52A3AE",
  51. "#85D0AE",
  52. "#A8DCA2",
  53. "#FBFFBE",
  54. "#FDF1A9",
  55. "#FFE286",
  56. "#FCB06C",
  57. "#F96F4A",
  58. "#E4574C",
  59. "#AF254F"]
  60. cutInWsField = self.turbineModelInfo[Field_CutInWS]
  61. cut_in_ws = cutInWsField.min() - 1 if cutInWsField.notna().any() else 2
  62. # if not dataFrame.empty and Field_CutInWS in dataFrame.columns and dataFrame[Field_CutInWS].notna().any():
  63. # cut_in_ws = dataFrame[Field_CutInWS].min() - 1
  64. # else:
  65. # cut_in_ws = 2
  66. grouped = dataFrame.groupby([Field_NameOfTurbine, Field_CodeOfTurbine])
  67. result_rows = []
  68. # 遍历每个设备的数据
  69. for name, group in grouped:
  70. # 创建颜色映射,将每个年月映射到一个唯一的颜色
  71. unique_months = group[Field_YearMonth].unique()
  72. colors = [
  73. colorsList[i % 12] for i in range(len(unique_months))]
  74. color_map = dict(zip(unique_months, colors))
  75. # 使用go.Scatter3d创建3D散点图
  76. trace = go.Scatter3d(
  77. x=group[Field_WindSpeed],
  78. y=group[Field_YearMonth],
  79. z=group[Field_ActiverPower],
  80. mode='markers',
  81. marker=dict(
  82. color=[color_map[month]
  83. for month in group[Field_YearMonth]],
  84. size=1.5,
  85. line=dict(
  86. color='rgba(0, 0, 0, 0)', # 设置边框颜色为透明,以去掉白色边框
  87. width=0 # 设置边框宽度为0,进一步确保没有边框
  88. ),
  89. opacity=0.8 # 调整散点的透明度,增加透视效果
  90. )
  91. )
  92. # 创建图形
  93. fig = go.Figure(data=[trace])
  94. # 更新图形的布局
  95. fig.update_layout(
  96. title={
  97. "text": f'当月发电量: {name[0]}',
  98. "x": 0.5
  99. },
  100. scene=dict(
  101. xaxis=dict(
  102. title='风速',
  103. range=[cut_in_ws, 25],
  104. ),
  105. yaxis=dict(
  106. title='时间',
  107. tickmode='array',
  108. tickvals=unique_months,
  109. ticktext=unique_months,
  110. categoryorder='category ascending'
  111. ),
  112. zaxis=dict(
  113. title='功率',
  114. range=[self.axisLowerLimitActivePower, self.axisUpperLimitActivePower],
  115. dtick=self.axisStepActivePower,
  116. )
  117. ),
  118. scene_camera=dict(
  119. up=dict(x=0, y=0, z=1), # 保持相机向上方向不变
  120. center=dict(x=0, y=0, z=0), # 保持相机中心位置不变
  121. eye=dict(x=-1.8, y=-1.8, z=1.2) # 调整eye属性以实现水平旋转180°
  122. ),
  123. margin=dict(t=50, b=10) # t为顶部(top)间距,b为底部(bottom)间距
  124. )
  125. # Save plot
  126. filePathOfHtml = os.path.join(outputAnalysisDir, f"{name[0]}.html")
  127. fig.write_html(filePathOfHtml)
  128. result_rows.append({
  129. Field_Return_TypeAnalyst: self.typeAnalyst(),
  130. Field_PowerFarmCode: conf.dataContract.dataFilter.powerFarmID,
  131. Field_Return_BatchCode: conf.dataContract.dataFilter.dataBatchNum,
  132. Field_CodeOfTurbine: name[1],
  133. Field_Return_FilePath: filePathOfHtml,
  134. Field_Return_IsSaveDatabase: True
  135. })
  136. result_df = pd.DataFrame(result_rows)
  137. return result_df