Thêm ngày bị thiếu vào khung dữ liệu gấu trúc


126

Dữ liệu của tôi có thể có nhiều sự kiện vào một ngày nhất định hoặc KHÔNG có sự kiện nào trong một ngày. Tôi lấy những sự kiện này, đếm từng ngày và vẽ chúng. Tuy nhiên, khi tôi vẽ chúng, hai loạt của tôi không luôn khớp.

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

Trong mã trên, idx trở thành một phạm vi nói 30 ngày. 09-01-2013 đến 09-30-2013 Tuy nhiên S chỉ có thể có 25 hoặc 26 ngày vì không có sự kiện nào xảy ra trong một ngày nhất định. Sau đó, tôi nhận được một AssertsError vì kích thước không khớp khi tôi cố gắng vẽ đồ thị:

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

Cách thích hợp để giải quyết vấn đề này là gì? Tôi có muốn xóa ngày không có giá trị khỏi IDX hay (mà tôi muốn làm hơn) được thêm vào chuỗi ngày thiếu với số 0. Tôi muốn có một biểu đồ đầy đủ trong 30 ngày với 0 giá trị. Nếu cách tiếp cận này là đúng, bất kỳ đề xuất về cách bắt đầu? Tôi có cần một số loại reindexchức năng động ?

Đây là một đoạn của S ( df.groupby(['simpleDate']).size() ), thông báo không có mục nào cho 04 và 05.

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

Câu trả lời:


252

Bạn có thể sử dụng 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)

sản lượng

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
reindexlà một chức năng tuyệt vời. Nó có thể (1) sắp xếp lại dữ liệu hiện có để khớp với một bộ nhãn mới, (2) chèn các hàng mới trong đó không có nhãn nào tồn tại trước đó, (3) điền dữ liệu cho các nhãn bị thiếu, (bao gồm cả điền vào trước / sau) (4) chọn các hàng theo nhãn!
unutbu

@unutbu Câu trả lời này là một phần của câu hỏi tôi cũng có, cảm ơn! Nhưng đã tự hỏi nếu bạn biết làm thế nào để tự động tạo một danh sách với các ngày có sự kiện?
Nick Duddy

2
Có một vấn đề (hoặc lỗi) với reindex: nó không hoạt động với ngày trước ngày 1/1/1970, vì vậy trong trường hợp này df.resample () hoạt động hoàn hảo.
Serge Gulbin

1
bạn có thể sử dụng điều này thay cho idx để bỏ qua việc nhập ngày bắt đầu và ngày kết thúc theo cách thủ công:idx = pd.date_range(df.index.min(), df.index.max())
Reveille

Bỏ liên kết đến tài liệu ở đây, để giúp bạn tiết kiệm tìm kiếm: pandas.pydata.org/pandas-docs/urdy/reference/api/ Kẻ
Harm te Molder

40

Một cách giải quyết nhanh hơn là sử dụng .asfreq(). Điều này không yêu cầu tạo ra một chỉ mục mới để gọi trong .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
Tôi thực sự thích phương pháp này; bạn tránh phải gọi date_rangevì nó ngầm sử dụng chỉ mục đầu tiên và cuối cùng là bắt đầu và kết thúc (đó là những gì bạn hầu như luôn muốn).
Michael Hays

Phương pháp rất sạch sẽ và chuyên nghiệp. Hoạt động tốt với việc sử dụng nội suy sau đó là tốt.
msarafzadeh

26

Một vấn đề là reindexsẽ thất bại nếu có các giá trị trùng lặp. Giả sử chúng tôi đang làm việc với dữ liệu được đánh dấu thời gian mà chúng tôi muốn lập chỉ mục theo ngày:

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

sản lượng

            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

Do 2016-11-16ngày trùng lặp , một nỗ lực để reindex:

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

thất bại với:

...
ValueError: cannot reindex from a duplicate axis

(bởi điều này có nghĩa là chỉ mục có các bản sao, không phải bản thân nó là bản sao)

Thay vào đó, chúng ta có thể sử dụng .locđể tra cứu các mục cho tất cả các ngày trong phạm vi:

df.loc[all_days]

sản lượng

            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 có thể được sử dụng trên chuỗi cột để điền vào chỗ trống nếu cần.


Bất kỳ ý tưởng về những gì để làm nếu cột ngày có chứa Blankshoặc NULLS? df.loc[all_days]sẽ không làm việc trong trường hợp đó.
Furqan Hashim

1
Việc chuyển danh sách thích .loc hoặc [] với bất kỳ nhãn bị thiếu nào sẽ tăng KeyError trong tương lai, bạn có thể sử dụng .reindex () thay thế. Xem tài liệu tại đây: pandas.pydata.org/pandas-docs/ sóng / trộm
Dmitrii Magas

19

Một cách tiếp cận khác là resample, có thể xử lý các ngày trùng lặp ngoài các ngày bị thiếu. Ví dụ:

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

resamplelà một hoạt động hoãn lại như groupbyvậy, vì vậy bạn cần phải theo dõi nó với một hoạt động khác. Trong trường hợp này meanhoạt động tốt, nhưng bạn cũng có thể sử dụng nhiều phương pháp gấu trúc khác như max,sum vv

Đây là dữ liệu gốc, nhưng có thêm mục nhập cho '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

Và đây là kết quả:

             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

Tôi đã để lại những ngày bị thiếu là NaN để làm rõ cách thức hoạt động của nó, nhưng bạn có thể thêm fillna(0)để thay thế NaN bằng số 0 theo yêu cầu của OP hoặc sử dụng một cái gì đó như interpolate()để điền vào các giá trị khác không dựa trên các hàng lân cận.


6

Đây là một phương pháp hay để điền ngày bị thiếu vào khung dữ liệu, với sự lựa chọn của bạn fill_value, days_backđể điền và sắp xếp thứ tự ( date_order) để sắp xếp khung dữ liệu:

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
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.