tsrTrendAnalyst.py 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  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.analystWithGoodPoint import AnalystWithGoodPoint
  7. class TSRTrendAnalyst(AnalystWithGoodPoint):
  8. """
  9. 风电机组叶尖速比时序分析
  10. """
  11. def typeAnalyst(self):
  12. return "tsr_trend"
  13. def selectColumns(self):
  14. return [Field_DeviceCode,Field_Time,Field_WindSpeed,Field_ActiverPower,Field_RotorSpeed,Field_GeneratorSpeed]
  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.selectColumns())
  19. dataFrameMerge = self.userDataFrame(dictionary,conf.dataContract.configAnalysis,self)
  20. turbineInfos = self.common.getTurbineInfos(conf.dataContract.dataFilter.powerFarmID, turbineCodes, self.turbineInfo)
  21. return self.drawTSRTrend(dataFrameMerge,turbineInfos, outputAnalysisDir, conf)
  22. def drawTSRTrend(self,dataFrameMerge:pd.DataFrame, turbineModelInfo: pd.Series,outputAnalysisDir, conf: Contract):
  23. # 检查所需列是否存在
  24. required_columns = {Field_TSR, Field_YearMonthDay}
  25. if not required_columns.issubset(dataFrameMerge.columns):
  26. raise ValueError(f"DataFrame缺少必要的列。需要的列有: {required_columns}")
  27. # 按设备名分组数据
  28. grouped = dataFrameMerge.groupby([Field_NameOfTurbine, Field_CodeOfTurbine])
  29. result_rows = []
  30. for name, group in grouped:
  31. # 计算四分位数和IQR
  32. Q1 = group[Field_TSR].quantile(0.25)
  33. Q3 = group[Field_TSR].quantile(0.75)
  34. IQR = Q3 - Q1
  35. # 定义离群值的范围
  36. lower_bound = Q1 - 1.5 * IQR
  37. upper_bound = Q3 + 1.5 * IQR
  38. # 筛选掉离群值
  39. filtered_group = group[(group[Field_TSR] >= lower_bound) & (group[Field_TSR] <= upper_bound)]
  40. # 创建箱线图
  41. fig = go.Figure()
  42. fig.add_trace(go.Box(
  43. x=filtered_group[Field_YearMonthDay], # 设置x轴数据为日期
  44. y=filtered_group[Field_TSR], # 设置y轴数据为风能利用系数
  45. # boxpoints='outliers', # 显示异常值(偏离值),不显示数据的所有点(只显示异常值)
  46. boxpoints=False, # 不显示偏离值
  47. marker=dict(color='lightgoldenrodyellow', size=1), # 设置偏离值的颜色和大小
  48. line=dict(color='lightgray', width=2), # 设置箱线和须线的颜色为灰色,粗细为2
  49. fillcolor='rgba(200, 200, 200, 0.5)', # 设置箱体的填充颜色和透明度
  50. name='叶尖速比' # 图例名称
  51. ))
  52. # 对于每个箱线图的中位数,绘制一个蓝色点
  53. medians = filtered_group.groupby(filtered_group[Field_YearMonthDay])[Field_TSR].median()
  54. fig.add_trace(go.Scatter(
  55. x=medians.index,
  56. y=medians.values,
  57. mode='markers',
  58. marker=dict(color='orange', size=3),
  59. name='叶尖速比中位数' # 中位数标记的图例名称
  60. ))
  61. # 设置图表的标题和轴标签
  62. fig.update_layout(
  63. title={
  64. 'text': f'机组: {name[0]}',
  65. #'x':0.5,
  66. },
  67. xaxis_title='时间',
  68. yaxis_title='叶尖速比',
  69. xaxis=dict(
  70. tickmode='auto', # 自动设置x轴刻度,以适应日期数据
  71. tickformat='%Y-%m-%d', # 设置x轴时间格式
  72. showgrid=True, # 显示网格线
  73. gridcolor='lightgray', # setting y-axis gridline color to black
  74. tickangle=-45,
  75. linecolor='black', # 设置y轴坐标系线颜色为黑色
  76. ticklen=5, # 设置刻度线的长度
  77. ),
  78. yaxis=dict(
  79. range=[self.axisLowerLimitTSR, self.axisUpperLimitTSR],
  80. dtick=self.axisStepTSR,
  81. showgrid=True, # 显示网格线
  82. gridcolor='lightgray', # setting y-axis gridline color to black
  83. linecolor='black', # 设置y轴坐标系线颜色为黑色
  84. ticklen=5, # 设置刻度线的长度
  85. ),
  86. paper_bgcolor='white', # 设置纸张背景颜色为白色
  87. plot_bgcolor='white', # 设置图表背景颜色为白色
  88. margin=dict(t=50, b=10) # t为顶部(top)间距,b为底部(bottom)间距
  89. )
  90. # 确保从 Series 中提取的是具体的值
  91. engineTypeCode = turbineModelInfo.get(Field_MillTypeCode, "")
  92. if isinstance(engineTypeCode, pd.Series):
  93. engineTypeCode = engineTypeCode.iloc[0]
  94. engineTypeName = turbineModelInfo.get(Field_MachineTypeCode, "")
  95. if isinstance(engineTypeName, pd.Series):
  96. engineTypeName = engineTypeName.iloc[0]
  97. # 构建最终的JSON对象
  98. json_output = {
  99. "analysisTypeCode": "叶尖速比时序分析",
  100. "engineCode": engineTypeCode,
  101. "engineTypeName": engineTypeName,
  102. "xaixs": "时间",
  103. "yaixs": "叶尖速比",
  104. "data": [{
  105. "engineName": name[0],
  106. "engineCode": name[1],
  107. "title":f'机组-{name[0]}',
  108. "xData": filtered_group[Field_YearMonthDay].tolist(),
  109. "yData": filtered_group[Field_TSR].tolist(),
  110. "color":'lightgray',
  111. "width":2,
  112. "type":"box_plot",
  113. "medians": {
  114. "x": medians.index.tolist(), # 中位数的 x 轴数据
  115. "y": medians.values.tolist(), # 中位数的 y 轴数据
  116. "mode":'markers',
  117. "color":'orange',
  118. "size":3
  119. }
  120. }]
  121. }
  122. # 保存图像
  123. # filePathOfImage = os.path.join(outputAnalysisDir, f"{name[0]}.png")
  124. # fig.write_image(filePathOfImage, scale=3)
  125. # filePathOfHtml = os.path.join(outputAnalysisDir, f"{name[0]}.html")
  126. # fig.write_html(filePathOfHtml)
  127. # 将JSON对象保存到文件
  128. output_json_path = os.path.join(outputAnalysisDir, f"{name[0]}.json")
  129. with open(output_json_path, 'w', encoding='utf-8') as f:
  130. import json
  131. json.dump(json_output, f, ensure_ascii=False, indent=4)
  132. # 如果需要返回DataFrame,可以包含文件路径
  133. result_rows.append({
  134. Field_Return_TypeAnalyst: self.typeAnalyst(),
  135. Field_PowerFarmCode: conf.dataContract.dataFilter.powerFarmID,
  136. Field_Return_BatchCode: conf.dataContract.dataFilter.dataBatchNum,
  137. Field_CodeOfTurbine: name[1],
  138. Field_Return_FilePath: output_json_path,
  139. Field_Return_IsSaveDatabase: True
  140. })
  141. # result_rows.append({
  142. # Field_Return_TypeAnalyst: self.typeAnalyst(),
  143. # Field_PowerFarmCode: conf.dataContract.dataFilter.powerFarmID,
  144. # Field_Return_BatchCode: conf.dataContract.dataFilter.dataBatchNum,
  145. # Field_CodeOfTurbine: name[1],
  146. # Field_Return_FilePath: filePathOfImage,
  147. # Field_Return_IsSaveDatabase: False
  148. # })
  149. # result_rows.append({
  150. # Field_Return_TypeAnalyst: self.typeAnalyst(),
  151. # Field_PowerFarmCode: conf.dataContract.dataFilter.powerFarmID,
  152. # Field_Return_BatchCode: conf.dataContract.dataFilter.dataBatchNum,
  153. # Field_CodeOfTurbine: name[1],
  154. # Field_Return_FilePath: filePathOfHtml,
  155. # Field_Return_IsSaveDatabase: True
  156. # })
  157. result_df = pd.DataFrame(result_rows)
  158. return result_df