От TimeDelta до плавающих дней в Pandas

У меня есть столбец TimeDelta со значениями, которые выглядят так:

2 дня 21:54:00.000000000

Я хотел бы иметь число с плавающей запятой, представляющее количество дней, скажем, здесь 2 + 21/24 = 2,875, без учета минут. Есть ли простой способ сделать это? Я видел ответ, предлагающий

res['Ecart_lacher_collecte'].apply(lambda x: float(x.item().days+x.item().hours/24.))

Но я получаю «AttributeError: объект« str »не имеет атрибута« элемент »»

Версия Numpy — «1.10.4», версия Pandas — u «0.17.1».

Первоначально столбцы были получены с помощью:

lac['DateHeureLacher'] = pd.to_datetime(lac['Date lacher']+' '+lac['Heure lacher'],format='%d/%m/%Y %H:%M:%S')
cap['DateCollecte'] = pd.to_datetime(cap['Date de collecte']+' '+cap['Heure de collecte'],format='%d/%m/%Y %H:%M:%S')

в первом скрипте. Затем во втором:

res = pd.merge(lac, cap, how='inner', on=['Loc'])
res['DateHeureLacher']  = pd.to_datetime(res['DateHeureLacher'],format='%Y-%m-%d %H:%M:%S')
res['DateCollecte']  = pd.to_datetime(res['DateCollecte'],format='%Y-%m-%d %H:%M:%S')
res['Ecart_lacher_collecte'] = res['DateCollecte'] - res['DateHeureLacher']

Может быть, сохранив его в csv, верните их типы обратно в строку? Преобразование, которое я пытаюсь сделать, находится в третьем сценарии.

Sexe_x  PiegeLacher latL    longL   Loc Col_x   DateHeureLacher Nb envolees PiegeCapture    latC    longC   Col_y   Sexe_y  Effectif    DateCollecte    DatePose    Ecart_lacher_collecte   Dist_m
M   Q0-002  1629238 237877  H   Rouge   2011-02-04 17:15:00 928 Q0-002  1629238 237877  Rouge   M   1   2011-02-07 15:09:00 2011-02-07 12:14:00 2 days 21:54:00.000000000   0
M   Q0-002  1629238 237877  H   Rouge   2011-02-04 17:15:00 928 Q0-002  1629238 237877  Rouge   M   4   2011-02-07 12:14:00 2011-02-07 09:42:00 2 days 18:59:00.000000000   0
M   Q0-002  1629238 237877  H   Rouge   2011-02-04 17:15:00 928 Q0-003  1629244 237950  Rouge   M   1   2011-02-07 15:10:00 2011-02-07 12:16:00 2 days 21:55:00.000000000   75

рез.информация():

Sexe_x                   922 non-null object
PiegeLacher              922 non-null object
latL                     922 non-null int64
longL                    922 non-null int64
Loc                      922 non-null object
Col_x                    922 non-null object
DateHeureLacher          922 non-null object
Nb envolees              922 non-null int64
PiegeCapture             922 non-null object
latC                     922 non-null int64
longC                    922 non-null int64
Col_y                    922 non-null object
Sexe_y                   922 non-null object
Effectif                 922 non-null int64
DateCollecte             922 non-null object
DatePose                 922 non-null object
Ecart_lacher_collecte    922 non-null object
Dist_m                   922 non-null int64

