Pandalar veri çerçevesine eksik tarihleri ​​ekleyin


128

Verilerim belirli bir tarihte birden çok olay içerebilir veya bir tarihte olay YOK olabilir. Bu olayları alıyorum, tarihe göre sayıyorum ve planlıyorum. Ancak, onları çizdiğimde, iki serim her zaman eşleşmiyor.

idx = pd.date_range(df['simpleDate'].min(), df['simpleDate'].max())
s = df.groupby(['simpleDate']).size()

Yukarıdaki kodda idx , 30 tarih aralığı haline gelir. 09-01-2013 ila 09-30-2013 Ancak S yalnızca 25 veya 26 gün sürebilir, çünkü belirli bir tarihte hiçbir olay olmamıştır. Ardından, çizmeye çalıştığımda boyutlar eşleşmediğinden bir AssertionError alıyorum:

fig, ax = plt.subplots()    
ax.bar(idx.to_pydatetime(), s, color='green')

Bununla başa çıkmanın doğru yolu nedir? IDX'ten hiçbir değer içermeyen tarihleri ​​kaldırmak mı yoksa (bunu yapmayı tercih ederim) seriye eksik tarihi 0 ile mi eklemek istiyorum. 0 değerleri olan 30 günlük tam bir grafiğe sahip olmayı tercih ederim. Bu yaklaşım doğruysa, nasıl başlayacağınıza dair herhangi bir öneriniz var mı? Bir çeşit dinamik reindexişleve ihtiyacım var mı?

İşte bir S ( df.groupby(['simpleDate']).size() ) parçası, 04 ve 05 için hiçbir girişe dikkat edin.

09-02-2013     2
09-03-2013    10
09-06-2013     5
09-07-2013     1

Yanıtlar:


257

Şunları kullanabilirsiniz Series.reindex:

import pandas as pd

idx = pd.date_range('09-01-2013', '09-30-2013')

s = pd.Series({'09-02-2013': 2,
               '09-03-2013': 10,
               '09-06-2013': 5,
               '09-07-2013': 1})
s.index = pd.DatetimeIndex(s.index)

s = s.reindex(idx, fill_value=0)
print(s)

verim

2013-09-01     0
2013-09-02     2
2013-09-03    10
2013-09-04     0
2013-09-05     0
2013-09-06     5
2013-09-07     1
2013-09-08     0
...

23
reindexinanılmaz bir işlevdir. (1) mevcut verileri yeni bir etiket setiyle eşleşecek şekilde yeniden sıralayabilir, (2) daha önce etiket bulunmayan yeni satırlar ekleyebilir, (3) eksik etiketler için verileri doldurabilir, (ileri / geri doldurma dahil) (4) satırları seçebilir etikete göre!
unutbu

@unutbu Bu cevap benim de sorduğum bir sorunun parçası, teşekkürler! Ancak, olayların olduğu tarihlerin dinamik olarak bir listesini nasıl oluşturacağınızı bilip bilmediğinizi merak ediyor muydunuz?
Nick Duddy

2
Yine de reindex ile ilgili bir sorun (veya hata) vardır: 1/1/1970 öncesindeki tarihlerle çalışmaz, bu nedenle bu durumda df.resample () mükemmel çalışır.
Sergey Gulbin

2
bunun yerine başlangıç ​​ve bitiş tarihlerini manuel olarak girmeyi idx için kullanabilirsiniz:idx = pd.date_range(df.index.min(), df.index.max())
Reveille

Aramayı kurtarmak için buradaki dokümantasyon bağlantısını bırakarak: pandas.pydata.org/pandas-docs/stable/reference/api/…
Harm te Molder

41

Daha hızlı bir çözüm kullanmaktır .asfreq(). Bu, içinde arama yapmak için yeni bir dizin oluşturulmasını gerektirmez .reindex().

# "broken" (staggered) dates
dates = pd.Index([pd.Timestamp('2012-05-01'), 
                  pd.Timestamp('2012-05-04'), 
                  pd.Timestamp('2012-05-06')])
s = pd.Series([1, 2, 3], dates)

print(s.asfreq('D'))
2012-05-01    1.0
2012-05-02    NaN
2012-05-03    NaN
2012-05-04    2.0
2012-05-05    NaN
2012-05-06    3.0
Freq: D, dtype: float64

1
Bu yöntemi gerçekten tercih ediyorum; date_rangeBaşlangıç ​​ve bitiş olarak örtük olarak ilk ve son dizini kullandığı için çağırmak zorunda kalmazsınız (neredeyse her zaman isteyeceğiniz şey budur).
Michael Hays

