Pandas:如何按日期拆分跨天的DataFrame时间区间行?
拆分跨天时间区间的DataFrame行
需求说明
需要将DataFrame中跨天的时间区间拆分为按天分段的行,每行对应当日的时间区间,并计算对应的时间差。
原始DataFrame
| Tiempo_ini | Valor | Tiempo_diff | Tiempo_fin | Cambio |
|---|---|---|---|---|
| 2018-01-02 21:51:47 | 1 | 0 days 12:46:30 | 2018-01-03 10:38:17 | True |
| 2018-01-03 22:11:17 | 1 | 0 days 07:46:00 | 2018-01-04 05:57:17 | True |
| 2018-01-04 22:22:17 | 1 | 0 days 08:38:00 | 2018-01-05 07:00:17 | True |
| 2018-01-05 07:01:17 | 1 | 1 days 13:01:30 | 2018-01-06 20:02:47 | True |
| 2018-01-06 20:30:17 | 1 | 0 days 04:27:00 | 2018-01-07 00:57:17 | True |
期望拆分效果
以第一行为例,需拆分为以下两行:
| Tiempo_ini | Valor | Tiempo_fin | Tiempo_diff | Cambio |
|---|---|---|---|---|
| 2018-01-02 21:51:47 | 1 | 2018-01-03 00:00:00 | 0 days 02:08:13 | True |
| 2018-01-03 00:00:00 | 1 | 2018-01-03 10:38:17 | 0 days 10:38:17 | True |
尝试的代码及问题
以下是尝试实现的代码,但循环中错误修改了结束时间,导致拆分逻辑混乱:
nuevos_registros = [] for index, row in df.iterrows(): if row['Cambio'] == True: tiempo_ini = row['Tiempo_ini'] tiempo_diff = row['Tiempo_diff'] tiempo_fin = row['Tiempo_fin'] while tiempo_diff > timedelta(seconds=0): tiempo_fin_original = row['Tiempo_fin'] tiempo_diff_original = tiempo_diff tiempo_diff = ((tiempo_ini + timedelta(days=1)).replace(hour=0, minute=0, second=0) - tiempo_ini) nuevo_registro = { "Tiempo_ini": tiempo_ini, "Valor": row["Valor"], "Tiempo_fin": (tiempo_ini + timedelta(days=1)).replace(hour=0, minute=0, second=1), "Tiempo_diff": ((tiempo_ini + timedelta(days=1)).replace(hour=0, minute=0, second=0) - tiempo_ini), "Cambio": True } nuevos_registros.append(nuevo_registro) tiempo_diff = tiempo_diff_original - tiempo_diff tiempo_ini = (tiempo_ini + timedelta(days=1)).replace(hour=0, minute=0, second=0) tiempo_fin = tiempo_fin_original else: nuevo_registro = { "Tiempo_ini": row['Tiempo_ini'], "Valor": row["Valor"], "Tiempo_fin": row['Tiempo_fin'], "Tiempo_diff": row['Tiempo_diff'], "Cambio": False } nuevos_registros.append(nuevo_registro) df_mod = pd.DataFrame(nuevos_registros) df_mod[df_mod['Cambio'] == True].head()
错误输出
| Tiempo_ini | Valor | Tiempo_fin | Tiempo_diff | Cambio |
|---|---|---|---|---|
| 2018-01-02 21:51:47 | 1 | 2018-01-03 00:00:01 | 0 days 02:08:13 | True |
| 2018-01-03 00:00:00 | 1 | 2018-01-04 00:00:01 | 1 days 00:00:00 | True |
| 2018-01-03 22:11:17 | 1 | 2018-01-04 00:00:01 | 0 days 01:48:43 | True |
| 2018-01-04 00:00:00 | 1 | 2018-01-05 00:00:01 | 1 days 00:00:00 | True |
| 2018-01-04 22:22:17 | 1 | 2018-01-05 00:00:01 | 0 days 01:37:43 | True |
解决方案
修正后的代码逻辑为:对每一行,逐段生成从当前起始时间到当日结束或原始结束时间的区间,直到覆盖整个原始时间范围:
import pandas as pd from datetime import timedelta, datetime def split_cross_day_rows(row): if not row['Cambio']: return [row.to_dict()] start = row['Tiempo_ini'] end = row['Tiempo_fin'] valor = row['Valor'] cambio = row['Cambio'] split_rows = [] current_start = start while current_start < end: # 计算当日结束时间(次日0点) day_end = current_start.replace(hour=0, minute=0, second=0) + timedelta(days=1) # 取当日结束和原始结束的较小值作为实际结束时间 actual_end = min(day_end, end) # 计算当前区间的时间差 diff = actual_end - current_start split_rows.append({ 'Tiempo_ini': current_start, 'Valor': valor, 'Tiempo_fin': actual_end, 'Tiempo_diff': diff, 'Cambio': cambio }) # 更新起始时间为当前区间的结束时间 current_start = actual_end return split_rows # 应用拆分函数并展平结果 nuevos_registros = [] for _, row in df.iterrows(): nuevos_registros.extend(split_cross_day_rows(row)) df_mod = pd.DataFrame(nuevos_registros) print(df_mod[df_mod['Cambio'] == True])
正确输出
| Tiempo_ini | Valor | Tiempo_fin | Tiempo_diff | Cambio |
|---|---|---|---|---|
| 2018-01-02 21:51:47 | 1 | 2018-01-03 00:00:00 | 0 days 02:08:13 | True |
| 2018-01-03 00:00:00 | 1 | 2018-01-03 10:38:17 | 0 days 10:38:17 | True |
| 2018-01-03 22:11:17 | 1 | 2018-01-04 00:00:00 | 0 days 01:48:43 | True |
| 2018-01-04 00:00:00 | 1 | 2018-01-04 05:57:17 | 0 days 05:57:17 | True |
| 2018-01-04 22:22:17 | 1 | 2018-01-05 00:00:00 | 0 days 01:37:43 | True |
| 2018-01-05 00:00:00 | 1 | 2018-01-05 07:00:17 | 0 days 07:00:17 | True |
| 2018-01-05 07:01:17 | 1 | 2018-01-06 00:00:00 | 0 days 16:58:43 | True |
| 2018-01-06 00:00:00 | 1 | 2018-01-06 20:02:47 | 0 days 20:02:47 | True |
| 2018-01-06 20:30:17 | 1 | 2018-01-07 00:00:00 | 0 days 03:29:43 | True |
| 2018-01-07 00:00:00 | 1 | 2018-01-07 00:57:17 | 0 days 00:57:17 | True |
内容的提问来源于stack exchange,提问作者jmateu888
相关产品推荐
相关产品推荐