person alpagarou    schedule 19.02.2016    source источник
comment
Я думаю, вы могли бы использовать float(res['columnName'].dt.days + res['columnName'].dt.hours / 24)   -  person Anton Protopopov    schedule 19.02.2016
comment
IIUC вы можете сделать res['columnname'].dt.total_seconds()/ (24 * 60 * 60)   -  person EdChum    schedule 19.02.2016
comment
@AntonProtopopov Я получаю сообщение об ошибке AttributeError: можно использовать только метод доступа .dt со значениями, подобными дате и времени.   -  person alpagarou    schedule 19.02.2016
comment
@EdChum Я получаю AttributeError: объект «Серия» не имеет атрибута «total_seconds»   -  person alpagarou    schedule 19.02.2016
comment
@alpagarou сначала вам нужно преобразовать столбцы в объект datetime. Вы могли бы сделать это с pd.to_datetime   -  person Anton Protopopov    schedule 19.02.2016
comment
Какая у вас версия pandas и можете ли вы опубликовать необработанные данные, код и свою версию numpy и отредактировать это в своем вопросе   -  person EdChum    schedule 19.02.2016
comment
@AntonProtopopov pd.to_datetime вызывает ValueError: неизвестный формат строки   -  person alpagarou    schedule 19.02.2016
comment
@alpagarou напишите дату, пожалуйста. Без этого трудно сказать, что может вызвать проблему   -  person Anton Protopopov    schedule 19.02.2016
comment
Вы говорите, что ваш столбец представляет собой строку временных дельт?   -  person EdChum    schedule 19.02.2016
comment
опубликовать вывод из res.info(), также работает ли res['Ecart_lacher_collecte'].dt.total_seconds()/ (24 * 60 * 60)?   -  person EdChum    schedule 19.02.2016
comment
@EdChum Сначала я получаю AttributeError: можно использовать только метод доступа .dt со значениями, подобными дате и времени, но когда я пытаюсь использовать res['Ecart_lacher_collecte'] = pd.to_datetime(res['Ecart_lacher_collecte']), я получаю ValueError: неизвестный формат строки   -  person alpagarou    schedule 19.02.2016
comment
Похоже, у вас есть недопустимые значения в ваших столбцах, можете ли вы попробовать pd.to_datetime(res['DateHeureLacher'],format='%Y-%m-%d %H:%M:%S', errors='coerce'), также вам нужно опубликовать минимальные данные и код, который мы можем запустить, который воспроизводит вашу ошибку, поскольку это должно работать, также когда вы читаете csv и устанавливаете parse_dates=['DateHeureLacher', 'DateCollecte'] в read_csv делает исправить dtypes?   -  person EdChum    schedule 19.02.2016
comment
parse_dates исправляет тип «DateHeureLacher» и «DateCollecte», но «Ecart_lacher_collecte», который был timedelta64[ns] в конце прецедентного скрипта, возвращается к объекту, когда я читаю его из csv (я также пытался включить его в parse_dates но это не работает) Вероятно, мне следует использовать error='coerce' для преобразования 'Ecart_lacher_collecte в datetime перед использованием dt.days, но какой формат мне указать?   -  person alpagarou    schedule 19.02.2016
comment
ОК, я только что попробовал это и обнаружил, что вы не можете указать dtype в read_csv, чтобы проанализировать его обратно как timedelta64, поэтому у вас есть пара вариантов: преобразовать timedelta64 в int64 с помощью astype, а затем при чтении обратно использовать пользовательский конвертер чтобы преобразовать его или после прочтения обратно, вы можете сделать astype(np.timedelta64), и это сработает   -  person EdChum    schedule 19.02.2016
comment
Я поместил свои последние два сценария в один и справился с этим. Однако я получаю много сообщений SettingWithCopyWarning ?   -  person alpagarou    schedule 19.02.2016


Ответы (3)


Вы можете использовать pd.to_timedelta или np.timedelta64 для определения продолжительность и разделить на это:

# set up as per @EdChum
df['total_days_td'] = df['time_delta'] / pd.to_timedelta(1, unit='D')
df['total_days_td'] = df['time_delta'] / np.timedelta64(1, 'D')
person jpp    schedule 05.02.2019
comment
Вы также можете использовать модуль datetime, например, datetime.timedelta(days=1) - person MarMat; 22.06.2020

Вы можете использовать dt.total_seconds и разделить это по общему количеству секунд в сутках, например:

In [25]:
df = pd.DataFrame({'dates':pd.date_range(dt.datetime(2016,1,1, 12,15,3), periods=10)})
df

Out[25]:
                dates
