processing_limit_power_by_wind.py 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. #!/usr/bin/env python
  2. # -*- coding:utf-8 -*-
  3. # @FileName :processing_limit_power_by_wind.py
  4. # @Time :2024/12/3 14:32
  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 pickle, 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_wind', methods=['POST', 'GET'])
  42. def processing_limit_power_by_agcavc():
  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. self.step = self.args.usable_power['step']
  75. self.segs = np.array([x * self.step for x in range(1, 50)]) # 对风速以50为间隔进行分段
  76. self.xs = np.array([self.segs[i - 1] + x if i > 0 else self.step / 2 for i, x in
  77. enumerate([self.step / 2 for _ in self.segs])]) # 分段的中间点
  78. self.polynomial = None
  79. self.width = 0
  80. self.max_ws = 50
  81. def segment_statis(self):
  82. """
  83. 对机头风速-实际功率进行分段处理,获取分度的中位点,四分位间距和斜率
  84. :return: glob_rp 总辐射分段
  85. """
  86. glob_rp = {} # dict: key 辐照度分段中间点 value 分段内的实际功率
  87. for index, row in self.weather_power.iterrows():
  88. ws_ = row[self.opt.usable_power["env"]]
  89. rp = row['C_REAL_VALUE']
  90. for i, seg in enumerate(self.segs):
  91. if ws_ <= seg:
  92. glob_rp.setdefault(self.xs[i], []).append(rp)
  93. break
  94. rms = []
  95. for i, x in enumerate(self.xs):
  96. rps = glob_rp.get(x)
  97. if rps is None:
  98. continue
  99. rps = np.array(rps)
  100. up = self.opt.usable_power['up_fractile']
  101. down = self.opt.usable_power['down_fractile']
  102. offset = 0
  103. while True:
  104. index = i-1 if i > 0 else 0
  105. while (self.xs[index] in rms or self.xs[index] not in glob_rp) and index >0:
  106. index -= 1
  107. x_l = self.xs[index] if index > 0 else 0
  108. q2_l = glob_rp[self.xs[index]][0] if index > 0 else 0
  109. down = down + offset
  110. if down > up:
  111. rms.append(x)
  112. self.logger.info("删除的坐标点为:{}".format(x))
  113. break
  114. q1 = np.percentile(rps, down) # 下四分位点
  115. q2 = round(np.percentile(rps, down+(up-down)/2), 3) # 中位点
  116. q3 = np.percentile(rps, up) # 上四分为点
  117. # q2 = np.around(np.mean(rps[(rps >= q1) & (rps <= q3)]), 3)
  118. iqr = q3 - q1 # 四分位间距
  119. k2 = round((q2-q2_l)/(x-x_l), 3) # 趋势斜率
  120. # std = np.around(np.std(rps[(rps >= q1) & (rps <= q3)]), 3)
  121. # mean = np.around(np.mean(rps), 3) # 实际功率均值
  122. # std = np.around(np.std(rps), 3) # 实际功率标准差
  123. # print("看看q2={},mean={}".format(q2, mean))
  124. if k2 >= 0:
  125. glob_rp[x] = [q2, iqr] # 更新dict
  126. break
  127. else:
  128. offset += 1
  129. glob_rp = {k: glob_rp[k] for k in glob_rp.keys() if k not in rms}
  130. glob_rp = {k: glob_rp[k] for k in sorted(glob_rp.keys())}
  131. return glob_rp
  132. def mapping_relation(self, glob_rp):
  133. degree = self.args.usable_power['degree']
  134. xs = list(glob_rp.keys())
  135. ys = [y[0] for y in glob_rp.values()]
  136. self.width = np.median(np.array([y[1] for y in glob_rp.values()]))
  137. coefficients = np.polyfit(xs, ys, degree)
  138. self.polynomial = np.poly1d(coefficients)
  139. self.max_ws = max(xs)
  140. # y_fit = self.polynomial(xs)
  141. # plt.scatter(xs, ys, label='Data', color='red')
  142. # plt.plot(xs, y_fit, label='Fitted polynomial', color='blue')
  143. # plt.plot(xs, y_fit+self.width/2, label='up polynomial', color='purple')
  144. # plt.plot(xs, y_fit-self.width/2, label='down polynomial', color='green')
  145. # plt.legend()
  146. # plt.xlabel('x')
  147. # plt.ylabel('y')
  148. # plt.title(f'Polynomial Fit (degree {degree})')
  149. # plt.show()
  150. def saveVar(self, path, data):
  151. os.makedirs(os.path.dirname(path), exist_ok=True)
  152. with open(path, 'wb') as file:
  153. pickle.dump(data, file)
  154. def filter_unlimited_power(self, ws, real_power):
  155. """
  156. 预测可用功主方法
  157. :param zfs: 要预测可用功率的总辐射
  158. :param k: 斜率
  159. :param b: 偏移量
  160. :return: 预测的可用功率
  161. """
  162. # coe = self.opt.usable_power['outliers_threshold']
  163. # seg = self.xs[np.argmax(self.segs >= ws)]
  164. up_offset = self.args.usable_power['up_offset']
  165. down_offset = self.args.usable_power['down_offset']
  166. high = self.polynomial(ws) + self.width/up_offset if self.polynomial(ws) + self.width/up_offset < self.opt.cap else self.opt.cap
  167. low = self.polynomial(ws) - self.width/down_offset if self.polynomial(ws) - self.width/down_offset > 0 else 0
  168. if low <= real_power <= high:
  169. return True
  170. else:
  171. return False
  172. def clean_limited_power(self, name):
  173. glob_rp = self.segment_statis()
  174. self.mapping_relation(glob_rp)
  175. new_weather_power, number = [], 0
  176. # fig, ax = plt.subplots()
  177. for index, row in self.weather_power.iterrows():
  178. zfs = row[self.args.usable_power["env"]]
  179. rp = row['C_REAL_VALUE']
  180. if zfs < 0 or rp < 0:
  181. continue
  182. if self.filter_unlimited_power(zfs, rp) and zfs <= self.max_ws:
  183. row['c'] = 'red'
  184. new_weather_power.append(row)
  185. else:
  186. row['c'] = 'blue'
  187. new_weather_power.append(row)
  188. new_weather_power = pd.concat(new_weather_power, axis=1).T
  189. new_weather_power.plot.scatter(x=self.args.usable_power["env"], y='C_REAL_VALUE', c='c')
  190. plt.savefig(parent_path + '/figs/测风法{}.png'.format(name))
  191. new_weather_power = new_weather_power[new_weather_power['c'] == 'red']
  192. number = len(new_weather_power)
  193. self.logger.info("未清洗限电前,总共有:{}条数据".format(len(self.weather_power)))
  194. self.logger.info("清除限电后保留的点有:" + str(number) + " 占比:" + str(round(number / len(self.weather_power), 2)))
  195. return new_weather_power.loc[:, ['C_TIME', 'C_REAL_VALUE', 'C_ABLE_VALUE', self.args.usable_power['env']]]
  196. if __name__ == '__main__':
  197. from logs import Log
  198. log = Log().logger
  199. args = {}
  200. # 实例化配置类
  201. power = pd.read_csv('./data/power.csv')
  202. weather = pd.read_csv('./data/tower-1-process.csv')
  203. weather_power = pd.merge(weather, power, on='C_TIME') # 联立数据
  204. lp = LimitPower(log, args, weather_power)
  205. # glob_rp = lp.segment_statis()
  206. # lp.mapping_relation(glob_rp)
  207. lp.clean_limited_power('测试1')
  208. # glob_rp = {k: glob_rp[k] for k in sorted(glob_rp.keys())}
  209. # keys = list(glob_rp.keys())
  210. # values = [v[0] for v in glob_rp.values()]
  211. # import matplotlib.pyplot as plt
  212. # fig, ax = plt.subplots()
  213. # ax.plot(keys, values)
  214. # plt.show()