Çok temiz ve profesyonel bir yöntem. Daha sonra da interpolate kullanarak iyi sonuç verir.
msarafzadeh

27

Bir sorun, reindexyinelenen değerler varsa başarısız olacaktır. Tarihe göre dizine eklemek istediğimiz zaman damgalı verilerle çalıştığımızı varsayalım:

df = pd.DataFrame({
    'timestamps': pd.to_datetime(
        ['2016-11-15 1:00','2016-11-16 2:00','2016-11-16 3:00','2016-11-18 4:00']),
    'values':['a','b','c','d']})
df.index = pd.DatetimeIndex(df['timestamps']).floor('D')
df

verim

            timestamps             values
2016-11-15  "2016-11-15 01:00:00"  a
2016-11-16  "2016-11-16 02:00:00"  b
2016-11-16  "2016-11-16 03:00:00"  c
2016-11-18  "2016-11-18 04:00:00"  d

Yinelenen 2016-11-16tarih nedeniyle , yeniden dizine ekleme girişimi:

all_days = pd.date_range(df.index.min(), df.index.max(), freq='D')
df.reindex(all_days)

şununla başarısız:

...
ValueError: cannot reindex from a duplicate axis

(bu, dizinin kopyaları olduğu anlamına gelir, kendisinin bir kopya olmadığı anlamına gelir)

Bunun yerine, .locaralıktaki tüm tarihler için girişleri aramak için kullanabiliriz :

df.loc[all_days]

verim

            timestamps             values
2016-11-15  "2016-11-15 01:00:00"  a
2016-11-16  "2016-11-16 02:00:00"  b
2016-11-16  "2016-11-16 03:00:00"  c
2016-11-17  NaN                    NaN
2016-11-18  "2016-11-18 04:00:00"  d

fillna gerekirse boşlukları doldurmak için sütun serilerinde kullanılabilir.


Tarih sütununda Blanksveya varsa ne yapılacağı hakkında bir fikriniz var NULLSmı? df.loc[all_days]bu durumda işe yaramaz.
Furqan Hashim

1
List-like'ları .loc veya [] 'a herhangi bir etiketle aktarmak, gelecekte KeyError'ı yükseltecektir, alternatif olarak .reindex () kullanabilirsiniz. Belgelere buradan bakın: pandas.pydata.org/pandas-docs/stable/…
Dmitrii Magas

19

resampleEksik tarihlere ek olarak yinelenen tarihleri ​​de işleyebilen alternatif bir yaklaşımdır . Örneğin:

df.resample('D').mean()

resamplebunun gibi ertelenmiş bir işlemdir groupby, başka bir işlemle takip etmeniz gerekir. Bu durumda meaniyi çalışır, ama aynı zamanda gibi birçok diğer pandalar yöntemleri kullanabilirsiniz max, sumvb

Orijinal veriler, ancak '2013-09-03' için ek bir girişle birlikte:

             val
date           
2013-09-02     2
2013-09-03    10
2013-09-03    20    <- duplicate date added to OP's data
2013-09-06     5
2013-09-07     1

Ve işte sonuçlar:

             val
date            
2013-09-02   2.0
2013-09-03  15.0    <- mean of original values for 2013-09-03
2013-09-04   NaN    <- NaN b/c date not present in orig
2013-09-05   NaN    <- NaN b/c date not present in orig
2013-09-06   5.0
2013-09-07   1.0

Bunun nasıl çalıştığını açıklığa kavuşturmak için eksik tarihleri ​​NaNs olarak bıraktım, ancak fillna(0)NaN'leri OP tarafından talep edildiği gibi sıfırlarla değiştirmek için ekleyebilir veya alternatif interpolate()olarak komşu satırlara göre sıfır olmayan değerlerle doldurmak gibi bir şey kullanabilirsiniz .


6

Eksik tarihleri ​​bir veri çerçevesine doldurmak için, seçtiğiniz fill_value, days_backdoldurmak ve veri çerçevesini sıralamak için sıralama düzeni ( date_order) ile doldurmanın güzel bir yöntemi :

def fill_in_missing_dates(df, date_col_name = 'date',date_order = 'asc', fill_value = 0, days_back = 30):

    df.set_index(date_col_name,drop=True,inplace=True)
    df.index = pd.DatetimeIndex(df.index)
    d = datetime.now().date()
    d2 = d - timedelta(days = days_back)
    idx = pd.date_range(d2, d, freq = "D")
    df = df.reindex(idx,fill_value=fill_value)
    df[date_col_name] = pd.DatetimeIndex(df.index)

    return df
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.