generatorSpeedTorqueAnalyst.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. import os
  2. import pandas as pd
  3. import numpy as np
  4. import plotly.graph_objects as go
  5. from plotly.subplots import make_subplots
  6. import plotly.express as px
  7. import seaborn as sns
  8. import matplotlib.pyplot as plt
  9. from matplotlib.ticker import MultipleLocator
  10. from behavior.analyst import Analyst
  11. from utils.directoryUtil import DirectoryUtil as dir
  12. from algorithmContract.confBusiness import *
  13. class GeneratorSpeedTorqueAnalyst(Analyst):
  14. """
  15. 风电机组发电机转速-转矩分析
  16. """
  17. def typeAnalyst(self):
  18. return "speed_torque"
  19. def turbinesAnalysis(self, dataFrameMerge, outputAnalysisDir, confData: ConfBusiness):
  20. self.create_and_save_plots(
  21. dataFrameMerge, outputAnalysisDir, confData)
  22. self.drawScatterGraph(dataFrameMerge, outputAnalysisDir, confData)
  23. self.drawScatterGraphForTurbines(
  24. dataFrameMerge, outputAnalysisDir, confData)
  25. def create_and_save_plots(self, dataFrame: pd.DataFrame, outputAnalysisDir, confData: ConfBusiness):
  26. # 检查所需列是否存在
  27. required_columns = {confData.field_gen_speed, Field_GeneratorTorque}
  28. if not required_columns.issubset(dataFrame.columns):
  29. raise ValueError(f"DataFrame缺少必要的列。需要的列有: {required_columns}")
  30. x_name = 'generator_speed'
  31. y_name = 'generator_torque'
  32. maxTurque = dataFrame[Field_GeneratorTorque].max()
  33. grouped = dataFrame.groupby(Field_NameOfTurbine)
  34. for name, group in grouped:
  35. groupNew = group.copy()
  36. if not self.common.isNone(confData.value_gen_speed_multiple):
  37. groupNew[confData.field_gen_speed] = group[confData.field_gen_speed] * \
  38. confData.value_gen_speed_multiple
  39. # sns.lmplot函数参数scatter_kws: 设置为{"s": 5}时,会出现颜色丢失问题;改为={"s": 5, "color": "b"}后,则造成图形风格不统一问题;
  40. g = sns.lmplot(x=confData.field_gen_speed, y=Field_GeneratorTorque, data=groupNew, fit_reg=False, scatter_kws={
  41. "s": 5, "color": "b"}, legend=False, height=6, aspect=1.2)
  42. # g = sns.lmplot(x=fieldGeneratorSpeed, y=Field_GeneratorTorque, data=group, fit_reg=False, scatter_kws={
  43. # "s": 5}, legend=False, height=6, aspect=1.2)
  44. for ax in g.axes.flat:
  45. # 创建每100个单位一个刻度的定位器
  46. loc = MultipleLocator(confData.graphSets["generatorSpeed"]["step"] if not self.common.isNone(
  47. confData.graphSets["generatorSpeed"]) and not self.common.isNone(
  48. confData.graphSets["generatorSpeed"]["step"]) else 200)
  49. ax.xaxis.set_major_locator(loc) # 将定位器应用到x轴上
  50. ax.set_xlim(confData.graphSets["generatorSpeed"]["min"] if not self.common.isNone(
  51. confData.graphSets["generatorSpeed"]["min"]) else 1000, confData.graphSets["generatorSpeed"]["max"] if not self.common.isNone(confData.graphSets["generatorSpeed"]["max"]) else 2000)
  52. yloc = MultipleLocator(confData.graphSets["generatorTorque"]["step"] if not self.common.isNone(
  53. confData.graphSets["generatorTorque"]["step"]) else 200)
  54. ax.yaxis.set_major_locator(yloc) # 将定位器应用到y轴上
  55. ax.set_ylim(confData.graphSets["generatorTorque"]["min"] if not self.common.isNone(
  56. confData.graphSets["generatorTorque"]["min"]) else 0, confData.graphSets["generatorTorque"]["max"] if not self.common.isNone(confData.graphSets["generatorTorque"]["max"]) else 2000)
  57. ax.set_xlabel(x_name)
  58. ax.set_ylabel(y_name)
  59. plt.tight_layout()
  60. plt.title(f'{Field_NameOfTurbine}={name}')
  61. # 保存图片到指定路径
  62. output_file = os.path.join(outputAnalysisDir, f"{name}.png")
  63. plt.savefig(output_file, bbox_inches='tight', dpi=120)
  64. plt.close()
  65. def drawScatterGraph(self, dataFrame: pd.DataFrame, outputAnalysisDir, confData: ConfBusiness):
  66. """
  67. 绘制风速-功率分布图并保存为文件。
  68. 参数:
  69. dataFrameMerge (pd.DataFrame): 包含数据的DataFrame,需要包含设备名、风速和功率列。
  70. outputAnalysisDir (str): 分析输出目录。
  71. confData (ConfBusiness): 配置
  72. """
  73. dataFrame = dataFrame[(dataFrame[Field_GeneratorTorque] > 0)]
  74. # 按设备名分组数据
  75. colorsList = ['#1f77b4', '#ff7f0e', '#2ca02c', '#d62728', '#9467bd',
  76. '#8c564b', '#e377c2', '#7f7f7f', '#bcbd22', '#17becf', '#aec7e8', '#ffbb78']
  77. grouped = dataFrame.groupby(Field_NameOfTurbine)
  78. # 遍历每个设备的数据
  79. for name, group in grouped:
  80. # 创建颜色映射,将每个年月映射到一个唯一的颜色
  81. unique_months = group[Field_YearMonth].unique()
  82. colors = [
  83. colorsList[i % len(colorsList)] for i in range(len(unique_months))]
  84. color_map = dict(zip(unique_months, colors))
  85. # 使用go.Scatter3d创建3D散点图
  86. trace = go.Scatter3d(
  87. x=group[confData.field_gen_speed],
  88. y=group[Field_YearMonth],
  89. z=group[Field_GeneratorTorque],
  90. mode='markers',
  91. marker=dict(
  92. color=[color_map[month]
  93. for month in group[Field_YearMonth]],
  94. size=2,
  95. line=dict(
  96. color='rgba(0, 0, 0, 0)', # 设置边框颜色为透明,以去掉白色边框
  97. width=0 # 设置边框宽度为0,进一步确保没有边框
  98. ),
  99. opacity=0.8 # 调整散点的透明度,增加透视效果
  100. )
  101. )
  102. # 创建图形
  103. fig = go.Figure(data=[trace])
  104. # 更新图形的布局
  105. fig.update_layout(
  106. title={
  107. "text": f'Monthly generator speed torque 3D scatter plot {name}',
  108. "x": 0.5
  109. },
  110. scene=dict(
  111. xaxis=dict(
  112. title='Generator Speed',
  113. dtick=confData.graphSets["generatorSpeed"]["step"] if not self.common.isNone(
  114. confData.graphSets["generatorSpeed"]["step"]) else 200, # 设置y轴刻度间隔为0.1
  115. range=[confData.graphSets["generatorSpeed"]["min"] if not self.common.isNone(
  116. confData.graphSets["generatorSpeed"]["min"]) else 1000, confData.graphSets["generatorSpeed"]["max"] if not self.common.isNone(confData.graphSets["generatorSpeed"]["max"]) else 2000], # 设置y轴的范围从0到1
  117. showgrid=True, # 显示网格线
  118. ),
  119. yaxis=dict(
  120. title='Time',
  121. tickmode='array',
  122. tickvals=unique_months,
  123. ticktext=unique_months,
  124. # dtick=500, # 设置y轴刻度间隔
  125. # range=[0,
  126. # group[Field_GeneratorTorque].max()], # 设置y轴的范围
  127. showgrid=True, # 显示网格线
  128. # categoryorder='category ascending'
  129. ),
  130. zaxis=dict(
  131. title='Generator Torque',
  132. dtick=confData.graphSets["generatorTorque"]["step"] if not self.common.isNone(
  133. confData.graphSets["generatorTorque"]["step"]) else 200, # 设置y轴刻度间隔为0.1
  134. range=[confData.graphSets["generatorTorque"]["min"] if not self.common.isNone(
  135. confData.graphSets["generatorTorque"]["min"]) else 0, confData.graphSets["generatorTorque"]["max"] if not self.common.isNone(confData.graphSets["generatorTorque"]["max"]) else 2000], # 设置y轴的范围从0到1
  136. )
  137. ),
  138. scene_camera=dict(
  139. up=dict(x=0, y=0, z=1), # 保持相机向上方向不变
  140. center=dict(x=0, y=0, z=0), # 保持相机中心位置不变
  141. eye=dict(x=-1.8, y=-1.8, z=1.2) # 调整eye属性以实现水平旋转180°
  142. ),
  143. margin=dict(t=50, b=10) # t为顶部(top)间距,b为底部(bottom)间距
  144. )
  145. # 保存图像
  146. outputFileHtml = os.path.join(
  147. outputAnalysisDir, "{}.html".format(name))
  148. fig.write_html(outputFileHtml)
  149. def drawScatterGraphForTurbines(self, dataFrame: pd.DataFrame, outputAnalysisDir, confData: ConfBusiness):
  150. """
  151. 绘制风速-功率分布图并保存为文件。
  152. 参数:
  153. dataFrameMerge (pd.DataFrame): 包含数据的DataFrame,需要包含设备名、风速和功率列。
  154. outputAnalysisDir (str): 分析输出目录。
  155. confData (ConfBusiness): 配置
  156. """
  157. dataFrame = dataFrame[(dataFrame[confData.field_power] > 0)].sort_values(
  158. by=Field_NameOfTurbine)
  159. # 创建3D散点图
  160. fig = px.scatter_3d(dataFrame,
  161. x=confData.field_gen_speed,
  162. y=Field_NameOfTurbine,
  163. z=Field_GeneratorTorque,
  164. color=Field_NameOfTurbine,
  165. labels={confData.field_gen_speed: 'Generator Speed',
  166. Field_NameOfTurbine: 'Turbine', Field_GeneratorTorque: 'Generator Torque'},
  167. )
  168. # 设置固定散点大小
  169. fig.update_traces(marker=dict(size=1.5))
  170. # 更新图形的布局
  171. fig.update_layout(
  172. title={
  173. "text": 'Turbine generator speed Turque 3D scatter plot',
  174. "x": 0.5
  175. },
  176. scene=dict(
  177. xaxis=dict(
  178. title='Generator Speed',
  179. dtick=confData.graphSets["generatorSpeed"]["step"] if not self.common.isNone(
  180. confData.graphSets["generatorSpeed"]["step"]) else 200, # 设置y轴刻度间隔为0.1
  181. range=[confData.graphSets["generatorSpeed"]["min"] if not self.common.isNone(
  182. confData.graphSets["generatorSpeed"]["min"]) else 1000, confData.graphSets["generatorSpeed"]["max"] if not self.common.isNone(confData.graphSets["generatorSpeed"]["max"]) else 2000], # 设置y轴的范围从0到1
  183. showgrid=True, # 显示网格线
  184. ),
  185. yaxis=dict(
  186. title='Turbine',
  187. showgrid=True, # 显示网格线
  188. ),
  189. zaxis=dict(
  190. title='Generator Turque',
  191. dtick=confData.graphSets["generatorTorque"]["step"] if not self.common.isNone(
  192. confData.graphSets["generatorTorque"]["step"]) else 200, # 设置y轴刻度间隔为0.1
  193. range=[confData.graphSets["generatorTorque"]["min"] if not self.common.isNone(
  194. confData.graphSets["generatorTorque"]["min"]) else 0, confData.graphSets["generatorTorque"]["max"] if not self.common.isNone(confData.graphSets["generatorTorque"]["max"]) else 2000], # 设置y轴的范围从0到1
  195. )
  196. ),
  197. scene_camera=dict(
  198. up=dict(x=0, y=0, z=1), # 保持相机向上方向不变
  199. center=dict(x=0, y=0, z=0), # 保持相机中心位置不变
  200. eye=dict(x=-1.8, y=-1.8, z=1.2) # 调整eye属性以实现水平旋转180°
  201. ),
  202. # 设置图例标题
  203. legend_title_text='Turbine',
  204. margin=dict(t=50, b=10) # t为顶部(top)间距,b为底部(bottom)间距
  205. )
  206. # 保存图像
  207. outputFileHtml = os.path.join(
  208. outputAnalysisDir, "{}.html".format(self.typeAnalyst()))
  209. fig.write_html(outputFileHtml)