Вычисление возраста из to_timedelta странно, и DateOffset не масштабируется в серии - PullRequest
3 голосов
/ 30 сентября 2019

У меня есть два столбца:

          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'>

Я могу использовать apply во втором варианте, df['date'] - df['age'].apply(lambda a: pd.DateOffset(years=a)), чтобы получить правильный результат, и (по понятным причинам) PerformanceWarning: Adding/subtracting array of DateOffsets to DatetimeArray not vectorized.

Какое здесь хорошее (питоническое и векторизованное) решение?

1 Ответ

1 голос
/ 30 сентября 2019

Это может сэкономить время для циклического перехода по уникальным возрастам вместо строк, что, вероятно, имеет место с реалистичными значениями для целых возрастов и очень длинным DataFrame.

pd.concat([gp.assign(dob = gp.date - pd.offsets.DateOffset(years=idx))
           for idx, 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

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: pd.concat([gp.assign(dob = gp.date - pd.offsets.DateOffset(years=idx)) 
                              for idx, gp in df.groupby('age', sort=False)]),
        lambda df: df.apply(lambda x: x['date'] - pd.DateOffset(years=int(x['age'])), axis=1),
    ],
    labels=["groupby", "apply"],
    n_range=[2 ** k for k in range(15)],
    equality_check=None,  # Because datetime type
    xlabel="len(df)"
)

enter image description here

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...