pandas 中按重复 id 的付款日期创建新列
支付老李
结论
可以用 pivot 将 payment_count 的值转成列名,并把对应的 payment_date 填入新列。转换后,每个 id 只占一行,也不需要单独计算 max('payment_count')。
result = (
df.pivot(index='id', columns='payment_count', values='payment_date')
.add_prefix('payment_date_')
.reset_index()
.rename_axis(columns=None)
)
结果如下:
id payment_date_1 payment_date_2
0 1 2/2/2020 4/6/2020
1 2 3/20/2020 3/29/2020
2 3 5/1/2020 5/30/2020
完整示例
创建原始 DataFrame 时,payment_count 需要写成列表:
import pandas as pd
df = pd.DataFrame({
'id': [1, 1, 2, 2, 3, 3],
'payment_count': [1, 2, 1, 2, 1, 2],
'payment_date': [
'2/2/2020',
'4/6/2020',
'3/20/2020',
'3/29/2020',
'5/1/2020',
'5/30/2020'
]
})
result = (
df.pivot(
index='id',
columns='payment_count',
values='payment_date'
)
.add_prefix('payment_date_')
.reset_index()
.rename_axis(columns=None)
)
print(result)
各项操作的作用如下:
index='id':让每个id在结果中只占一行。columns='payment_count':将付款序号1、2等转成列。values='payment_date':用付款日期填充新列。add_prefix('payment_date_'):生成payment_date_1、payment_date_2等列名。reset_index():将id从索引恢复为普通列。
列数会随最大的 payment_count 自动增加。例如,数据中出现 payment_count=3 时,结果会自动新增 payment_date_3。
日期类型的处理
如果后续需要按日期排序、比较日期或计算时间间隔,最好先把字符串转换为日期类型:
df['payment_date'] = pd.to_datetime(
df['payment_date'],
format='%m/%d/%Y'
)
转换后执行相同的 pivot 操作即可。如果需要按原格式输出,可以再进行格式化:
date_columns = result.columns.drop('id')
result[date_columns] = result[date_columns].apply(
lambda col: col.dt.strftime('%-m/%-d/%Y')
)
部分 Windows 环境不支持 %-m 和 %-d。如果代码需要跨平台运行,可以保留标准的 datetime64 类型,或者使用 %m/%d/%Y,以带前导零的格式输出日期。
存在重复记录时
pivot 要求每个 (id, payment_count) 组合最多只有一条记录。如果同一组合对应多个付款日期,执行时会抛出重复索引错误。
遇到这种情况,需要先确定记录的选择规则,再改用 pivot_table。例如,保留最晚的付款日期:
df['payment_date'] = pd.to_datetime(
df['payment_date'],
format='%m/%d/%Y'
)
result = (
df.pivot_table(
index='id',
columns='payment_count',
values='payment_date',
aggfunc='max'
)
.add_prefix('payment_date_')
.reset_index()
.rename_axis(columns=None)
)
这里的 max 比较的是付款日期,不是 payment_count。如果每个 (id, payment_count) 组合原本就是唯一的,优先使用含义更直接的 pivot。
备注:内容仅供参考。
