Ich habe einen Datenrahmen, der wie folgt aussieht
x = pd.DataFrame({'user': ['a','a','b','b'], 'dt': ['2016-01-01','2016-01-02', '2016-01-05','2016-01-06'], 'val': [1,33,2,1]})
Ich möchte in der Lage sein, das minimale und maximale Datum in der Datumsspalte zu finden und diese Spalte zu erweitern, um alle Daten dort zu haben, während Sie gleichzeitig ausfüllen 0
für die val
Säule. Die gewünschte Ausgabe ist also
dt user val
0 2016-01-01 a 1
1 2016-01-02 a 33
2 2016-01-03 a 0
3 2016-01-04 a 0
4 2016-01-05 a 0
5 2016-01-06 a 0
6 2016-01-01 b 0
7 2016-01-02 b 0
8 2016-01-03 b 0
9 2016-01-04 b 0
10 2016-01-05 b 2
11 2016-01-06 b 1
Ich habe die hier und hier erwähnte Lösung ausprobiert, aber sie sind nicht das, was ich suche. Alle mögliche Hinweise sehr geschätzt.
Anfangsdatenrahmen:
dt user val
0 2016-01-01 a 1
1 2016-01-02 a 33
2 2016-01-05 b 2
3 2016-01-06 b 1
Konvertieren Sie zuerst die Daten in datetime:
x['dt'] = pd.to_datetime(x['dt'])
Generieren Sie dann die Daten und eindeutigen Benutzer:
dates = x.set_index('dt').resample('D').asfreq().index
>> DatetimeIndex(['2016-01-01', '2016-01-02', '2016-01-03', '2016-01-04',
'2016-01-05', '2016-01-06'],
dtype="datetime64[ns]", name="dt", freq='D')
users = x['user'].unique()
>> array(['a', 'b'], dtype=object)
Auf diese Weise können Sie einen MultiIndex erstellen:
idx = pd.MultiIndex.from_product((dates, users), names=['dt', 'user'])
>> MultiIndex(levels=[[2016-01-01 00:00:00, 2016-01-02 00:00:00, 2016-01-03 00:00:00, 2016-01-04 00:00:00, 2016-01-05 00:00:00, 2016-01-06 00:00:00], ['a', 'b']],
labels=[[0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5], [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1]],
names=['dt', 'user'])
Damit können Sie Ihren DataFrame neu indizieren:
x.set_index(['dt', 'user']).reindex(idx, fill_value=0).reset_index()
Out:
dt user val
0 2016-01-01 a 1
1 2016-01-01 b 0
2 2016-01-02 a 33
3 2016-01-02 b 0
4 2016-01-03 a 0
5 2016-01-03 b 0
6 2016-01-04 a 0
7 2016-01-04 b 0
8 2016-01-05 a 0
9 2016-01-05 b 2
10 2016-01-06 a 0
11 2016-01-06 b 1
die dann nach Benutzern sortiert werden können:
x.set_index(['dt', 'user']).reindex(idx, fill_value=0).reset_index().sort_values(by='user')
Out:
dt user val
0 2016-01-01 a 1
2 2016-01-02 a 33
4 2016-01-03 a 0
6 2016-01-04 a 0
8 2016-01-05 a 0
10 2016-01-06 a 0
1 2016-01-01 b 0
3 2016-01-02 b 0
5 2016-01-03 b 0
7 2016-01-04 b 0
9 2016-01-05 b 2
11 2016-01-06 b 1
Wie @ayhan vorschlägt
x.dt = pd.to_datetime(x.dt)
Einzeiler, der hauptsächlich @ayhans Ideen verwendet, während er integriert wird stack
/unstack
und fill_value
x.set_index(
['dt', 'user']
).unstack(
fill_value=0
).asfreq(
'D', fill_value=0
).stack().sort_index(level=1).reset_index()
dt user val
0 2016-01-01 a 1
1 2016-01-02 a 33
2 2016-01-03 a 0
3 2016-01-04 a 0
4 2016-01-05 a 0
5 2016-01-06 a 0
6 2016-01-01 b 0
7 2016-01-02 b 0
8 2016-01-03 b 0
9 2016-01-04 b 0
10 2016-01-05 b 2
11 2016-01-06 b 1
Eine alte Frage mit bereits ausgezeichneten Antworten; Dies ist eine Alternative, mit der Komplett Funktion von pyjanitor das könnte bei der Abstraktion helfen, wenn explizit fehlende Zeilen generiert werden:
#pip install pyjanitor
import pandas as pd
import janitor as jn
x['dt'] = pd.to_datetime(x['dt'])
# generate complete list of dates
dates = dict(dt = pd.date_range(x.dt.min(), x.dt.max(), freq='1D'))
# build the new dataframe, and fill nulls with 0
x.complete('user', dates, sort = True).fillna(0, downcast="infer")
user dt val
0 a 2016-01-01 00:00:00 1
1 a 2016-01-02 00:00:00 33
2 a 2016-01-03 00:00:00 0
3 a 2016-01-04 00:00:00 0
4 a 2016-01-05 00:00:00 0
5 a 2016-01-06 00:00:00 0
6 b 2016-01-01 00:00:00 0
7 b 2016-01-02 00:00:00 0
8 b 2016-01-03 00:00:00 0
9 b 2016-01-04 00:00:00 0
10 b 2016-01-05 00:00:00 2
11 b 2016-01-06 00:00:00 1
.