processed.py 2.6 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485
  1. import numpy as np
  2. import pandas as pd
  3. import pvlib
  4. def night_mask(
  5. df,
  6. lat=None,
  7. lon=None,
  8. time_col="time",
  9. elev_min=0.0,
  10. ssrd_col=None,
  11. hour_window=None,
  12. ):
  13. """
  14. 判断夜间点,返回 bool 数组:
  15. True = 夜间
  16. False = 白天
  17. 优先级:
  18. 1. lat/lon 均存在 -> 根据太阳高度角判断
  19. 2. ssrd_col 存在 -> 辐照 <= 0 判为夜间
  20. 3. hour_window 存在 -> 窗口外判为夜间
  21. """
  22. # 1. 根据太阳高度角判断
  23. times = pd.to_datetime(df[time_col], errors="coerce")
  24. # 如果时间没有时区,这里按 UTC 处理
  25. if times.dt.tz is None:
  26. times = times.dt.tz_localize("UTC")
  27. else:
  28. times = times.dt.tz_convert("UTC")
  29. solar_position = pvlib.solarposition.get_solarposition(
  30. time=times,
  31. latitude=float(lat),
  32. longitude=float(lon),
  33. )
  34. elevation = solar_position["apparent_elevation"].to_numpy()
  35. return elevation <= float(elev_min)
  36. # 2. 没有经纬度时,可根据太阳辐照判断
  37. if ssrd_col is not None and ssrd_col in df.columns:
  38. ssrd = pd.to_numeric(df[ssrd_col], errors="coerce")
  39. return (ssrd <= 0).fillna(False).to_numpy()
  40. # 3. 根据小时范围粗略判断
  41. if hour_window is not None:
  42. start_hour, end_hour = hour_window
  43. times = pd.to_datetime(df[time_col], errors="coerce")
  44. hours = times.dt.hour
  45. if start_hour <= end_hour:
  46. daytime = (hours >= start_hour) & (hours <= end_hour)
  47. else:
  48. daytime = (hours >= start_hour) | (hours <= end_hour)
  49. return (~daytime).fillna(False).to_numpy()
  50. raise ValueError(
  51. "无法判断昼夜:请提供 lat/lon、ssrd_col 或 hour_window"
  52. )
  53. def solor_processed_power(df, n, power_col="v1-Dlinear-288", *, lat=None, lon=None,
  54. elev_min=0.0, clip_negative=True, ssrd_col=None,
  55. hour_window=None, time_col="time"):
  56. """只对前 n 行: 夜间置 0(按太阳高度角判定) + 负值置 0。"""
  57. processed = df.copy()
  58. m = min(int(n), len(processed))
  59. if m <= 0:
  60. return processed
  61. head = processed.iloc[:m]
  62. night = night_mask(head, lat=lat, lon=lon, time_col=time_col, elev_min=elev_min,
  63. ssrd_col=ssrd_col, hour_window=hour_window)
  64. p = pd.to_numeric(head[power_col], errors="coerce")
  65. bad = np.asarray(night, dtype=bool)
  66. if clip_negative:
  67. bad |= (p < 0).fillna(False).to_numpy()
  68. if bad.any():
  69. processed.loc[processed.index[:m], power_col] = p.mask(bad, 0.0).to_numpy()
  70. return processed