Результат объекта MonthEnd - ‹11 * MonthEnds› вместо числа

В моем фреймворке pandas я хочу найти разницу между датами в месяцах. Функция .dt.to_period('M') возвращает объект MonthEnd, например <11 * MonthEnds>, вместо номера месяца.

Я попытался изменить тип столбца с помощью pd.to_numeric() и удалить буквы с помощью re.sub("[^0-9]", "", 'blablabla123bla'). Оба не работают с объектом MonthEnd.

df['duration_dataset'] = df['date_1'].dt.to_period('M') - df['date_2'].dt.to_period('M')

Я ожидал 11, но результат был <11 * MonthEnds>.

Вот минимальный фрейм данных

d = {'date_1': ['2018-03-31','2018-09-30'], 'date_2': ['2017-12-31','2017-12-31']}
df = pd.DataFrame(data=d)

df['date_1'] = pd.to_datetime(df['date_1'], format='%Y-%m-%d')
df['date_2'] = pd.to_datetime(df['date_2'], format='%Y-%m-%d')

df['duration_dataset'] = df['date_1'].dt.to_period('M') - df['date_2'].dt.to_period('M')

df

person Inge    schedule 31.01.2019    source источник
comment
Я нахожу такое поведение, панды 0.24.0   -  person ALollz    schedule 31.01.2019
comment
Похоже, у вас есть _ 1_ объекты, поэтому это смещение даты.   -  person Martijn Pieters    schedule 31.01.2019
comment
Не могли бы вы создать минимальный фрейм данных, воспроизводящий проблему? Включите код для создания кадра с несколькими строками, чтобы показать, как создаются экземпляры MonthEnd, чтобы мы могли помочь исправить это и вместо этого получить целочисленное количество месяцев.   -  person Martijn Pieters    schedule 31.01.2019
comment
Ах, я тестировал Pandas 0.23.4, сейчас обновляюсь.   -  person Martijn Pieters    schedule 31.01.2019
comment
У меня Панды версии 0.24.0   -  person Inge    schedule 31.01.2019
comment
Вы можете попытаться получить доступ к объекту MonthEnd, используя <object>.__dict__['n']   -  person alphazeta    schedule 30.03.2020


Ответы (2)


Это новое поведение в Pandas 0.24, где вычитание Period() объектов дает DateOffset подкласс.

Вы можете получить числовое значение из атрибута DateOffset.n:

from operator import attrgetter

df['duration_dataset'] = (
    df['date_1'].dt.to_period('M') -
    df['date_2'].dt.to_period('M')).apply(attrgetter('n'))

Это производит

      date_1     date_2  duration_dataset
0 2018-03-31 2017-12-31                 3
1 2018-09-30 2017-12-31                 9

для вашего образца фрейма данных.

Вместо того, чтобы преобразовывать ваши даты в периоды, вы могли бы вместо этого преобразовать их в количество месяцев, начиная с года 0, а затем вычесть эти числа:

df['duration_dataset'] = (
    df['date_1'].dt.year * 12 + df['date_1'].dt.month - 1 -
    (df['date_2'].dt.year * 12 + df['date_2'].dt.month - 1)
)

который можно упростить до

df['duration_dataset'] = (
    12 * (df['date_1'].dt.year - df['date_2'].dt.year) +
    df['date_1'].dt.month - df['date_2'].dt.month
)
person Martijn Pieters    schedule 31.01.2019
comment
Я потратил около четырех часов своей жизни, пытаясь понять это. Большое вам спасибо! - person Sean McCarthy; 25.04.2019
comment
Нет необходимости вычитать на 1, когда вы делаете df['duration_dataset'] = ( df['date_1'].dt.year * 12 + df['date_1'].dt.month - 1 - (df['date_2'].dt.year * 12 + df['date_2'].dt.month - 1) ), верно? - person titusAdam; 24.03.2020
comment
@titusAdam: здесь при прямом вычитании -1 с обеих сторон уравнения можно исключить, да: df['date_1'].dt.year * 12 + df['date_1'].dt.month - df['date_2'].dt.year * 12 - df['date_2'].dt.month. - person Martijn Pieters; 24.03.2020

Вы также можете принуждать Period к int с помощью astype('int') и напрямую учитывать разницу:

df['duration_dataset'] = df['date_1'].dt.to_period('M').astype('int') - df['date_2'].dt.to_period('M').astype('int')

Вроде бы быстрее.

person L. Francis Cong    schedule 29.04.2020