processing_limit_power_by_solar.py 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. #!/usr/bin/env python
  2. # -*- coding:utf-8 -*-
  3. # @FileName :processing_limit_power_by_solar.py
  4. # @Time :2024/12/3 18:40
  5. # @Author :David
  6. # @Company: shenyang JY
  7. import os, time
  8. import pandas as pd
  9. import numpy as np
  10. from pymongo import MongoClient
  11. from flask import request, app
  12. from logs import Log
  13. import matplotlib.pyplot as plt
  14. import traceback
  15. current_path = os.path.dirname(__file__)
  16. parent_path = os.path.dirname(current_path)
  17. def get_data_from_mongo(args):
  18. mongodb_connection,mongodb_database,mongodb_read_table = "mongodb://root:sdhjfREWFWEF23e@192.168.1.43:30000/",args['mongodb_database'],args['mongodb_read_table']
  19. client = MongoClient(mongodb_connection)
  20. # 选择数据库(如果数据库不存在,MongoDB 会自动创建)
  21. db = client[mongodb_database]
  22. collection = db[mongodb_read_table] # 集合名称
  23. data_from_db = collection.find() # 这会返回一个游标(cursor)
  24. # 将游标转换为列表,并创建 pandas DataFrame
  25. df = pd.DataFrame(list(data_from_db))
  26. client.close()
  27. return df
  28. def insert_data_into_mongo(res_df,args):
  29. mongodb_connection,mongodb_database,mongodb_write_table = "mongodb://root:sdhjfREWFWEF23e@192.168.1.43:30000/",args['mongodb_database'],args['mongodb_write_table']
  30. client = MongoClient(mongodb_connection)
  31. db = client[mongodb_database]
  32. if mongodb_write_table in db.list_collection_names():
  33. db[mongodb_write_table].drop()
  34. print(f"Collection '{mongodb_write_table} already exist, deleted successfully!")
  35. collection = db[mongodb_write_table] # 集合名称
  36. # 将 DataFrame 转为字典格式
  37. data_dict = res_df.to_dict("records") # 每一行作为一个字典
  38. # 插入到 MongoDB
  39. collection.insert_many(data_dict)
  40. print("data inserted successfully!")
  41. @app.route('/processing_limit_power_by_solar', methods=['POST', 'GET'])
  42. def processing_limit_power_by_solar():
  43. # 获取程序开始时间
  44. start_time = time.time()
  45. result = {}
  46. success = 0
  47. print("Program starts execution!")
  48. try:
  49. logger = Log().logger
  50. args = request.values.to_dict()
  51. weather_power = get_data_from_mongo(args)
  52. lp = LimitPower(logger, args, weather_power)
  53. weather_power = lp.clean_limited_power('')
  54. print('args', args)
  55. logger.info(args)
  56. insert_data_into_mongo(weather_power, args)
  57. success = 1
  58. except Exception as e:
  59. my_exception = traceback.format_exc()
  60. my_exception.replace("\n", "\t")
  61. result['msg'] = my_exception
  62. end_time = time.time()
  63. result['success'] = success
  64. result['args'] = args
  65. result['start_time'] = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(start_time))
  66. result['end_time'] = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(end_time))
  67. print("Program execution ends!")
  68. return result
  69. class LimitPower(object):
  70. def __init__(self, logger, args, weather_power):
  71. self.logger = logger
  72. self.args = args
  73. self.weather_power = weather_power
  74. def segment_statis(self):
  75. """
  76. 对总辐射-实际功率进行分段处理,获取分度的中位点,四分位间距和斜率
  77. :return: glob_rp 总辐射分段
  78. """
  79. segs = [x for x in range(50, 2000, 100)] # 对辐照度以100为间隔进行分段
  80. xs = [segs[i-1]+x if i>0 else 25 for i, x in enumerate([50 for _ in segs])] # 分段的中间点
  81. glob_rp = {} # dict: key 辐照度分段中间点 value 分段内的实际功率
  82. for index, row in self.weather_power.iterrows():
  83. glob_ = row[self.args.usable_power["env"]]
  84. rp = row['C_REAL_VALUE']
  85. for i, seg in enumerate(segs):
  86. if glob_ <= seg and not (i > 0 and rp < 1):
  87. glob_rp.setdefault(xs[i], []).append(rp)
  88. break
  89. for i, x in enumerate(xs):
  90. rps = glob_rp.get(x)
  91. if rps is None:
  92. glob_rp = {k: v for k, v in glob_rp.items() if k not in xs[xs.index(x):]}
  93. break
  94. x_l = xs[i-1] if i > 0 else 0
  95. q2_l = glob_rp[xs[i-1]][0] if i > 0 else 0
  96. q1 = np.percentile(rps, self.args.usable_power['down_fractile']) # 实际功率下四分位点
  97. q2 = np.percentile(rps, 50) # 实际功率中位点
  98. q3 = np.percentile(rps, self.args.usable_power['up_fractile']) # 实际功率上四分位点
  99. iqr = q3 -q1 # 四分位间距
  100. k1 = round(q2/x, 5) # 整体斜率
  101. k2 = round((q2-q2_l)/(x-x_l), 5) # 趋势斜率,相对上一个中位点
  102. glob_rp[x] = [q2, iqr, k1, k2] # 更新dict
  103. return glob_rp
  104. def mapping_relation(self, glob_rp):
  105. """
  106. 拟合分段处理后的斜率和偏移量
  107. :param glob_rp: 总辐射分段
  108. :return: k_final 斜率 bias 实际功率的分布宽度, glob_rp 总辐射分段
  109. """
  110. ks, iqrs, delete_x, tag_x = [], [], [], [] # ks所有分段斜率集合,iqrs所有分段间距集合,delete_x删除的x坐标集合
  111. for x, values in glob_rp.items():
  112. k1 = values[-2]
  113. k2 = values[-1]
  114. iqrs.append(values[-3])
  115. if k1 > 0 and k2 > 0: # 清除趋势小于等于0的斜率
  116. ks.append(k1)
  117. tag_x.append(x)
  118. else:
  119. delete_x.append(x)
  120. # print("删除的斜率:", k1, k2)
  121. bias = round(np.median(iqrs), 3) # 中位点
  122. # print("++++1", ks)
  123. mean = np.mean(ks) # 均值
  124. std = np.std(ks) # 标准差
  125. ks = np.array(ks)
  126. z_score = (ks-mean)/std # z均值
  127. # print("----", z_score)
  128. outliers = np.abs(z_score) > self.args.usable_power['outliers_threshold'] # 超过阈值为离群点
  129. ks = ks[~outliers] # 消除离群点
  130. delete_x1 = list(np.array(tag_x)[outliers]) # 清除大于阈值的离群点
  131. k_final = round(np.mean(ks), 5) # 对清洗后的斜率做平均
  132. # print("++++2:", ks)
  133. delete_x.extend(delete_x1)
  134. self.logger.info("拟合可用功率,删除的斜率:" + ' '.join([str(x) for x in delete_x]))
  135. glob_rp = {k: v for k, v in glob_rp.items() if k not in delete_x} # 清洗后剩下的分段点位
  136. return k_final, bias, glob_rp
  137. def filter_unlimited_power(self, zfs, real_power, k, b):
  138. """
  139. 预测可用功主方法
  140. :param zfs: 要预测可用功率的总辐射
  141. :param k: 斜率
  142. :param b: 偏移量
  143. :return: 预测的可用功率
  144. """
  145. high = k*zfs+b/2 if k*zfs+b/2 < self.args.cap else self.args.cap
  146. low = k*zfs-b/2 if k*zfs-b/2 > 0 else 0
  147. if low <= real_power <= high:
  148. return True
  149. else:
  150. return False
  151. def clean_limited_power(self, name, is_repair=False):
  152. if is_repair is True:
  153. glob_rp = self.segment_statis()
  154. k_final, bias, glob_rp = self.mapping_relation(glob_rp)
  155. self.args.usable_power['k'] = float(k_final)
  156. self.args.usable_power['bias'] = float(bias)
  157. new_weather_power = []
  158. for index, row in self.weather_power.iterrows():
  159. zfs = row[self.args.usable_power["env"]]
  160. rp = row['C_REAL_VALUE']
  161. if self.filter_unlimited_power(zfs, rp, self.args.usable_power['k'], self.args.usable_power['bias'] * self.args.usable_power['coe']):
  162. row['c'] = 'red'
  163. new_weather_power.append(row)
  164. else:
  165. row['c'] = 'blue'
  166. new_weather_power.append(row)
  167. new_weather_power = pd.concat(new_weather_power, axis=1).T
  168. new_weather_power.plot.scatter(x=self.args.usable_power["env"], y='C_REAL_VALUE', c='c')
  169. plt.savefig(parent_path + '/figs/测光法{}.png'.format(name))
  170. new_weather_power = new_weather_power[new_weather_power['c'] == 'red']
  171. number = len(new_weather_power)
  172. self.logger.info("测光法-未清洗限电前,总共有:{}条数据".format(len(self.weather_power)))
  173. self.logger.info("测光法-清除限电后保留的点有:" + str(number) + " 占比:" + str(round(number / len(self.weather_power), 2)))
  174. return new_weather_power.loc[:, ['C_TIME', 'C_REAL_VALUE', 'C_ABLE_VALUE']]
  175. if __name__ == '__main__':
  176. power = pd.read_csv('2023-12-01至2023-12-23实际功率导出文件.csv', date_parser=['时间'])
  177. weather = pd.read_csv('2023-12-01至2023-12-23气象站数据导出文件.csv', date_parser=['时间'])
  178. weather_power = pd.merge(weather, power, on='时间') # 联立数据
  179. # glob_rp = segment_statis(weather_power)
  180. # k_final, bias, glob_rp = mapping_relation(glob_rp)