dataMarker.py 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411
  1. import os
  2. import re
  3. import pandas as pd
  4. import numpy as np
  5. import matplotlib.pyplot as plt
  6. from matplotlib.pyplot import MultipleLocator
  7. import math
  8. import pdb
  9. from algorithmContract.confBusiness import * #将这个包里的全部加载
  10. intervalPower = 25
  11. intervalWindspeed = 0.25
  12. class DataMarker:
  13. #选取时间、风速、功率数据
  14. def preprocessData(self,dataFrame:pd.DataFrame,confData:ConfBusiness):
  15. timeStamp = dataFrame[confData.field_turbine_time]
  16. activePower = dataFrame[confData.field_power]
  17. windSpeed = dataFrame[confData.field_wind_speed]
  18. dataFramePartOfSCADA = pd.concat([timeStamp,activePower,windSpeed], axis=1)
  19. return dataFramePartOfSCADA
  20. #计算分仓数目
  21. def calculateIntervals(self,activePowerMax, ratedPower, windSpeedCutOut):
  22. binNumOfPower = math.floor((activePowerMax) / intervalPower) + 1 if (activePowerMax) >= ratedPower else math.floor(ratedPower / intervalPower)
  23. binNumOfWindSpeed = math.ceil(windSpeedCutOut / intervalWindspeed)
  24. return binNumOfPower, binNumOfWindSpeed
  25. def calculateTopP(self,activePowerMax,ratedPower):
  26. TopP = 0
  27. if activePowerMax >= ratedPower:
  28. TopP = math.floor((activePowerMax - ratedPower) / intervalPower) + 1
  29. else:
  30. TopP = 0
  31. return TopP
  32. def chooseData(self,dataFramePartOfSCADA,dataFrame:pd.DataFrame,confData:ConfBusiness):
  33. # 初始化标签列
  34. SM1 = dataFramePartOfSCADA.shape
  35. AA1 = SM1[0]
  36. lab = [[0] for _ in range(AA1)]
  37. lab = pd.DataFrame(lab,columns=['lab'])
  38. dataFramePartOfSCADA = pd.concat([dataFramePartOfSCADA,lab],axis=1) #在tpv后加一列标签列
  39. dataFramePartOfSCADA = dataFramePartOfSCADA.values
  40. SM = dataFramePartOfSCADA.shape #(52561,4)
  41. AA = SM[0]
  42. nCounter1 = 0
  43. DzMarch809_0 = np.zeros((AA, 3))
  44. Point_line = np.zeros(AA, dtype=int)
  45. APower = dataFrame[confData.field_power].values
  46. WSpeed = dataFrame[confData.field_wind_speed].values
  47. for i in range(AA):
  48. if (APower[i] > 10.0) & (WSpeed[i] > 0.0):
  49. nCounter1 += 1
  50. DzMarch809_0[nCounter1-1, 0] = WSpeed[i]
  51. DzMarch809_0[nCounter1-1, 1] = APower[i]
  52. Point_line[nCounter1-1] = i+1
  53. if APower[i] <= 10:
  54. dataFramePartOfSCADA[i,SM[1]-1] = -1
  55. DzMarch809 = DzMarch809_0[:nCounter1, :]
  56. return DzMarch809,nCounter1,dataFramePartOfSCADA,Point_line,SM
  57. def gridCount(self,binNumOfWindSpeed,binNumOfPower,nCounter1,DzMarch809):
  58. # 遍历有效数据
  59. XBoxNumber = np.ones((binNumOfPower, binNumOfWindSpeed),dtype=int)
  60. for i in range(nCounter1):
  61. for m in range(1, binNumOfPower + 1):
  62. if (DzMarch809[i,1] > (m - 1) * intervalPower) and (DzMarch809[i,1] <= m * intervalPower):
  63. nWhichP = m
  64. break
  65. for n in range(1, binNumOfWindSpeed + 1):
  66. if (DzMarch809[i, 0] > (n - 1) * intervalWindspeed) and (DzMarch809[i, 0] <= n * intervalWindspeed):
  67. nWhichV = n
  68. break
  69. if (nWhichP > 0) and (nWhichV > 0):
  70. XBoxNumber[nWhichP - 1][nWhichV - 1] += 1
  71. for m in range(1,binNumOfPower+1):
  72. for n in range(1,binNumOfWindSpeed+1):
  73. XBoxNumber[m-1,n-1] = XBoxNumber[m-1,n-1] - 1
  74. return XBoxNumber
  75. def percentageDots(self,XBoxNumber, binNumOfPower, binNumOfWindSpeed,axis):
  76. BoxPercent = np.zeros((binNumOfPower, binNumOfWindSpeed), dtype=float)
  77. BinSum = np.zeros((binNumOfPower if axis == 'power' else binNumOfWindSpeed, 1), dtype=int)
  78. for i in range(1,1+(binNumOfPower if axis == 'power' else binNumOfWindSpeed)):
  79. for m in range(1,(binNumOfWindSpeed if axis == 'power' else binNumOfPower)+1):
  80. BinSum[i-1] = BinSum[i-1] + (XBoxNumber[i-1,m-1] if axis == 'power' else XBoxNumber[m-1,i-1])
  81. for m in range(1,(binNumOfWindSpeed if axis == 'power' else binNumOfPower)+1):
  82. if BinSum[i-1]>0:
  83. if axis == 'power':
  84. BoxPercent[i-1,m-1] = (XBoxNumber[i-1,m-1] / BinSum[i-1])*100
  85. else:
  86. BoxPercent[m-1,i-1] = (XBoxNumber[m-1,i-1] / BinSum[i-1])*100
  87. return BoxPercent,BinSum
  88. def maxBoxPercentage(self,BoxPercent, binNumOfPower, binNumOfWindSpeed, axis):
  89. BoxMaxIndex = np.zeros((binNumOfPower if axis == 'power' else binNumOfWindSpeed,1),dtype = int)
  90. BoxMax = np.zeros((binNumOfPower if axis == 'power' else binNumOfWindSpeed,1),dtype = float)
  91. for m in range(1,(binNumOfPower if axis == 'power' else binNumOfWindSpeed)+1):
  92. BoxMaxIndex[m-1] = (np.argmax(BoxPercent[m-1, :])) if axis == 'power' else (np.argmax(BoxPercent[:, m-1]))
  93. BoxMax[m-1] = (np.max(BoxPercent[m-1, :]))if axis == 'power' else (np.max(BoxPercent[:, m-1]))
  94. return BoxMaxIndex, BoxMax
  95. def extendBoxPercent(self,m, BoxMax,TopP,BoxMaxIndex,BoxPercent,binNumOfPower,binNumOfWindSpeed):
  96. DotDense = np.zeros(binNumOfPower)
  97. DotDenseLeftRight = np.zeros((binNumOfPower,2))
  98. DotValve = m
  99. PDotDenseSum = 0
  100. for i in range(binNumOfPower - TopP):
  101. PDotDenseSum = BoxMax[i]
  102. iSpreadRight = 1
  103. iSpreadLeft = 1
  104. while PDotDenseSum < DotValve:
  105. if (BoxMaxIndex[i] + iSpreadRight) < binNumOfWindSpeed-1-1:
  106. PDotDenseSum += BoxPercent[i, BoxMaxIndex[i] + iSpreadRight]
  107. iSpreadRight += 1
  108. else:
  109. break
  110. if (BoxMaxIndex[i] - iSpreadLeft) > 0:
  111. PDotDenseSum += BoxPercent[i, BoxMaxIndex[i] - iSpreadLeft]
  112. iSpreadLeft += 1
  113. else:
  114. break
  115. iSpreadRight = iSpreadRight-1
  116. iSpreadLeft = iSpreadLeft-1
  117. DotDenseLeftRight[i, 0] = iSpreadLeft
  118. DotDenseLeftRight[i, 1] = iSpreadRight
  119. DotDense[i] = iSpreadLeft + iSpreadRight + 1
  120. return DotDenseLeftRight
  121. def calculatePWidth(self,binNumOfPower,TopP,DotDenseLeftRight,PBinSum):
  122. PowerLimit = np.zeros(binNumOfPower, dtype=int)
  123. WidthAverage = 0
  124. WidthAverage_L = 0
  125. nCounter = 0
  126. PowerLimitValve = 6
  127. N_Pcount = 20
  128. for i in range(binNumOfPower - TopP):
  129. if (DotDenseLeftRight[i, 1] > PowerLimitValve) and (PBinSum[i] > N_Pcount):
  130. PowerLimit[i] = 1
  131. if DotDenseLeftRight[i, 1] <= PowerLimitValve:
  132. WidthAverage += DotDenseLeftRight[i, 1]
  133. WidthAverage_L += DotDenseLeftRight[i,1]
  134. nCounter += 1
  135. WidthAverage /= nCounter if nCounter > 0 else 1
  136. WidthAverage_L /= nCounter if nCounter > 0 else 1
  137. return WidthAverage, WidthAverage_L,PowerLimit
  138. def amendMaxBox(self,binNumOfPower,TopP,PowerLimit,BoxMaxIndex):
  139. for i in range(1, binNumOfPower - TopP+1):
  140. if (PowerLimit[i] == 1) and (abs(BoxMaxIndex[i] - BoxMaxIndex[i - 1]) > 5):
  141. BoxMaxIndex[i] = BoxMaxIndex[i - 1] + 1
  142. return BoxMaxIndex
  143. def markBoxLimit(self,binNumOfPower,binNumOfWindSpeed,TopP,CurveWidthR,CurveWidthL,BoxMaxIndex):
  144. BBoxRemove = np.zeros((binNumOfPower, binNumOfWindSpeed), dtype=int)
  145. for m in range(binNumOfPower - TopP):
  146. for n in range(int(BoxMaxIndex[m]) + int(CurveWidthR), binNumOfWindSpeed):
  147. BBoxRemove[m, n] = 1
  148. for n in range(int(BoxMaxIndex[m]) - int(CurveWidthL)+1, 0, -1):
  149. BBoxRemove[m, n-1] = 2
  150. return BBoxRemove
  151. def markBoxPLimit(self,binNumOfPower,binNumOfWindSpeed,TopP,CurveWidthR,PowerLimit,BoxPercent,BoxMaxIndex,mm_value:int,BBoxRemove,nn_value:int):
  152. BBoxLimit = np.zeros((binNumOfPower, binNumOfWindSpeed), dtype=int)
  153. for i in range(2, binNumOfPower - TopP):
  154. if PowerLimit[i] == 1:
  155. BBoxLimit[i, int(BoxMaxIndex[i] + CurveWidthR + 1):binNumOfWindSpeed] = 1
  156. IsolateValve = 3
  157. for m in range(binNumOfPower - TopP):
  158. for n in range(int(BoxMaxIndex[m]) + int(CurveWidthR), binNumOfWindSpeed):
  159. if BoxPercent[m, n] < IsolateValve:
  160. BBoxRemove[m, n] = 1
  161. for m in range(binNumOfPower - TopP, binNumOfPower):
  162. for n in range(binNumOfWindSpeed):
  163. BBoxRemove[m, n] = 3
  164. # 标记功率主带拐点左侧的欠发网格
  165. for m in range(mm_value - 1, binNumOfPower - TopP):
  166. for n in range(int(nn_value) - 2):
  167. BBoxRemove[m, n] = 2
  168. return BBoxLimit
  169. def markData(self,binNumOfPower, binNumOfWindSpeed,DzMarch809,BBoxRemove,nCounter1):
  170. DzMarch809Sel = np.zeros(nCounter1, dtype=int)
  171. nWhichP = 0
  172. nWhichV = 0
  173. for i in range(nCounter1):
  174. for m in range( binNumOfPower ):
  175. if ((DzMarch809[i,1])> m * intervalPower) and ((DzMarch809[i,1]) <= (m+1) * intervalPower):
  176. nWhichP = m #m记录的是index
  177. break
  178. for n in range( binNumOfWindSpeed ):
  179. if DzMarch809[i,0] > ((n+1) * intervalWindspeed - intervalWindspeed/2) and DzMarch809[i,0] <= ((n+1) * intervalWindspeed + intervalWindspeed / 2):
  180. nWhichV = n
  181. break
  182. if nWhichP >= 0 and nWhichV >= 0:
  183. if BBoxRemove[nWhichP, nWhichV] == 1:
  184. DzMarch809Sel[i] = 1
  185. elif BBoxRemove[nWhichP, nWhichV] == 2:
  186. DzMarch809Sel[i] = 2
  187. elif BBoxRemove[nWhichP , nWhichV] == 3:
  188. DzMarch809Sel[i] = 0
  189. return DzMarch809Sel
  190. def windowFilter(self,nCounter1,ratedPower,DzMarch809,DzMarch809Sel,Point_line):
  191. PVLimit = np.zeros((nCounter1, 3))
  192. nLimitTotal = 0
  193. nWindowLength = 6
  194. LimitWindow = np.zeros(nWindowLength)
  195. UpLimit = 0
  196. LowLimit = 0
  197. PowerStd = 30
  198. nWindowNum = np.floor(nCounter1/nWindowLength)
  199. PowerLimitUp = ratedPower - 100
  200. PowerLimitLow = 100
  201. # 循环遍历每个窗口
  202. for i in range(int(nWindowNum)):
  203. start_idx = i * nWindowLength
  204. end_idx = start_idx + nWindowLength
  205. LimitWindow = DzMarch809[start_idx:end_idx, 1]
  206. bAllInAreas = np.all(LimitWindow >= PowerLimitLow) and np.all(LimitWindow <= PowerLimitUp)
  207. if not bAllInAreas:
  208. continue
  209. UpLimit = LimitWindow[0] + PowerStd
  210. LowLimit = LimitWindow[0] - PowerStd
  211. bAllInUpLow = np.all(LimitWindow >= LowLimit) and np.all(LimitWindow <= UpLimit)
  212. if bAllInUpLow:
  213. DzMarch809Sel[start_idx:end_idx] = 4
  214. for j in range(nWindowLength):
  215. PVLimit[nLimitTotal, :2] = DzMarch809[start_idx + j, :2]
  216. PVLimit[nLimitTotal, 2] = Point_line[start_idx + j] # 对数据进行标识
  217. nLimitTotal += 1
  218. return PVLimit,nLimitTotal
  219. def store_points(self,DzMarch809, DzMarch809Sel,Point_line, nCounter1):
  220. PVDot = np.zeros((nCounter1, 3))
  221. PVBad = np.zeros((nCounter1, 3))
  222. nCounterPV = 0
  223. nCounterBad = 0
  224. for i in range(nCounter1):
  225. if DzMarch809Sel[i] == 0:
  226. nCounterPV += 1
  227. PVDot[nCounterPV-1, :2] = DzMarch809[i, :2]
  228. PVDot[nCounterPV-1, 2] = Point_line[i]
  229. elif DzMarch809Sel[i] in [1, 2, 3]:
  230. nCounterBad += 1
  231. PVBad[nCounterBad-1, :2] = DzMarch809[i, :2]
  232. PVBad[nCounterBad-1, 2] = Point_line[i]
  233. return PVDot, nCounterPV,PVBad,nCounterBad
  234. def markAllData(self,nCounterPV,nCounterBad,dataFramePartOfSCADA,PVDot,PVBad,SM,nLimitTotal,PVLimit):
  235. for i in range(nCounterPV):
  236. dataFramePartOfSCADA[int(PVDot[i, 2] - 1), (SM[1]-1)] = 1
  237. #坏点
  238. for i in range(nCounterBad):
  239. dataFramePartOfSCADA[int(PVBad[i, 2] - 1),(SM[1]-1)] = 5 # 坏点标识
  240. # 对所有数据中的限电点进行标注
  241. for i in range(nLimitTotal):
  242. dataFramePartOfSCADA[int(PVLimit[i, 2] - 1),(SM[1]-1)] = 4 # 限电点标识
  243. return dataFramePartOfSCADA
  244. # 4. 数据可视化
  245. def plotData(self,turbineName:str,ws:list, ap:list):
  246. fig = plt.figure()
  247. plt.scatter(ws, ap, s=1, c='black', marker='.')
  248. ax = plt.gca()
  249. ax.xaxis.set_major_locator(MultipleLocator(5))
  250. ax.yaxis.set_major_locator(MultipleLocator(500))
  251. plt.title(turbineName)
  252. plt.xlim((0, 30))
  253. plt.ylim((0, 2200))
  254. plt.tick_params(labelsize=8)
  255. plt.xlabel("V/(m$·$s$^{-1}$)", fontsize=8)
  256. plt.ylabel("P/kW", fontsize=8)
  257. plt.show()
  258. def main(self,confData: ConfBusiness,dataFrame:pd.DataFrame):
  259. dataFramePartOfSCADA = self.preprocessData(dataFrame, confData)
  260. powerMax = dataFrame[confData.field_power].max()
  261. binNumOfPower, binNumOfWindSpeed = self.calculateIntervals(powerMax,confData.rated_power,confData.rated_cut_out_windspeed)
  262. TopP = self.calculateTopP(powerMax,confData.rated_power)
  263. # 根据功率阈值对数据进行标签分配
  264. DzMarch809,nCounter1,dataFramePartOfSCADA,Point_line,SM = self.chooseData(dataFramePartOfSCADA,dataFrame,confData)
  265. XBoxNumber = self.gridCount(binNumOfWindSpeed,binNumOfPower,nCounter1,DzMarch809)
  266. PBoxPercent,PBinSum = self.percentageDots(XBoxNumber, binNumOfPower, binNumOfWindSpeed, 'power')
  267. VBoxPercent,VBinSum = self.percentageDots(XBoxNumber, binNumOfPower, binNumOfWindSpeed, 'speed')
  268. PBoxMaxIndex, PBoxMaxP = self.maxBoxPercentage(PBoxPercent, binNumOfPower, binNumOfWindSpeed, 'power')
  269. VBoxMaxIndex, VBoxMaxV = self.maxBoxPercentage(VBoxPercent, binNumOfPower, binNumOfWindSpeed, 'speed')
  270. if PBoxMaxIndex[0] > 14: PBoxMaxIndex[0] = 9
  271. DotDenseLeftRight = self.extendBoxPercent(90, PBoxMaxP,TopP,PBoxMaxIndex,PBoxPercent,binNumOfPower,binNumOfWindSpeed)
  272. WidthAverage, WidthAverage_L,PowerLimit = self.calculatePWidth(binNumOfPower,TopP,DotDenseLeftRight,PBinSum)
  273. PBoxMaxIndex = self.amendMaxBox(binNumOfPower,TopP,PowerLimit,PBoxMaxIndex)
  274. # 计算功率主带的左右边界
  275. CurveWidthR = np.ceil(WidthAverage) + 2
  276. CurveWidthL = np.ceil(WidthAverage_L) + 2
  277. #确定功率主带的左上拐点,即额定风速位置的网格索引
  278. CurveTop = np.zeros((2, 1), dtype=int)
  279. BTopFind = 0
  280. mm_value = None
  281. nn_value = None
  282. for m in range(binNumOfPower - TopP, 0, -1):
  283. for n in range(int(np.floor(int(confData.rated_cut_in_windspeed) / intervalWindspeed)), binNumOfWindSpeed - 1):
  284. if (VBoxPercent[m, n - 1] < VBoxPercent[m, n]) and (VBoxPercent[m, n] <= VBoxPercent[m, n + 1]) and (XBoxNumber[m, n] >= 3):
  285. CurveTop[0] = m
  286. CurveTop[1] = n #[第80个,第40个]
  287. BTopFind = 1
  288. mm_value = m
  289. nn_value = n
  290. break
  291. if BTopFind == 1:
  292. break
  293. #标记网格
  294. BBoxRemove = self.markBoxLimit(binNumOfPower,binNumOfWindSpeed,TopP,CurveWidthR,CurveWidthL,PBoxMaxIndex)
  295. if mm_value is not None and nn_value is not None:
  296. BBoxLimit = self.markBoxPLimit(binNumOfPower,binNumOfWindSpeed,TopP,CurveWidthR,PowerLimit,PBoxPercent,PBoxMaxIndex,mm_value,BBoxRemove,nn_value)
  297. DzMarch809Sel = self.markData(binNumOfPower, binNumOfWindSpeed,DzMarch809,BBoxRemove,nCounter1)
  298. PVLimit,nLimitTotal = self.windowFilter(nCounter1,confData.rated_power,DzMarch809,DzMarch809Sel,Point_line)
  299. #将功率滑动窗口主带平滑化
  300. nSmooth = 0
  301. for i in range(binNumOfPower - TopP - 1):
  302. PVLeftDown = np.zeros(2)
  303. PVRightUp = np.zeros(2)
  304. if PBoxMaxIndex[i + 1] - PBoxMaxIndex[i] >= 1:
  305. # 计算左下和右上顶点的坐标
  306. PVLeftDown[0] = (PBoxMaxIndex[i]+1 + CurveWidthR) * 0.25 - 0.125
  307. PVLeftDown[1] = (i) * 25
  308. PVRightUp[0] = (PBoxMaxIndex[i+1]+1 + CurveWidthR) * 0.25 - 0.125
  309. PVRightUp[1] = (i+1) * 25
  310. for m in range(nCounter1):
  311. # 检查当前点是否在锯齿区域内
  312. if (DzMarch809[m, 0] > PVLeftDown[0]) and (DzMarch809[m, 0] < PVRightUp[0]) and (DzMarch809[m, 1] > PVLeftDown[1]) and (DzMarch809[m, 1] < PVRightUp[1]):
  313. # 检查斜率是否大于对角连线
  314. if ((DzMarch809[m, 1] - PVLeftDown[1]) / (DzMarch809[m, 0] - PVLeftDown[0])) > ((PVRightUp[1] - PVLeftDown[1]) / (PVRightUp[0] - PVLeftDown[0])):
  315. # 如果在锯齿左上三角形中,则选中并增加锯齿平滑计数器
  316. DzMarch809Sel[m] = 0
  317. nSmooth += 1
  318. # DzMarch809Sel 数组现在包含了锯齿平滑的选择结果,nSmooth 是选中的点数
  319. PVDot, nCounterPV,PVBad,nCounterBad = self.store_points(DzMarch809, DzMarch809Sel,Point_line, nCounter1)
  320. #标注
  321. dataFramePartOfSCADA = self.markAllData(nCounterPV,nCounterBad,dataFramePartOfSCADA,PVDot,PVBad,SM,nLimitTotal,PVLimit)
  322. A = dataFramePartOfSCADA[:,-1]
  323. A=pd.DataFrame(A,columns=['lab'])
  324. dataFrame = pd.concat([dataFrame,A],axis=1)
  325. """
  326. 标识 说明
  327. 5 坏点
  328. 4 限功率点
  329. 1 好点
  330. 0 null
  331. -1 P<=10
  332. """
  333. print("lab unique :",dataFrame['lab'].unique())
  334. # data=dataFrame[dataFrame['lab']==1]
  335. # self.plotData(data[Field_NameOfTurbine].iloc[0],data[confData.field_wind_speed],data[confData.field_power])
  336. return dataFrame
  337. if __name__ == '__main__':
  338. main()