| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236 |
- import os
- import pandas as pd
- import numpy as np
- import plotly.graph_objects as go
- from plotly.subplots import make_subplots
- import plotly.express as px
- import seaborn as sns
- import matplotlib.pyplot as plt
- from matplotlib.ticker import MultipleLocator
- from behavior.analyst import Analyst
- from utils.directoryUtil import DirectoryUtil as dir
- from algorithmContract.confBusiness import *
- class GeneratorSpeedTorqueAnalyst(Analyst):
- """
- 风电机组发电机转速-转矩分析
- """
- def typeAnalyst(self):
- return "speed_torque"
- def turbinesAnalysis(self, dataFrameMerge, outputAnalysisDir, confData: ConfBusiness):
- self.create_and_save_plots(
- dataFrameMerge, outputAnalysisDir, confData)
- self.drawScatterGraph(dataFrameMerge, outputAnalysisDir, confData)
- self.drawScatterGraphForTurbines(
- dataFrameMerge, outputAnalysisDir, confData)
- def create_and_save_plots(self, dataFrame: pd.DataFrame, outputAnalysisDir, confData: ConfBusiness):
- # 检查所需列是否存在
- required_columns = {confData.field_gen_speed, Field_GeneratorTorque}
- if not required_columns.issubset(dataFrame.columns):
- raise ValueError(f"DataFrame缺少必要的列。需要的列有: {required_columns}")
- x_name = 'generator_speed'
- y_name = 'generator_torque'
- maxTurque = dataFrame[Field_GeneratorTorque].max()
- grouped = dataFrame.groupby(Field_NameOfTurbine)
- for name, group in grouped:
- groupNew = group.copy()
- if not self.common.isNone(confData.value_gen_speed_multiple):
- groupNew[confData.field_gen_speed] = group[confData.field_gen_speed] * \
- confData.value_gen_speed_multiple
- # sns.lmplot函数参数scatter_kws: 设置为{"s": 5}时,会出现颜色丢失问题;改为={"s": 5, "color": "b"}后,则造成图形风格不统一问题;
- g = sns.lmplot(x=confData.field_gen_speed, y=Field_GeneratorTorque, data=groupNew, fit_reg=False, scatter_kws={
- "s": 5, "color": "b"}, legend=False, height=6, aspect=1.2)
- # g = sns.lmplot(x=fieldGeneratorSpeed, y=Field_GeneratorTorque, data=group, fit_reg=False, scatter_kws={
- # "s": 5}, legend=False, height=6, aspect=1.2)
- for ax in g.axes.flat:
- # 创建每100个单位一个刻度的定位器
- loc = MultipleLocator(confData.graphSets["generatorSpeed"]["step"] if not self.common.isNone(
- confData.graphSets["generatorSpeed"]) and not self.common.isNone(
- confData.graphSets["generatorSpeed"]["step"]) else 200)
- ax.xaxis.set_major_locator(loc) # 将定位器应用到x轴上
- ax.set_xlim(confData.graphSets["generatorSpeed"]["min"] if not self.common.isNone(
- confData.graphSets["generatorSpeed"]["min"]) else 1000, confData.graphSets["generatorSpeed"]["max"] if not self.common.isNone(confData.graphSets["generatorSpeed"]["max"]) else 2000)
- yloc = MultipleLocator(confData.graphSets["generatorTorque"]["step"] if not self.common.isNone(
- confData.graphSets["generatorTorque"]["step"]) else 200)
- ax.yaxis.set_major_locator(yloc) # 将定位器应用到y轴上
- ax.set_ylim(confData.graphSets["generatorTorque"]["min"] if not self.common.isNone(
- confData.graphSets["generatorTorque"]["min"]) else 0, confData.graphSets["generatorTorque"]["max"] if not self.common.isNone(confData.graphSets["generatorTorque"]["max"]) else 2000)
- ax.set_xlabel(x_name)
- ax.set_ylabel(y_name)
- plt.tight_layout()
- plt.title(f'{Field_NameOfTurbine}={name}')
- # 保存图片到指定路径
- output_file = os.path.join(outputAnalysisDir, f"{name}.png")
- plt.savefig(output_file, bbox_inches='tight', dpi=120)
- plt.close()
- def drawScatterGraph(self, dataFrame: pd.DataFrame, outputAnalysisDir, confData: ConfBusiness):
- """
- 绘制风速-功率分布图并保存为文件。
- 参数:
- dataFrameMerge (pd.DataFrame): 包含数据的DataFrame,需要包含设备名、风速和功率列。
- outputAnalysisDir (str): 分析输出目录。
- confData (ConfBusiness): 配置
- """
- dataFrame = dataFrame[(dataFrame[Field_GeneratorTorque] > 0)]
- # 按设备名分组数据
- colorsList = ['#1f77b4', '#ff7f0e', '#2ca02c', '#d62728', '#9467bd',
- '#8c564b', '#e377c2', '#7f7f7f', '#bcbd22', '#17becf', '#aec7e8', '#ffbb78']
- grouped = dataFrame.groupby(Field_NameOfTurbine)
- # 遍历每个设备的数据
- for name, group in grouped:
- # 创建颜色映射,将每个年月映射到一个唯一的颜色
- unique_months = group[Field_YearMonth].unique()
- colors = [
- colorsList[i % len(colorsList)] for i in range(len(unique_months))]
- color_map = dict(zip(unique_months, colors))
- # 使用go.Scatter3d创建3D散点图
- trace = go.Scatter3d(
- x=group[confData.field_gen_speed],
- y=group[Field_YearMonth],
- z=group[Field_GeneratorTorque],
- mode='markers',
- marker=dict(
- color=[color_map[month]
- for month in group[Field_YearMonth]],
- size=2,
- line=dict(
- color='rgba(0, 0, 0, 0)', # 设置边框颜色为透明,以去掉白色边框
- width=0 # 设置边框宽度为0,进一步确保没有边框
- ),
- opacity=0.8 # 调整散点的透明度,增加透视效果
- )
- )
- # 创建图形
- fig = go.Figure(data=[trace])
- # 更新图形的布局
- fig.update_layout(
- title={
- "text": f'Monthly generator speed torque 3D scatter plot {name}',
- "x": 0.5
- },
- scene=dict(
- xaxis=dict(
- title='Generator Speed',
- dtick=confData.graphSets["generatorSpeed"]["step"] if not self.common.isNone(
- confData.graphSets["generatorSpeed"]["step"]) else 200, # 设置y轴刻度间隔为0.1
- range=[confData.graphSets["generatorSpeed"]["min"] if not self.common.isNone(
- confData.graphSets["generatorSpeed"]["min"]) else 1000, confData.graphSets["generatorSpeed"]["max"] if not self.common.isNone(confData.graphSets["generatorSpeed"]["max"]) else 2000], # 设置y轴的范围从0到1
- showgrid=True, # 显示网格线
- ),
- yaxis=dict(
- title='Time',
- tickmode='array',
- tickvals=unique_months,
- ticktext=unique_months,
- # dtick=500, # 设置y轴刻度间隔
- # range=[0,
- # group[Field_GeneratorTorque].max()], # 设置y轴的范围
- showgrid=True, # 显示网格线
- # categoryorder='category ascending'
- ),
- zaxis=dict(
- title='Generator Torque',
- dtick=confData.graphSets["generatorTorque"]["step"] if not self.common.isNone(
- confData.graphSets["generatorTorque"]["step"]) else 200, # 设置y轴刻度间隔为0.1
- range=[confData.graphSets["generatorTorque"]["min"] if not self.common.isNone(
- confData.graphSets["generatorTorque"]["min"]) else 0, confData.graphSets["generatorTorque"]["max"] if not self.common.isNone(confData.graphSets["generatorTorque"]["max"]) else 2000], # 设置y轴的范围从0到1
- )
- ),
- scene_camera=dict(
- up=dict(x=0, y=0, z=1), # 保持相机向上方向不变
- center=dict(x=0, y=0, z=0), # 保持相机中心位置不变
- eye=dict(x=-1.8, y=-1.8, z=1.2) # 调整eye属性以实现水平旋转180°
- ),
- margin=dict(t=50, b=10) # t为顶部(top)间距,b为底部(bottom)间距
- )
- # 保存图像
- outputFileHtml = os.path.join(
- outputAnalysisDir, "{}.html".format(name))
- fig.write_html(outputFileHtml)
- def drawScatterGraphForTurbines(self, dataFrame: pd.DataFrame, outputAnalysisDir, confData: ConfBusiness):
- """
- 绘制风速-功率分布图并保存为文件。
- 参数:
- dataFrameMerge (pd.DataFrame): 包含数据的DataFrame,需要包含设备名、风速和功率列。
- outputAnalysisDir (str): 分析输出目录。
- confData (ConfBusiness): 配置
- """
- dataFrame = dataFrame[(dataFrame[confData.field_power] > 0)].sort_values(
- by=Field_NameOfTurbine)
- # 创建3D散点图
- fig = px.scatter_3d(dataFrame,
- x=confData.field_gen_speed,
- y=Field_NameOfTurbine,
- z=Field_GeneratorTorque,
- color=Field_NameOfTurbine,
- labels={confData.field_gen_speed: 'Generator Speed',
- Field_NameOfTurbine: 'Turbine', Field_GeneratorTorque: 'Generator Torque'},
- )
- # 设置固定散点大小
- fig.update_traces(marker=dict(size=1.5))
- # 更新图形的布局
- fig.update_layout(
- title={
- "text": 'Turbine generator speed Turque 3D scatter plot',
- "x": 0.5
- },
- scene=dict(
- xaxis=dict(
- title='Generator Speed',
- dtick=confData.graphSets["generatorSpeed"]["step"] if not self.common.isNone(
- confData.graphSets["generatorSpeed"]["step"]) else 200, # 设置y轴刻度间隔为0.1
- range=[confData.graphSets["generatorSpeed"]["min"] if not self.common.isNone(
- confData.graphSets["generatorSpeed"]["min"]) else 1000, confData.graphSets["generatorSpeed"]["max"] if not self.common.isNone(confData.graphSets["generatorSpeed"]["max"]) else 2000], # 设置y轴的范围从0到1
- showgrid=True, # 显示网格线
- ),
- yaxis=dict(
- title='Turbine',
- showgrid=True, # 显示网格线
- ),
- zaxis=dict(
- title='Generator Turque',
- dtick=confData.graphSets["generatorTorque"]["step"] if not self.common.isNone(
- confData.graphSets["generatorTorque"]["step"]) else 200, # 设置y轴刻度间隔为0.1
- range=[confData.graphSets["generatorTorque"]["min"] if not self.common.isNone(
- confData.graphSets["generatorTorque"]["min"]) else 0, confData.graphSets["generatorTorque"]["max"] if not self.common.isNone(confData.graphSets["generatorTorque"]["max"]) else 2000], # 设置y轴的范围从0到1
- )
- ),
- scene_camera=dict(
- up=dict(x=0, y=0, z=1), # 保持相机向上方向不变
- center=dict(x=0, y=0, z=0), # 保持相机中心位置不变
- eye=dict(x=-1.8, y=-1.8, z=1.2) # 调整eye属性以实现水平旋转180°
- ),
- # 设置图例标题
- legend_title_text='Turbine',
- margin=dict(t=50, b=10) # t为顶部(top)间距,b为底部(bottom)间距
- )
- # 保存图像
- outputFileHtml = os.path.join(
- outputAnalysisDir, "{}.html".format(self.typeAnalyst()))
- fig.write_html(outputFileHtml)
|