|
@@ -29,6 +29,7 @@ class PowerScatter2DAnalyst(AnalystWithGoodBadPoint):
|
|
|
def turbinesAnalysis(self, outputAnalysisDir, conf: Contract, turbineCodes):
|
|
|
dictionary = self.processTurbineData(turbineCodes, conf, self.selectColumns())
|
|
|
dataFrame = self.userDataFrame(dictionary, conf.dataContract.configAnalysis, self)
|
|
|
+ turbineInfos = self.common.getTurbineInfos(conf.dataContract.dataFilter.powerFarmID, turbineCodes, self.turbineInfo)
|
|
|
if len(dataFrame) <= 0:
|
|
|
print("After screening for blade pitch angle less than the configured value, plot power curve scatter points without data")
|
|
|
return
|
|
@@ -37,9 +38,9 @@ class PowerScatter2DAnalyst(AnalystWithGoodBadPoint):
|
|
|
|
|
|
for groupByKey, contractPowerCurveOfMillType in grouped:
|
|
|
break
|
|
|
- return self.drawOfPowerCurveScatter(dataFrame, outputAnalysisDir, conf, contractPowerCurveOfMillType)
|
|
|
+ return self.drawOfPowerCurveScatter(dataFrame, turbineInfos,outputAnalysisDir, conf, contractPowerCurveOfMillType)
|
|
|
|
|
|
- def drawOfPowerCurveScatter(self, dataFrame: pd.DataFrame, outputAnalysisDir, conf: Contract, dataFrameGuaranteePowerCurve: pd.DataFrame):
|
|
|
+ def drawOfPowerCurveScatter(self, dataFrame: pd.DataFrame, turbineModelInfo: pd.Series, outputAnalysisDir, conf: Contract, dataFrameGuaranteePowerCurve: pd.DataFrame):
|
|
|
"""
|
|
|
绘制风速-功率分布图并保存为文件。
|
|
|
|
|
@@ -59,70 +60,6 @@ class PowerScatter2DAnalyst(AnalystWithGoodBadPoint):
|
|
|
# cut_in_ws = dataFrame[Field_CutInWS].min() - 1
|
|
|
# else:
|
|
|
# cut_in_ws = 2
|
|
|
- '''
|
|
|
- # 按设备名分组数据
|
|
|
- grouped = dataFrame.groupby([Field_NameOfTurbine, Field_CodeOfTurbine])
|
|
|
- result_rows = []
|
|
|
- # 定义固定的颜色映射列表
|
|
|
- fixed_colors =[
|
|
|
- 'rgb(255, 0, 0)', # 红色
|
|
|
- 'rgb(0, 255, 0)', # 绿色
|
|
|
- 'rgb(0, 0, 255)', # 蓝色
|
|
|
- 'rgb(255, 255, 0)', # 黄色
|
|
|
- 'rgb(255, 0, 255)', # 紫色
|
|
|
- 'rgb(0, 255, 255)' # 青色
|
|
|
- ]
|
|
|
- # 遍历每个设备的数据
|
|
|
- for name, group in grouped:
|
|
|
- fig = make_subplots()
|
|
|
- # 提取月份
|
|
|
- group['month'] = group['monthIntTime'].apply(lambda x: datetime.fromtimestamp(x).month)
|
|
|
- unique_months = group['month'].unique()
|
|
|
-
|
|
|
- # 计算时间跨度
|
|
|
- time_span_months = len(unique_months)
|
|
|
-
|
|
|
- if time_span_months >= 6:
|
|
|
- # 绘制散点图(时间跨度大于等于6个月)
|
|
|
- scatter = go.Scatter(x=group[Field_WindSpeed],
|
|
|
- y=group[Field_ActiverPower],
|
|
|
- mode='markers',
|
|
|
- marker=dict(
|
|
|
- color=group['monthIntTime'],
|
|
|
- colorscale='Rainbow',
|
|
|
- size=3,
|
|
|
- opacity=0.7,
|
|
|
- colorbar=dict(
|
|
|
- tickvals=np.linspace(
|
|
|
- group['monthIntTime'].min(), group['monthIntTime'].max(), 6),
|
|
|
- ticktext=[datetime.fromtimestamp(ts).strftime(
|
|
|
- '%Y-%m') for ts in np.linspace(group['monthIntTime'].min(), group['monthIntTime'].max(), 6)],
|
|
|
- thickness=18,
|
|
|
- len=1, # 设置颜色条的长度,使其占据整个图的高度
|
|
|
- outlinecolor='rgba(255,255,255,0)'
|
|
|
- ),
|
|
|
- showscale=True
|
|
|
- ),
|
|
|
- showlegend=False) # 不显示散点图的legend,用colorbar代替
|
|
|
- fig.add_trace(scatter)
|
|
|
- else:
|
|
|
- # 绘制散点图(时间跨度小于6个月)
|
|
|
- for i,month in enumerate(unique_months):
|
|
|
- month_data = group[group['month'] == month]
|
|
|
- # 使用固定的颜色列表
|
|
|
- color = fixed_colors[i % len(fixed_colors)]
|
|
|
- scatter = go.Scatter(x=month_data[Field_WindSpeed],
|
|
|
- y=month_data[Field_ActiverPower],
|
|
|
- mode='markers',
|
|
|
- marker=dict(
|
|
|
- color=color,
|
|
|
- size=3,
|
|
|
- opacity=0.7
|
|
|
- ),
|
|
|
- name=f'{datetime.fromtimestamp(month_data["monthIntTime"].iloc[0]).strftime("%Y-%m")}',
|
|
|
- showlegend=True)
|
|
|
- fig.add_trace(scatter)
|
|
|
- '''
|
|
|
|
|
|
# 按设备名分组数据
|
|
|
grouped = dataFrame.groupby([Field_NameOfTurbine, Field_CodeOfTurbine])
|
|
@@ -166,6 +103,8 @@ class PowerScatter2DAnalyst(AnalystWithGoodBadPoint):
|
|
|
"#3E409C"
|
|
|
|
|
|
]
|
|
|
+ # 新建一个列表存储输出json格式的数据
|
|
|
+ turbine_data_list = []
|
|
|
# 遍历每个设备的数据
|
|
|
for name, group in grouped:
|
|
|
fig = make_subplots()
|
|
@@ -241,7 +180,47 @@ class PowerScatter2DAnalyst(AnalystWithGoodBadPoint):
|
|
|
legend=dict(yanchor="bottom", y=0, xanchor="right", x=1, font=dict(
|
|
|
size=10), bgcolor='rgba(255,255,255,0)')
|
|
|
)
|
|
|
+ # 确保从 Series 中提取的是具体的值
|
|
|
+ engineTypeCode = turbineModelInfo.get(Field_MillTypeCode, "")
|
|
|
+ if isinstance(engineTypeCode, pd.Series):
|
|
|
+ engineTypeCode = engineTypeCode.iloc[0]
|
|
|
+
|
|
|
+ engineTypeName = turbineModelInfo.get(Field_MachineTypeCode, "")
|
|
|
+ if isinstance(engineTypeName, pd.Series):
|
|
|
+ engineTypeName = engineTypeName.iloc[0]
|
|
|
|
|
|
+ # 定义要替换的空值类型
|
|
|
+ na_values = {pd.NA, float('nan')}
|
|
|
+ # 构建最终的JSON对象
|
|
|
+ json_output = {
|
|
|
+ "analysisTypeCode": "逐月有功功率散点2D分析",
|
|
|
+ "engineCode": engineTypeCode,
|
|
|
+ "engineTypeName": engineTypeName,
|
|
|
+ "xaixs": "风速(m/s)",
|
|
|
+ "yaixs": "有功功率(kw)",
|
|
|
+ "data": [
|
|
|
+ {# 提取机组数据
|
|
|
+ "engineName": name[0],
|
|
|
+ "engineCode": name[1],
|
|
|
+ "title":f' 机组: {name[0]}',
|
|
|
+ "xData": group[Field_WindSpeed].replace(na_values, None).tolist(),
|
|
|
+ "xrange":[cut_in_ws, 25],
|
|
|
+ "yData": group[Field_ActiverPower].replace(na_values, None).tolist(),
|
|
|
+ "yrange":[self.axisLowerLimitActivePower,self.axisUpperLimitActivePower],
|
|
|
+ "colorbar": group['monthIntTime'].tolist(),
|
|
|
+ "colorbartitle": "年月",
|
|
|
+ "mode":"markers"
|
|
|
+
|
|
|
+ },
|
|
|
+ {# 提取合同功率曲线数据
|
|
|
+ "enginName": "合同功率曲线",
|
|
|
+ "xData":dataFrameGuaranteePowerCurve[Field_WindSpeed].replace(na_values, None).tolist(),
|
|
|
+ "yData":dataFrameGuaranteePowerCurve[Field_ActiverPower].replace(na_values, None).tolist(),
|
|
|
+ "zData": [],
|
|
|
+ "mode":"lines+markers"
|
|
|
+ }]
|
|
|
+ }
|
|
|
+
|
|
|
# 保存图像
|
|
|
pngFileName = f"{name[0]}-scatter.png"
|
|
|
pngFilePath = os.path.join(outputAnalysisDir, pngFileName)
|
|
@@ -252,6 +231,23 @@ class PowerScatter2DAnalyst(AnalystWithGoodBadPoint):
|
|
|
htmlFilePath = os.path.join(outputAnalysisDir, htmlFileName)
|
|
|
fig.write_html(htmlFilePath)
|
|
|
|
|
|
+
|
|
|
+ # 将JSON对象保存到文件
|
|
|
+ output_json_path = os.path.join(outputAnalysisDir, f"{name[0]}-scatter.json")
|
|
|
+ with open(output_json_path, 'w', encoding='utf-8') as f:
|
|
|
+ import json
|
|
|
+ json.dump(json_output, f, ensure_ascii=False, indent=4)
|
|
|
+
|
|
|
+ # 如果需要返回DataFrame,可以包含文件路径
|
|
|
+ result_rows.append({
|
|
|
+ Field_Return_TypeAnalyst: self.typeAnalyst(),
|
|
|
+ Field_PowerFarmCode: conf.dataContract.dataFilter.powerFarmID,
|
|
|
+ Field_Return_BatchCode: conf.dataContract.dataFilter.dataBatchNum,
|
|
|
+ Field_CodeOfTurbine: name[1],
|
|
|
+ Field_Return_FilePath: output_json_path,
|
|
|
+ Field_Return_IsSaveDatabase: True
|
|
|
+ })
|
|
|
+
|
|
|
result_rows.append({
|
|
|
Field_Return_TypeAnalyst: self.typeAnalyst(),
|
|
|
Field_PowerFarmCode: conf.dataContract.dataFilter.powerFarmID,
|
|
@@ -269,6 +265,7 @@ class PowerScatter2DAnalyst(AnalystWithGoodBadPoint):
|
|
|
Field_Return_FilePath: htmlFilePath,
|
|
|
Field_Return_IsSaveDatabase: True
|
|
|
})
|
|
|
+
|
|
|
|
|
|
result_df = pd.DataFrame(result_rows)
|
|
|
return result_df
|