init docs
This commit is contained in:
Binary file not shown.
@@ -0,0 +1,226 @@
|
|||||||
|
import sys
|
||||||
|
print("Python 解释器路径:", sys.executable)
|
||||||
|
print("已安装的包路径:", sys.path)
|
||||||
|
import data
|
||||||
|
import pandas as pd
|
||||||
|
import numpy as np
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
# ----------------------
|
||||||
|
# 配置参数
|
||||||
|
# ----------------------
|
||||||
|
RAW_DATA_PATH = Path(r'D:\经手项目\数据分析项目\哈萨克斯坦项目\CHUZHIBAO\T01\T01 原始数据.xlsx')
|
||||||
|
CLEANED_DATA_PATH = Path(r'D:\经手项目\数据分析项目\哈萨克斯坦项目\CHUZHIBAO\T01\T01cleaned_scada_data.xlsx')
|
||||||
|
#中间保存路径
|
||||||
|
AFTER_LIMIT_POWER_PATH = Path(r'D:\经手项目\数据分析项目\哈萨克斯坦项目\CHUZHIBAO\T01\T01after_limit_power_cleaning.xlsx')
|
||||||
|
AFTER_TIP_SPEED_PATH = Path(r'D:\经手项目\数据分析项目\哈萨克斯坦项目\CHUZHIBAO\T01\T01after_tip_speed_cleaning.xlsx')
|
||||||
|
AFTER_SPEED_POWER_PATH = Path(r'D:\经手项目\数据分析项目\哈萨克斯坦项目\CHUZHIBAO\T01\T01after_speed_power_cleaning.xlsx')
|
||||||
|
RESULT_TABLE_PATH = Path(r'F:\风速区间功率均值表.xlsx')
|
||||||
|
RESULT_PLOT_PATH = Path(r'F:\风速功率曲线.png')
|
||||||
|
RATED_POWER = 4800 # 额定功率
|
||||||
|
RATED_WIND_SPEED = 18# 额定风速
|
||||||
|
POWER_STEP = 5
|
||||||
|
WIND_SPEED_STEP = 0.25 # 风速区间步长
|
||||||
|
WIND_SPEED_CHANGE_THRESHOLD = 1
|
||||||
|
#TIME_THRESHOLD = pd.Timedelta(hours=2)
|
||||||
|
IQR_MULTIPLIER1 = 1.8
|
||||||
|
IQR_MULTIPLIER2 = 2
|
||||||
|
MINIMUM_GENERATOR_SPEED = 1
|
||||||
|
K = 0.9 # k值:删除小于k倍最小发电机转速的点
|
||||||
|
|
||||||
|
|
||||||
|
# ----------------------
|
||||||
|
# 工具函数:定义保存数据
|
||||||
|
def save_intermediate_data(data,file_path,step_name):
|
||||||
|
"""保存中间数据的专用函数,仅做保存"""
|
||||||
|
try:
|
||||||
|
if data.empty:
|
||||||
|
print(f"⚠️{step_name}为空,不保存")
|
||||||
|
return
|
||||||
|
#创建目录(如果不存在)
|
||||||
|
file_path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
#保存数据
|
||||||
|
data.to_excel(file_path, index=False,engine='openpyxl')
|
||||||
|
print(f"✅ {step_name}已保存({len(data)}条)至:{file_path}")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"❌ 保存{step_name}失败:{str(e)}")
|
||||||
|
|
||||||
|
# ----------------------
|
||||||
|
# 数据清洗函数
|
||||||
|
# ----------------------
|
||||||
|
|
||||||
|
def load_data(file_path):
|
||||||
|
try:
|
||||||
|
if not file_path.exists():
|
||||||
|
raise FileNotFoundError(f"文件不存在: {file_path}")
|
||||||
|
data = pd.read_excel(file_path, engine='openpyxl')
|
||||||
|
print(f"成功读取数据,共{len(data)}条记录")
|
||||||
|
return data
|
||||||
|
except Exception as e:
|
||||||
|
print(f"数据读取错误: {str(e)}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def clean_scada_data(raw_data):
|
||||||
|
if raw_data is None or raw_data.empty:
|
||||||
|
print("无数据可清洗")
|
||||||
|
return None
|
||||||
|
|
||||||
|
data = raw_data.copy()
|
||||||
|
|
||||||
|
# 1. 基础过滤:删除停机数据
|
||||||
|
initial_count = len(data)
|
||||||
|
data = data[data['平均有功功率'] > 0].copy()
|
||||||
|
|
||||||
|
#1.5 删除小于最小发电机转速的点
|
||||||
|
if not data.empty and '平均发电机转速' in data.columns:
|
||||||
|
# 计算阈值:k倍最小发电机转速
|
||||||
|
speed_threshold = K * MINIMUM_GENERATOR_SPEED
|
||||||
|
# 过滤数据
|
||||||
|
data = data[data['平均发电机转速'] >= speed_threshold].copy()
|
||||||
|
removed_count = initial_count - len(data)
|
||||||
|
print(f"发电机转速过滤后保留 {len(data)} 条数据(移除{removed_count}条低转速数据,阈值: {speed_threshold:.2f})")
|
||||||
|
initial_count = len(data)
|
||||||
|
|
||||||
|
# 2. 时间列处理
|
||||||
|
if '时间' not in data.columns:
|
||||||
|
print("警告:数据中未找到'时间'列,无法进行限功率点识别")
|
||||||
|
return data
|
||||||
|
|
||||||
|
data['时间'] = pd.to_datetime(data['时间'], errors='coerce')
|
||||||
|
time_invalid_count = data['时间'].isna().sum()
|
||||||
|
data = data.dropna(subset=['时间'])
|
||||||
|
print(f"时间处理后保留 {len(data)} 条数据(移除{time_invalid_count}条无效时间数据)")
|
||||||
|
|
||||||
|
# 3. 识别并移除限功率点
|
||||||
|
|
||||||
|
if not data.empty:
|
||||||
|
min_power = data['平均有功功率'].min()
|
||||||
|
power_intervals = np.arange(min_power, RATED_POWER, POWER_STEP)
|
||||||
|
limit_power_points = []
|
||||||
|
|
||||||
|
for interval in power_intervals:
|
||||||
|
mask = (data['平均有功功率'] >= interval) & (data['平均有功功率'] < interval + POWER_STEP)
|
||||||
|
interval_data = data[mask]
|
||||||
|
if interval_data.empty:
|
||||||
|
continue
|
||||||
|
|
||||||
|
grouped = interval_data.groupby(interval_data['时间'].dt.date)
|
||||||
|
for date, group in grouped:
|
||||||
|
group_sorted = group.sort_values('时间')
|
||||||
|
wind_speed_range = group_sorted['平均风速'].max()-group_sorted['平均风速'].min()
|
||||||
|
if wind_speed_range > WIND_SPEED_CHANGE_THRESHOLD:
|
||||||
|
limit_power_points.extend(group_sorted.index)
|
||||||
|
|
||||||
|
data = data.drop(limit_power_points,errors='ignore')
|
||||||
|
print(f"限功率识别后保留{len(data)}条数据(移除{len(limit_power_points)}条限功率数据)")
|
||||||
|
save_intermediate_data(data,AFTER_LIMIT_POWER_PATH,"限功率清洗后的数据")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# 4. 计算并清洗叶尖速比
|
||||||
|
if not data.empty and '平均风速' in data.columns :
|
||||||
|
data = data[data['平均风速'] > 0].copy()
|
||||||
|
print(f"移除风速为0的数据后保留 {len(data)} 条数据")
|
||||||
|
|
||||||
|
data['叶尖速比'] =data['平均发电机转速']*3.14*162*78*30/data['平均风速']
|
||||||
|
#data['叶尖速比']= data['叶轮转速']*100/data['平均风速']
|
||||||
|
|
||||||
|
if not data.empty:
|
||||||
|
wind_speed_min = data['平均风速'].min()
|
||||||
|
wind_speed_max = data['平均风速'].max()
|
||||||
|
wind_speed_intervals = np.arange(wind_speed_min, wind_speed_max, WIND_SPEED_STEP)
|
||||||
|
cleaned_data_list = []
|
||||||
|
|
||||||
|
for interval in wind_speed_intervals:
|
||||||
|
mask = (data['平均风速'] >= interval) & (data['平均风速'] < interval + WIND_SPEED_STEP)
|
||||||
|
interval_data = data[mask]
|
||||||
|
|
||||||
|
if len(interval_data) >= 4:
|
||||||
|
q1 = interval_data['叶尖速比'].quantile(0.25)
|
||||||
|
q3 = interval_data['叶尖速比'].quantile(0.75)
|
||||||
|
iqr = q3 - q1
|
||||||
|
lower = q1 - IQR_MULTIPLIER1 * iqr
|
||||||
|
upper = q3 + IQR_MULTIPLIER2 * iqr
|
||||||
|
interval_cleaned = interval_data[(interval_data['叶尖速比'] >= lower) &
|
||||||
|
(interval_data['叶尖速比'] <= upper)]
|
||||||
|
|
||||||
|
else:
|
||||||
|
interval_cleaned = interval_data
|
||||||
|
|
||||||
|
cleaned_data_list.append(interval_cleaned)
|
||||||
|
|
||||||
|
data = pd.concat(cleaned_data_list)
|
||||||
|
print(f"叶尖速比清洗后保留 {len(data)} 条数据")
|
||||||
|
save_intermediate_data(data,AFTER_TIP_SPEED_PATH,"叶尖速比清洗后的数据")
|
||||||
|
|
||||||
|
# 5. 风速-功率关系清洗
|
||||||
|
if not data.empty and '平均风速' in data.columns and '平均有功功率' in data.columns:
|
||||||
|
wind_speed_min = data['平均风速'].min()
|
||||||
|
wind_speed_max = data['平均风速'].max()
|
||||||
|
wind_speed_intervals = np.arange(wind_speed_min, wind_speed_max, WIND_SPEED_STEP)
|
||||||
|
final_cleaned_list = []
|
||||||
|
|
||||||
|
for interval in wind_speed_intervals:
|
||||||
|
mask = (data['平均风速'] >= interval) & (data['平均风速'] < interval + WIND_SPEED_STEP)
|
||||||
|
interval_data = data[mask]
|
||||||
|
|
||||||
|
if len(interval_data) >= 4:
|
||||||
|
q1 = interval_data['平均有功功率'].quantile(0.25)
|
||||||
|
q3 = interval_data['平均有功功率'].quantile(0.75)
|
||||||
|
iqr = q3 - q1
|
||||||
|
lower_limit = q1 - IQR_MULTIPLIER1 * iqr
|
||||||
|
upper_limit = q3 + IQR_MULTIPLIER2 * iqr
|
||||||
|
|
||||||
|
interval_cleaned = interval_data[(interval_data['平均有功功率'] >= lower_limit) &
|
||||||
|
(interval_data['平均有功功率'] <= upper_limit)]
|
||||||
|
else:
|
||||||
|
interval_cleaned = interval_data
|
||||||
|
final_cleaned_list.append(interval_cleaned)
|
||||||
|
|
||||||
|
data = pd.concat(final_cleaned_list)
|
||||||
|
print(f"风速-功率清洗后保留 {len(data)} 条数据")
|
||||||
|
save_intermediate_data(data, AFTER_SPEED_POWER_PATH, "风速功率清洗后的数据")
|
||||||
|
|
||||||
|
# 6. 高风速区二次过滤
|
||||||
|
#if not data.empty and '平均风速' in data.columns:
|
||||||
|
#high_wind_mask = data['平均风速'] >= RATED_WIND_SPEED
|
||||||
|
#high_wind_data = data[high_wind_mask]
|
||||||
|
|
||||||
|
#if len(high_wind_data) > 0:
|
||||||
|
#robust_mean = high_wind_data['平均有功功率'].median()
|
||||||
|
#high_wind_filtered = high_wind_data[
|
||||||
|
#(high_wind_data['平均有功功率'] >= robust_mean * 0.98) &
|
||||||
|
#(high_wind_data['平均有功功率'] <= robust_mean * 1.02)
|
||||||
|
#]
|
||||||
|
|
||||||
|
#low_wind_data = data[~high_wind_mask]
|
||||||
|
#data = pd.concat([low_wind_data, high_wind_filtered])
|
||||||
|
#print(f"高风速区二次过滤后保留 {len(data)} 条数据")
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
# ----------------------
|
||||||
|
# 主程序:执行数据清洗并保存结果
|
||||||
|
# ----------------------
|
||||||
|
if __name__ == "__main__":
|
||||||
|
# 加载原始数据
|
||||||
|
raw_data = load_data(RAW_DATA_PATH)
|
||||||
|
|
||||||
|
# 清洗数据
|
||||||
|
cleaned_data = clean_scada_data(raw_data)
|
||||||
|
|
||||||
|
# 保存清洗后的数据到指定路径
|
||||||
|
if cleaned_data is not None and not cleaned_data.empty:
|
||||||
|
try:
|
||||||
|
# 创建保存目录(如果不存在)
|
||||||
|
CLEANED_DATA_PATH.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
# 保存为Excel文件
|
||||||
|
cleaned_data.to_excel(CLEANED_DATA_PATH, index=False, engine='openpyxl')
|
||||||
|
print(f"清洗后的数据已成功保存到: {CLEANED_DATA_PATH}")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"保存数据时出错: {str(e)}")
|
||||||
|
else:
|
||||||
|
print("没有可保存的清洗后数据")
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,148 @@
|
|||||||
|
import pandas as pd
|
||||||
|
import numpy as np
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
# ----------------------
|
||||||
|
# 配置参数
|
||||||
|
# ----------------------
|
||||||
|
RAW_DATA_PATH = Path(r'H:\尽调项目\和展清云洛阳项目\6#\6 处理后.xlsx')
|
||||||
|
|
||||||
|
|
||||||
|
RESULT_TABLE_PATH = Path(r'H:\尽调项目\和展清云洛阳项目\6#\6风速区间功率均值表.xlsx')
|
||||||
|
RESULT_PLOT_PATH = Path(r'H:\尽调项目\和展清云洛阳项目\6#\6风速功率曲线.png')
|
||||||
|
RATED_POWER = 6700 # 额定功率
|
||||||
|
RATED_WIND_SPEED = 18 # 额定风速
|
||||||
|
POWER_STEP = 5
|
||||||
|
WIND_SPEED_STEP = 0.5 # 风速区间步长
|
||||||
|
WIND_SPEED_CHANGE_THRESHOLD = 1
|
||||||
|
#TIME_THRESHOLD = pd.Timedelta(hours=2)
|
||||||
|
IQR_MULTIPLIER = 1.5
|
||||||
|
MIN_DATA_COUNT = 3
|
||||||
|
|
||||||
|
def load_data(file_path):
|
||||||
|
try:
|
||||||
|
if not file_path.exists():
|
||||||
|
raise FileNotFoundError(f"文件不存在: {file_path}")
|
||||||
|
data = pd.read_excel(file_path, engine='openpyxl')
|
||||||
|
print(f"成功读取数据,共{len(data)}条记录")
|
||||||
|
return data
|
||||||
|
except Exception as e:
|
||||||
|
print(f"数据读取错误: {str(e)}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def clean_scada_data(raw_data):
|
||||||
|
if raw_data is None or raw_data.empty:
|
||||||
|
print("无数据可清洗")
|
||||||
|
return None
|
||||||
|
|
||||||
|
data = raw_data.copy()
|
||||||
|
|
||||||
|
# ----------------------
|
||||||
|
# 区间功率均值计算与可视化(核心修改部分)
|
||||||
|
# ----------------------
|
||||||
|
def calculate_interval_power_mean(raw_data, wind_speed_step=0.5):
|
||||||
|
"""按风速区间计算功率平均值,区间采用左开右闭形式 (start, end]"""
|
||||||
|
if raw_data is None or raw_data.empty:
|
||||||
|
print("无清洗后的数据可计算均值")
|
||||||
|
return None
|
||||||
|
|
||||||
|
# 确定风速区间范围
|
||||||
|
wind_min = 1 - wind_speed_step*0.5
|
||||||
|
wind_max = 25 + wind_speed_step*0.5
|
||||||
|
# 生成区间起点
|
||||||
|
wind_intervals = np.arange(wind_min, wind_max, wind_speed_step)
|
||||||
|
|
||||||
|
# 计算每个区间的平均功率(核心修改:使用左开右闭区间 (start, end])
|
||||||
|
result = []
|
||||||
|
for interval_start in wind_intervals:
|
||||||
|
interval_end = interval_start + wind_speed_step
|
||||||
|
# 关键修改:左边界>,右边界≤
|
||||||
|
mask = (raw_data['平均风速'] > interval_start) & (raw_data['平均风速'] <= interval_end)
|
||||||
|
interval_data = raw_data[mask]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if not interval_data.empty:
|
||||||
|
wind_mid = (interval_start + interval_end) / 2 # 区间中点
|
||||||
|
power_mean = interval_data['平均有功功率'].mean()
|
||||||
|
result.append({
|
||||||
|
'风速区间起点': interval_start,
|
||||||
|
'风速区间终点': interval_end,
|
||||||
|
'风速': wind_mid,
|
||||||
|
'实际功率': power_mean,
|
||||||
|
'区间数据量': len(interval_data),
|
||||||
|
})
|
||||||
|
|
||||||
|
# 转换为DataFrame并按风速排序
|
||||||
|
result_df = pd.DataFrame(result).sort_values('风速').reset_index(drop=True)
|
||||||
|
print(f"已计算{len(result_df)}个风速区间的功率平均值(左开右闭区间)")
|
||||||
|
return result_df
|
||||||
|
|
||||||
|
|
||||||
|
def plot_power_curve(result_df, rated_power, rated_wind_speed, save_path):
|
||||||
|
"""绘制风速-功率曲线并保存图片"""
|
||||||
|
if result_df is None or result_df.empty:
|
||||||
|
print("无数据可绘制曲线")
|
||||||
|
return False
|
||||||
|
|
||||||
|
plt.style.use('seaborn-v0_8-talk')
|
||||||
|
fig, ax = plt.subplots(figsize=(12, 6))
|
||||||
|
|
||||||
|
# 绘制功率曲线
|
||||||
|
ax.plot(result_df['风速'], result_df['实际功率'],
|
||||||
|
color='#2c7fb8', linewidth=2.5, marker='o', markersize=5,
|
||||||
|
label='实际功率曲线')
|
||||||
|
|
||||||
|
# 绘制额定功率参考线
|
||||||
|
ax.axhline(y=rated_power, color='#e41a1c', linestyle='--', linewidth=1.5,
|
||||||
|
label=f'额定功率 ({rated_power}kW)')
|
||||||
|
|
||||||
|
# 绘制额定风速参考线
|
||||||
|
ax.axvline(x=rated_wind_speed, color='#4daf4a', linestyle='-.', linewidth=1.5,
|
||||||
|
label=f'额定风速 ({rated_wind_speed}m/s)')
|
||||||
|
|
||||||
|
# 设置坐标轴标签和标题
|
||||||
|
ax.set_xlabel('风速 (m/s)', fontsize=12)
|
||||||
|
ax.set_ylabel('功率 (kW)', fontsize=12)
|
||||||
|
ax.set_title('风速-功率曲线(左开右闭区间平均值)', fontsize=14, pad=20)
|
||||||
|
|
||||||
|
# 添加网格和图例
|
||||||
|
ax.grid(alpha=0.3)
|
||||||
|
ax.legend(fontsize=10)
|
||||||
|
|
||||||
|
# 调整布局并保存
|
||||||
|
plt.tight_layout()
|
||||||
|
plt.savefig(save_path, dpi=300, bbox_inches='tight')
|
||||||
|
plt.close()
|
||||||
|
print(f"功率曲线已保存至 {save_path}")
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
# ----------------------
|
||||||
|
# 主程序执行
|
||||||
|
# ----------------------
|
||||||
|
if __name__ == "__main__":
|
||||||
|
# 1. 读取并清洗数据
|
||||||
|
raw_data = load_data(RAW_DATA_PATH)
|
||||||
|
|
||||||
|
|
||||||
|
# 2. 计算风速区间功率平均值(左开右闭区间)
|
||||||
|
interval_power_df = calculate_interval_power_mean(raw_data, wind_speed_step=WIND_SPEED_STEP)
|
||||||
|
|
||||||
|
# 3. 保存结果表格
|
||||||
|
if interval_power_df is not None:
|
||||||
|
try:
|
||||||
|
RESULT_TABLE_PATH.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
interval_power_df.to_excel(RESULT_TABLE_PATH, index=False)
|
||||||
|
print(f"风速区间功率均值表已保存至 {RESULT_TABLE_PATH}")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"保存表格失败:{str(e)}")
|
||||||
|
|
||||||
|
# 4. 绘制并保存功率曲线图片
|
||||||
|
if interval_power_df is not None:
|
||||||
|
plot_power_curve(interval_power_df, RATED_POWER, RATED_WIND_SPEED, RESULT_PLOT_PATH)
|
||||||
|
|
||||||
Binary file not shown.
Reference in New Issue
Block a user