0 2016-01-01 12:15:03
1 2016-01-02 12:15:03
2 2016-01-03 12:15:03
3 2016-01-04 12:15:03
4 2016-01-05 12:15:03
5 2016-01-06 12:15:03
6 2016-01-07 12:15:03
7 2016-01-08 12:15:03
8 2016-01-09 12:15:03
9 2016-01-10 12:15:03

In [26]:
df['time_delta'] = df['dates'] - pd.datetime(2015,11,6,8,10)
df

Out[26]:
                dates       time_delta
0 2016-01-01 12:15:03 56 days 04:05:03
1 2016-01-02 12:15:03 57 days 04:05:03
2 2016-01-03 12:15:03 58 days 04:05:03
3 2016-01-04 12:15:03 59 days 04:05:03
4 2016-01-05 12:15:03 60 days 04:05:03
5 2016-01-06 12:15:03 61 days 04:05:03
6 2016-01-07 12:15:03 62 days 04:05:03
7 2016-01-08 12:15:03 63 days 04:05:03
8 2016-01-09 12:15:03 64 days 04:05:03
9 2016-01-10 12:15:03 65 days 04:05:03

In [27]:
df['total_days_td'] = df['time_delta'].dt.total_seconds() / (24 * 60 * 60)
df

Out[27]:
                dates       time_delta  total_days_td
0 2016-01-01 12:15:03 56 days 04:05:03      56.170174
1 2016-01-02 12:15:03 57 days 04:05:03      57.170174
2 2016-01-03 12:15:03 58 days 04:05:03      58.170174
3 2016-01-04 12:15:03 59 days 04:05:03      59.170174
4 2016-01-05 12:15:03 60 days 04:05:03      60.170174
5 2016-01-06 12:15:03 61 days 04:05:03      61.170174
6 2016-01-07 12:15:03 62 days 04:05:03      62.170174
7 2016-01-08 12:15:03 63 days 04:05:03      63.170174
8 2016-01-09 12:15:03 64 days 04:05:03      64.170174
9 2016-01-10 12:15:03 65 days 04:05:03      65.170174
person EdChum    schedule 19.02.2016
comment
да, это работает, но я в шоке, что у них нет доступных dt.total_days() или dt.total_hours... - person Ledger Yu; 06.01.2017
comment
@LedgerYu, потому что это не стандартный метод для timedelta, кроме того, это тривиально вычислить самостоятельно, но, кроме того, если бы мы следовали вашей логике, у нас были бы методы для total_days, total_hours, total_quarters, total_years, total_milliseconds и т. д. - person EdChum; 06.01.2017
comment
Вы можете просто сделать dt.days, а не как функцию. Работает на меня! - person Guillochon; 05.07.2018
comment
@Guillochon dt.days даст вам только количество дней и выкинет всю информацию о времени - person Taylor; 06.09.2018

Вы пытались использовать это вместо этого?

res['Ecart_lacher_collecte'].apply(lambda x: (x.total_seconds()//(3600*24)) + (x.total_seconds()%(3600*24)//3600)/24))

Первый термин - это день (2 в вашем случае). Второй термин - это соотношение часов без учета минут (21/24 в вашем случае).

Если вы не хотите, чтобы данные минут и секунд игнорировались, и вам нужно соотношение, которое учитывает все секунды в дне, код приведен ниже:

res['Ecart_lacher_collecte'].apply(lambda x: (x.total_seconds()/(3600*24))
person sharinganSawant    schedule 22.10.2016
comment
Зачем отделять дивизию, а потом вот так присоединяться к ней? Единственное отличие от x.total_seconds()/(3600*24) заключается в том, что неполные часы вообще не учитываются, что, как я сомневаюсь, является желаемым поведением. - person Elias Hasle; 18.10.2018
comment
Да все верно. неполные часы не учитываются. Это потому, что первоначальный постер так хотел: «Я хотел бы иметь число с плавающей запятой, представляющее количество дней, скажем, здесь 2 + 21/24 = 2,875, без учета минут». - person sharinganSawant; 19.10.2018
comment
@EliasHasle Я отредактировал ответ, в котором упомянул, почему не учитывались неполные часы. - person sharinganSawant; 19.10.2018