python - 从 to_timedelta 计算年龄很奇怪,并且 DateOffset 不可扩展到 Series

标签 python pandas

我有两列:

          date   age
0   2016-01-05  47.0
1   2016-01-05  43.0
2   2016-01-05  28.0
3   2016-01-05  46.0
4   2016-01-04  39.0

我想要的是另一列日期和年龄之间的差异:

          date   age           dob
0   2016-01-05  47.0    1969-01-05
1   2016-01-05  43.0    1973-01-05
2   2016-01-05  28.0    1988-01-05
3   2016-01-05  46.0    1970-01-05
4   2016-01-04  39.0    1977-01-04

看起来很简单,但是简单的 df['date'] - df['age'].astype('timedelta64[Y]') 给出:

0   1969-01-04 14:27:36
1   1973-01-04 13:44:24
2   1988-01-05 05:02:24
3   1970-01-04 20:16:48
4   1977-01-03 13:01:12

为什么要附加时间戳?甚至 pd.to_timedelta(df['age'], unit='Y') 也给出了相同的结果,并附加警告 unit='Y' 已弃用。

此外,df['date'] - pd.DateOffset(years=df['age']) 抛出(可以理解):

TypeError: cannot convert the series to <class 'int'>

我可以在第二个选项中使用 applydf['date'] - df['age'].apply(lambda a: pd.DateOffset(years=a)) ,以迂回地获得正确的结果,并且(可以理解)PerformanceWarning: Adding/subtracting array of DateOffsets to DatetimeArray not vectorized

什么是好的(pythonic 和矢量化)解决方案?

最佳答案

如果您需要为每一行指定不同的非标准偏移量(即月或年),它可以节省时间遍历唯一偏移量而不是行。使用 groupby 完成此操作。

当唯一偏移量的数量 << DataFrame 中的行数时,尤其如此。对于整数年龄和非常长的 DataFrame 的实际值,很可能就是这种情况。

pd.concat([gp.assign(dob = gp.date - pd.offsets.DateOffset(years=age))
           for age, gp in df.groupby('age', sort=False)])

        date   age        dob
0 2016-01-05  47.0 1969-01-05
1 2016-01-05  43.0 1973-01-05
2 2016-01-05  28.0 1988-01-05
3 2016-01-05  46.0 1970-01-05
4 2016-01-04  39.0 1977-01-04

一些时间:

import perfplot
import pandas as pd
import numpy as np


def with_groupby(df):
    s = pd.concat([gp.date - pd.offsets.DateOffset(years=idx)
                   for idx, gp in df.groupby('age', sort=False)])
    return s
    
def with_apply(df):
    s = df.apply(lambda x: x['date'] - pd.DateOffset(years=int(x['age'])), axis=1)
    return s
    
    
perfplot.show(
    setup=lambda n: pd.DataFrame({'date': np.random.choice(pd.date_range('1980-01-01', 
                                                                         freq='50D', periods=100), n),
                                  'age': np.random.choice(range(100), n)}), 
    kernels=[lambda df: with_groupby(df),
             lambda df: with_apply(df)],
    labels=["groupby", "apply"],
    n_range=[2 ** k for k in range(1, 20)],
    equality_check=lambda x,y: x.sort_index().compare(y.sort_index()).empty,
    xlabel='len(df)'
)

enter image description here

关于python - 从 to_timedelta 计算年龄很奇怪,并且 DateOffset 不可扩展到 Series,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58174267/

相关文章:

Python 类 : Why can't I use the method len() inside __eq__(self, 其他)?

python - 将纬度/经度分配给邮政编码

python - 'patch' 方法在收到 URL 中的 pk 时生成 500 错误

python - Pandas map lambda 函数的不区分大小写的字典

python - 将缺失的列添加到其他数据框中的数据框中

python - 基于列在数据框中添加或减去两列?

python - 为什么要继承对象类型

python - 遵循算法的冒泡排序

python - 尝试使用 sqlalchemy 插入 pandas 时出现错误 fetchall

python - 将每日数据按月份分组并计算每个用户的对象数