wake-up-neo.net

Ergänzen Sie pandas dataframe um fehlende Daten

Meine Daten können mehrere Ereignisse an einem bestimmten Datum oder KEINE Ereignisse an einem Datum enthalten. Ich nehme diese Ereignisse, zähle sie nach Datum und zeichne sie auf. Wenn ich sie zeichne, stimmen meine beiden Serien jedoch nicht immer überein.

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

Im obigen Code wird idx ein Bereich von etwa 30 Daten. 09-01-2013 bis 09-30-2013 Jedoch S hat möglicherweise nur 25 oder 26 Tage, da für ein bestimmtes Datum keine Ereignisse stattgefunden haben. Ich erhalte dann einen AssertionError, da die Größen nicht übereinstimmen, wenn ich versuche zu zeichnen:

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

Wie kann man das richtig angehen? Möchten Sie Daten ohne Werte aus IDX entfernen oder (was ich lieber tun würde), fügen Sie der Serie das fehlende Datum mit einer Anzahl von 0 hinzu Ich hätte lieber eine vollständige Grafik von 30 Tagen mit 0 Werten. Wenn dieser Ansatz richtig ist, gibt es Vorschläge für den Einstieg? Benötige ich eine Art dynamische reindex Funktion?

Hier ist ein Ausschnitt aus S (df.groupby(['simpleDate']).size()), beachten Sie keine Einträge für 04 und 05.

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

Du könntest benutzen 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)

ausbeuten

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
...
194
unutbu

Eine schnellere Problemumgehung ist die Verwendung von .asfreq() . Dazu muss kein neuer Index erstellt werden, um in .reindex() aufzurufen.

# "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
24
Brad Solomon

Ein Problem ist, dass reindex fehlschlägt, wenn es doppelte Werte gibt. Angenommen, wir arbeiten mit Zeitstempeldaten, die wir nach Datum indizieren möchten:

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

ausbeuten

            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

Aufgrund des Duplikats 2016-11-16 Datum, ein Versuch, neu zu indizieren:

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

scheitert mit:

...
ValueError: cannot reindex from a duplicate axis

(Dies bedeutet, dass der Index Duplikate enthält, nicht, dass er selbst ein Dup ist.)

Stattdessen können wir .loc, um Einträge für alle Daten im Bereich nachzuschlagen:

df.loc[all_days]

ausbeuten

            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 kann für die Spaltenserie verwendet werden, um bei Bedarf Lücken zu füllen.

21
Nick Edgar

Ein alternativer Ansatz ist resample , der neben fehlenden Daten auch doppelte Daten verarbeiten kann. Beispielsweise:

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

resample ist eine verzögerte Operation wie groupby, daher müssen Sie ihr eine weitere Operation folgen. In diesem Fall funktioniert mean gut, aber Sie können auch viele andere pandas Methoden wie max, sum usw. verwenden.

Hier sind die Originaldaten, aber mit einem zusätzlichen Eintrag für '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

Und hier sind die Ergebnisse:

             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

Ich habe die fehlenden Daten als NaNs belassen, um zu verdeutlichen, wie dies funktioniert, aber Sie können fillna(0) hinzufügen, um NaNs durch vom OP angeforderte Nullen zu ersetzen, oder alternativ etwas wie interpolate() zum Füllen verwenden mit Werten ungleich Null basierend auf den benachbarten Zeilen.

13
JohnE

Hier ist eine nette Methode, um fehlende Daten in einen Datenrahmen einzufügen, mit Ihrer Wahl von fill_value, days_back zum Ausfüllen und Sortieren (date_order) nach dem der Datenrahmen sortiert werden soll:

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
4
eiTan LaVi