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

如何为DataFrame中每个唯一ID标记sold状态前29天内的行

问题需求

给定如下结构的Pandas DataFrame:

id |    date   | status
________________________
...     ...      ...
1  |2020-01-01 | reserve
1  |2020-01-02 | sold
2  |2020-01-01 | free
3  |2020-01-03 | reserve
3  |2020-01-25 | signed
3  |2020-01-30 | sold
...     ...      ...
10 |2020-01-02 | signed
10 |2020-02-15 | sold 
...     ...      ....

需要实现:

  • 找到所有status为sold的行
  • 将每个sold行往前29天内的所有行标记为1,其余行标记为0
  • 最终得到带label列的DataFrame,示例如下:
id |    date   | status  | label
_________________________________
...     ...      ...        ...
1  |2020-01-01 | reserve | 1 
1  |2020-01-02 | sold    | 1
2  |2019-12-02 | free    | 0    # no sold status for 2
3  |2020-01-03 | reserve | 1
3  |2020-01-25 | signed  | 1
3  |2020-01-30 | sold    | 1
...     ...      ...        ...
10 |2020-01-02 | signed  | 0
10 |2020-02-15 | sold    | 1    # more than 29 days from 2020-02-15
...     ...      ....       ...

输入DataFrame构造代码:

d = {'id': [1, 1, 2, 3, 3, 3, 10, 10], 
     'date': ['2020-01-01', '2020-01-02', '2020-01-01', '2020-01-03', '2020-01-25', '2020-01-30', '2020-01-02', '2020-02-15'],
     'status': ['reserve', 'sold', 'free', 'reserve', 'signed', 'sold', 'signed', 'sold']
    }
df = pd.DataFrame(data=d)

原尝试代码存在错误,无法正确实现需求:

from datetime import timedelta
import pandas as pd

def make_labels(df):    
    
    def get_indices(df):
        return list(df[df['date'] >= df.iloc[-1]['date'] - timedelta(days=29)].index)

    df.sort_values(['id', 'date'], inplace=True)
    zero_labels = pd.Series(0, index = df.index, name='sold_labels')    
    one_lables = df.groupby('id')['status'].apply(lambda s: get_indices if s.iloc[-1] == 'sold').sum()
    zero_labels.loc[one_lables] = 1
    
    return zero_labels

df['label'] = make_labels(df)
问题分析

原代码的核心问题:

  • groupby('id')['status'].apply(...)中,lambda返回的是get_indices函数对象而非调用结果,且仅处理了每组最后一行是sold的情况,忽略了组内可能存在多个sold行的场景
  • 未将date列转换为datetime类型,直接进行日期运算会触发错误
  • 逻辑上只覆盖了每组最后一个sold行的29天范围,不符合"所有sold行往前29天内的行都标记为1"的需求
解决方案

正确实现步骤:

  1. 将date列转换为datetime类型,确保日期运算有效
  2. 按id分组,对每组内的行按日期排序
  3. 对每组,提取所有sold行的日期,判断每行日期是否落在任意一个sold日期的前29天到该sold日期的区间内
  4. 生成对应的label列

实现代码:

from datetime import timedelta
import pandas as pd

# 构造输入DataFrame
d = {'id': [1, 1, 2, 3, 3, 3, 10, 10], 
     'date': ['2020-01-01', '2020-01-02', '2020-01-01', '2020-01-03', '2020-01-25', '2020-01-30', '2020-01-02', '2020-02-15'],
     'status': ['reserve', 'sold', 'free', 'reserve', 'signed', 'sold', 'signed', 'sold']
    }
df = pd.DataFrame(data=d)

# 转换date列为datetime类型
df['date'] = pd.to_datetime(df['date'])

def assign_label(group):
    # 按日期排序组内数据
    group = group.sort_values('date').reset_index(drop=True)
    # 获取组内所有sold行的日期
    sold_dates = group[group['status'] == 'sold']['date']
    if sold_dates.empty:
        # 无sold行,全部标记0
        group['label'] = 0
        return group
    # 对每行判断是否在任意一个sold日期的前29天区间内
    group['label'] = group['date'].apply(
        lambda x: any((sold_date - timedelta(days=29) <= x) & (x <= sold_date) for sold_date in sold_dates)
    ).astype(int)
    return group

# 按id分组应用标签赋值逻辑
df = df.groupby('id').apply(assign_label).reset_index(drop=True)

print(df)
输出结果

运行后得到的DataFrame如下:

id       date   status  label
0   1 2020-01-01  reserve      1
1   1 2020-01-02     sold      1
2   2 2020-01-01     free      0
3   3 2020-01-03  reserve      1
4   3 2020-01-25   signed      1
5   3 2020-01-30     sold      1
6  10 2020-01-02   signed      0
7  10 2020-02-15     sold      1

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 22:09:22