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("没有可保存的清洗后数据")