You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何按ID计算DataFrame中各值在指定日期的累计存续天数?

问题描述

我有如下DataFrame:

id      value       Date       value_change
0   100101  AAAAAAA  01/01/2022        True
1   100101  BBBBBBB  02/01/2022        True
2   100101  BBBBBBB  03/01/2022       False 
3   100101  BBBBBBB  04/01/2022       False
4   100101  BBBBBBB  05/01/2022       False
5   100101  BBBBBBB  06/01/2022       False
6   100101  AAAAAAA  07/01/2022        True
7   100101  CCCCCCC  08/01/2022        True
8   100102  BBBBBBB  09/01/2022        True
9   100102  BBBBBBB  10/01/2022       False
10  100102  BBBBBBB  11/01/2022       False
11  100102  BBBBBBB  12/01/2022       False
12  100102  BBBBBBB  13/01/2022       False
13  100102  BBBBBBB  14/01/2022       False
14  100102  AAAAAAA  15/01/2022        True

需要针对每个id,计算任意给定日期下每个value被分配的累计天数。例如id=100101在2022年1月7日时,AAAAAA累计2天,BBBBBB累计5天。

之前尝试用计算Min(Date)的方法实现,但出现错误:比如AAAAAA在2022年1月1日的结果正确(1天),但1月2日被错误算成2天、1月3日算成3天,显然不符合实际。

解决方案

1. 转换日期格式并划分value生效周期

首先将Date列转为datetime类型,方便后续日期计算;然后利用value_change字段标记每个value的生效周期——每次value_change=True代表切换到新value,以此为依据给每个周期编号。

import pandas as pd

# 转换日期格式
df['Date'] = pd.to_datetime(df['Date'], format='%d/%m/%Y')
# 按id分组,给每个value的生效周期编序号
df['period'] = df.groupby('id')['value_change'].cumsum()

2. 确定每个周期的起止日期

每个周期的起始日期是该周期的最早日期,结束日期则是下一个周期的起始日期减1天(如果是最后一个周期,就用当前周期的最晚日期),以此明确每个value的生效时间段。

# 提取每个周期的基础起止日期
period_info = df.groupby(['id', 'period']).agg(
    start_date=('Date', 'min'),
    end_date=('Date', 'max')
).reset_index()
# 获取下一个周期的起始日期,用于修正当前周期的结束日
period_info['next_start'] = period_info.groupby('id')['start_date'].shift(-1)
# 处理最后一个周期的结束日(无后续周期时保留原end_date)
period_info['end_date'] = period_info.apply(
    lambda x: x['next_start'] - pd.Timedelta(days=1) if pd.notna(x['next_start']) else x['end_date'],
    axis=1
)
# 关联周期对应的value
period_info = period_info.merge(df[['id', 'period', 'value']].drop_duplicates(), on=['id', 'period'])

3. 编写函数计算指定日期的累计天数

编写函数,输入目标id和日期,即可算出该id下每个value到目标日期的累计生效天数:

def get_value_total_days(target_id, target_date):
    # 筛选当前id的所有周期数据
    id_data = period_info[period_info['id'] == target_id].copy()
    # 计算每个周期的有效天数:
    # 若周期起始日晚于目标日期,贡献0天;否则取周期结束日与目标日期的较小值,减去起始日再加1(包含首尾日期)
    id_data['valid_days'] = id_data.apply(
        lambda x: (min(x['end_date'], target_date) - x['start_date']).days + 1 
        if x['start_date'] <= target_date else 0,
        axis=1
    )
    # 按value分组求和,得到累计天数
    result = id_data.groupby('value')['valid_days'].sum().reset_index()
    return result

# 测试示例:id=100101,日期2022-01-07
test_result = get_value_total_days(100101, pd.to_datetime('2022-01-07'))
print(test_result)

输出结果符合需求:

value  valid_days
0  AAAAAAA           2
1  BBBBBBB           5

4. 批量计算所有日期的结果(可选)

如果需要给原DataFrame的每一行都加上对应日期的各value累计天数,可通过apply批量处理:

# 给每行计算对应日期的各value天数
def add_days_to_row(row):
    days_dict = get_value_total_days(row['id'], row['Date']).set_index('value')['valid_days'].to_dict()
    # 补全所有出现过的value,未出现的填0
    for val in df['value'].unique():
        days_dict.setdefault(val, 0)
    return pd.Series(days_dict)

# 合并结果到原DataFrame
final_df = pd.concat([df, df.apply(add_days_to_row, axis=1)], axis=1)
# 查看关键列
print(final_df[['id', 'Date', 'value', 'AAAAAAA', 'BBBBBBB', 'CCCCCCC']])

这种方法解决了之前用Min(Date)的错误——它会忽略value的切换节点,错误地把所有日期都算成最早日期到当前日期的天数,而本方案只统计每个value在自身生效周期内的天数。

内容的提问来源于stack exchange,提问作者bagardegeo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 20:16:05