DataFrame pandalarında tarihler arasındaki günlerin sayısını içeren sütun ekleyin


101

"A" daki tarihleri ​​"B" deki tarihlerden çıkarmak ve farkı olan yeni bir sütun eklemek istiyorum.

df
          A        B
one 2014-01-01  2014-02-28 
two 2014-02-03  2014-03-01

Aşağıdakileri denedim, ancak bunu bir for döngüsüne dahil etmeye çalıştığımda bir hata alıyorum ...

import datetime
date1=df['A'][0]
date2=df['B'][0]
mdate1 = datetime.datetime.strptime(date1, "%Y-%m-%d").date()
rdate1 = datetime.datetime.strptime(date2, "%Y-%m-%d").date()
delta =  (mdate1 - rdate1).days
print delta

Ne yapmalıyım?

Yanıtlar:


100

Bunların tarih saat sütunları olduğunu varsayarsak (geçerli değillerse to_datetime) bunları çıkarmanız yeterlidir:

df['A'] = pd.to_datetime(df['A'])
df['B'] = pd.to_datetime(df['B'])

In [11]: df.dtypes  # if already datetime64 you don't need to use to_datetime
Out[11]:
A    datetime64[ns]
B    datetime64[ns]
dtype: object

In [12]: df['A'] - df['B']
Out[12]:
one   -58 days
two   -26 days
dtype: timedelta64[ns]

In [13]: df['C'] = df['A'] - df['B']

In [14]: df
Out[14]:
             A          B        C
one 2014-01-01 2014-02-28 -58 days
two 2014-02-03 2014-03-01 -26 days

Not: Yeni bir panda kullandığınızdan emin olun (ör. 0.13.1), bu eski sürümlerde çalışmayabilir.


24
Sonuçtaki "günler" kısmından kurtulabilir miyiz, sadece sayısal değeri görmemiz gerekiyor, yani. Bu durumda -58, -26.
0nir

6
@AndyHayden yorumunu genişletmek için, bu işe yarıyor ama olmalı pd.offsets.Day(1)('s' ile). Ben de genellikle (df['A'] - df['B']) / pd.offsets.Day(-1)
reddediyorum

12
Ancak, bunu bütün bir Dizi üzerinde yapmak istiyorsanız, (df['A'] - df['B']) / np.timedelta64(-1, 'D')tam olarak anlamadığım nedenlere ihtiyacınız var .
dirkjot

@dirkjot Yazım hatasını tespit ettiğiniz için teşekkürler! IIRC bu son pandalarda düzeltildi, 0.16.2 / 0.17 mi kullanıyorsunuz?
Andy Hayden

2
@webelo DatetimeIndex / Series'in kendisinin .dt.dayskesinlikle tercih edilmesi gereken bir özniteliği olmalıdır.
Andy Hayden

110

'Günler' metin öğesini kaldırmak için, seri için dt () erişimcisini de kullanabilirsiniz: https://pandas.pydata.org/pandas-docs/stable/generated/pandas.Series.dt.html

Yani,

df[['A','B']] = df[['A','B']].apply(pd.to_datetime) #if conversion required
df['C'] = (df['B'] - df['A']).dt.days

döndüren:

             A          B   C
one 2014-01-01 2014-02-28  58
two 2014-02-03 2014-03-01  26

3
Mükemmel cevap. Benim durumumda df['C'] = (df['B'] - df['A']).dt.daysişe yaramadı ve kullanmak zorunda kaldım df['C'] = (df['B'] - df['A']).days. Benimkinin neden beklendiği gibi gün sayısını vermediğine dair bir fikriniz var mı?
Samuel Nde

Nde - tam olarak nasıl çalışmadı? Hata mı yoksa yanlış değerler mi? Hem A hem de B sütunlarını tarih saatine başarıyla dönüştürdünüz mü?
Ricky McMaster

1
Her iki sütunum da tarih saattir (veya datetime64[ns]kesin olmak gerekirse). Bunu yaptığımda df['C'] = (df['B'] - df['A']).dt.days, AttributeError: 'Timedelta' nesnesinin 'dt' özniteliği olmadığını belirten bir öznitelik hatası aldım , bu yüzden df ['C'] = (df ['B'] - df ['A']) denedim . bana istenen cevabı veren günler . (Elbette yukarıdaki örnekte değil kendi veri çerçevemi kullanıyorum. Ya da tarihimde zamanım olduğu için değil de olabilir 2018-09-24 10:17:18.800277)
Samuel Nde

1
mükemmel cevap.
user3065757

1
Harika çözüm. Teşekkürler!
Rodrigo Hjort

11

Bunu yapmanın en Pythonic (ve en hızlı) yolu için en iyi seçeneğiniz liste kavrayışıdır:

[int(i.days) for i in (df.B - df.A)]
  1. timedelta'yı iade edeceğim (ör. '-58 gün')
  2. i.days bu değeri uzun bir tam sayı değeri olarak döndürecektir (örneğin -58L)
  3. int (günler) aradığınız -58'i size verecektir.

Sütunlarınız tarih saat biçiminde değilse. Daha kısa sözdizimi şöyle olacaktır:df.A = pd.to_datetime(df.A)


1

Buna ne dersin:

times['days_since'] = max(list(df.index.values))  
times['days_since'] = times['days_since'] - times['months']  
times
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.