Додайте пропущені дати до фрейму даних панд


127

Мої дані можуть мати декілька подій у певну дату або НЕ подій на дату. Я беру ці події, беру підрахунок за датою та малюю їх. Однак, коли я їх закладаю, мої дві серії не завжди відповідають.

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

У наведеному вище коді idx стає діапазоном, наприклад, 30 дат. З 01.09.2013 по 30.09.2013 Однак, S може мати лише 25 або 26 днів, оскільки жодна подія не відбулася за вказану дату. Потім я отримую AssertionError, оскільки розміри не збігаються, коли я намагаюся побудувати графік:

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

Який правильний спосіб вирішити це? Чи хочу я видалити дати ID без значень з IDX або (що я б краще зробив) - додати до серії пропущену дату з рахунком 0. Я вважаю за краще мати повний графік 30 днів із 0 значеннями. Якщо цей підхід правильний, якісь пропозиції щодо того, як розпочати роботу? Чи потрібна мені якась динамічна reindexфункція?

Ось фрагмент S ( df.groupby(['simpleDate']).size() ), зауважте, що немає записів за 04 та 05.

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

Відповіді:


254

Ви можете використовувати 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)

врожайність

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
reindex- це дивовижна функція. Він може (1) упорядкувати наявні дані, щоб вони відповідали новому набору міток, (2) вставити нові рядки, де раніше не існувало мітки, (3) заповнити дані про відсутніх мітках (у тому числі шляхом заповнення вперед / назад) (4) вибір рядків за етикеткою!
unutbu

@unutbu Це відповідає на питання, яке у мене теж було, дякую! Але вам цікаво, чи знаєте ви, як динамічно створити список із датами, які мають події?
Нік Дадді

2
Існує одна проблема (або помилка) з повторним вимкненням: він не працює з датами до 1.01.1970, тому в цьому випадку df.resample () працює відмінно.
Сергій Гулбін

1
ви можете використовувати це замість idx, щоб пропустити дати початку та кінця вручну:idx = pd.date_range(df.index.min(), df.index.max())
Reveille

Перемістивши посилання на документацію сюди, щоб заощадити пошук: pandas.pydata.org/pandas-docs/stable/reference/api/…
Шкода

40

Швидше вирішити проблему .asfreq(). Це не вимагає створення нового індексу для дзвінка всередині .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
Я дуже віддаю перевагу цьому методу; вам не потрібно телефонувати, date_rangeоскільки він неявно використовує перший і останній індекс як початок і кінець (що ви майже завжди хочете).
Майкл Хейс

Дуже чистий і професійний метод. Добре працює і з використанням інтерполяту.
msarafzadeh

26

Одне питання полягає в тому, що reindexне вдасться, якщо є дублюючі значення. Скажімо, ми працюємо з датами, розміченими часом, які ми хочемо індексувати за датою:

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

врожайність

            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

Через повторювану 2016-11-16дату спроба повторно встановити:

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

не вдається:

...
ValueError: cannot reindex from a duplicate axis

(це означає, що індекс має дублікати, а не те, що він сам є дубліком)

Натомість ми можемо .locшукати записи для всіх дат у діапазоні:

df.loc[all_days]

врожайність

            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 може бути використаний у серії стовпців для заповнення пробілів, якщо це необхідно.


Будь-яка ідея, що робити, якщо стовпець Дата містить Blanksабо NULLS? df.loc[all_days]не буде працювати в такому випадку.
Фуркан Хашим

1
Передача списку-лайків до .loc або [] з будь-якою відсутньою міткою призведе до збільшення KeyError у майбутньому, ви можете використовувати .reindex () як альтернативу. Дивіться документацію тут: pandas.pydata.org/pandas-docs/stable/…
Дмитро Магас

19

Альтернативний підхід - resampleце можливість обробляти повторювані дати на додаток до пропущених дат. Наприклад:

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

resampleє відкладеною операцією, як groupbyотже, вам потрібно слідувати за нею в іншій операції. В цьому випадку meanдобре працює, але ви можете також використовувати багато методів інших панд , як max, sumі т.д.

Ось оригінальні дані, але з додатковим записом для '2013-09-03':

             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

І ось результати:

             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

Я залишив пропущені дати як NaN, щоб зрозуміти, як це працює, але ви можете додати, fillna(0)щоб замінити NaN на нулі, як цього вимагає ОП, або ж використати щось на зразок interpolate()заповнення ненульовими значеннями на основі сусідніх рядків.


6

Ось приємний метод заповнення пропущених дат у кадр даних, на ваш вибір fill_value, days_backдля заповнення та сортування порядку ( date_order), за яким можна сортувати кадр даних:

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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.