calculate_cip_cycles.py 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. import pandas as pd
  2. from datetime import datetime, timedelta
  3. import os
  4. def read_cip_events(file_path="CIP_events_all_units.csv"):
  5. """
  6. 读取 CIP 事件文件
  7. 参数:
  8. file_path: CIP 事件 CSV 文件路径
  9. 返回:
  10. DataFrame,包含 unit_id, start_date, end_date
  11. """
  12. cip_df = pd.read_csv(file_path, parse_dates=['start_date', 'end_date'])
  13. cip_df = cip_df.sort_values(['unit_id', 'start_date']).reset_index(drop=True)
  14. return cip_df
  15. def filter_cip_events(cip_df, min_gap_days=30):
  16. """
  17. 过滤 CIP 事件:
  18. 1. 删除与前一 CIP 结束间隔 < min_gap_days 的事件
  19. 2. 删除与下一 CIP 开始间隔 < min_gap_days 的事件
  20. 参数:
  21. cip_df: 原始 CIP 事件 DataFrame
  22. min_gap_days: 间隔阈值(天)
  23. 返回:
  24. 过滤后的 CIP DataFrame
  25. """
  26. filtered_all = []
  27. # 按机组处理
  28. for unit_id, group in cip_df.groupby('unit_id'):
  29. group = group.sort_values('start_date').reset_index(drop=True)
  30. # -------- 第一步:检查前一 CIP 间隔 --------
  31. keep_rows = []
  32. for idx, row in group.iterrows():
  33. if idx == 0:
  34. keep_rows.append(row)
  35. continue
  36. prev_end = group.loc[idx - 1, 'end_date']
  37. if (row['start_date'] - prev_end).days >= min_gap_days:
  38. keep_rows.append(row)
  39. # 如果间隔小于 min_gap_days,就直接不保留该事件
  40. group = pd.DataFrame(keep_rows).reset_index(drop=True)
  41. # -------- 第二步:检查下一 CIP 间隔 --------
  42. keep_rows = []
  43. for idx, row in group.iterrows():
  44. if idx == len(group) - 1:
  45. keep_rows.append(row)
  46. continue
  47. next_start = group.loc[idx + 1, 'start_date']
  48. if (next_start - row['end_date']).days >= min_gap_days:
  49. keep_rows.append(row)
  50. # 如果与下一 CIP 开始间隔小于 min_gap_days,则不保留该事件
  51. filtered_all.append(pd.DataFrame(keep_rows))
  52. return pd.concat(filtered_all, ignore_index=True)
  53. def get_full_stop_days(control_file):
  54. """
  55. 计算完全停机天数:
  56. - 一整天没有控制字记录的日期视为完全停机
  57. """
  58. df = pd.read_csv(control_file, parse_dates=['time'])
  59. df = df.sort_values('time')
  60. full_stop_days = []
  61. if df.empty:
  62. return full_stop_days
  63. # 获取记录的日期范围
  64. start_date = df['time'].dt.date.min()
  65. end_date = df['time'].dt.date.max()
  66. # 生成连续日期(datetime.date)
  67. all_dates = pd.date_range(start=start_date, end=end_date).to_pydatetime()
  68. all_dates = [d.date() for d in all_dates] # 转成 datetime.date
  69. # 获取已记录的日期
  70. recorded_dates = set(df['time'].dt.date.unique())
  71. # 没有记录的日期即完全停机天
  72. for d in all_dates:
  73. if d not in recorded_dates:
  74. full_stop_days.append(d)
  75. return full_stop_days
  76. def calculate_cip_cycles(filtered_cip_df, high_freq_dir="high_freq_cip"):
  77. """
  78. 根据 CIP 事件和完全停机天数计算 CIP 周期
  79. 参数:
  80. filtered_cip_df: 过滤后的 CIP 事件 DataFrame
  81. high_freq_dir: 控制字文件所在目录
  82. 返回:
  83. DataFrame,包含 unit_id, cycle_start, cycle_end, cycle_days
  84. """
  85. cip_cycles = []
  86. # 按机组处理
  87. for unit_id, group in filtered_cip_df.groupby('unit_id'):
  88. group = group.sort_values('start_date').reset_index(drop=True)
  89. # 读取控制字文件,获取完全停机天数
  90. control_file = os.path.join(high_freq_dir, f"C_M_RO{unit_id}_DB_control_word_raw.csv")
  91. full_stop_days = get_full_stop_days(control_file)
  92. for idx in range(len(group)):
  93. # 周期开始时间 = 上一个 CIP 结束的下一天
  94. prev_end = group.loc[idx - 1, 'end_date'] if idx > 0 else None
  95. start_cycle = (prev_end + timedelta(days=1)) if prev_end else None
  96. # 周期结束时间 = 当前 CIP 开始的前一天
  97. end_cycle = group.loc[idx, 'start_date'] - timedelta(days=1)
  98. # 如果没有可计算周期,则跳过
  99. if start_cycle and start_cycle > end_cycle:
  100. continue
  101. if start_cycle:
  102. # 周期天数 = 周期结束日 - 周期开始日 + 1 - 完全停机天数
  103. cycle_days = (end_cycle - start_cycle).days + 1
  104. stop_days = sum(1 for d in full_stop_days if start_cycle.date() <= d <= end_cycle.date())
  105. cycle_days -= stop_days
  106. cip_cycles.append({
  107. 'unit_id': unit_id,
  108. 'cycle_start': start_cycle,
  109. 'cycle_end': end_cycle,
  110. 'cycle_days': cycle_days
  111. })
  112. return pd.DataFrame(cip_cycles)
  113. # ======= 主流程 =======
  114. if __name__ == "__main__":
  115. # 1. 读取 CIP 事件文件
  116. cip_file = "CIP_events_all_units.csv"
  117. cip_df = read_cip_events(cip_file)
  118. # 2. 过滤 CIP 事件(删除间隔小于30天的异常事件)
  119. filtered_cip_df = filter_cip_events(cip_df, min_gap_days=30)
  120. # 3. 计算 CIP 周期(排除完全停机天数)
  121. cip_cycles_df = calculate_cip_cycles(filtered_cip_df, high_freq_dir="high_freq_cip")
  122. # 4. 保存结果
  123. cip_cycles_df.to_csv("CIP_cycles_with_days.csv", index=False)
  124. print("CIP 周期计算完成,结果已保存为 CIP_cycles_with_days.csv")