Panda: come includere tutte le colonne per tutte le righe anche se il valore manca in un dataframe con un formato lungo?
All'inizio può sembrare una domanda strana, ma ho trovato difficile trovare termini "standard" quando si parla di elementi di dati di a long format
. Quindi ho pensato di usare gli stessi termini usati da Hadley Wickham in uno dei primi esempi nel suo articolo su Tidy Data :

In un campione dei miei dati del mondo reale, la riga contiene le date , la colonna contiene le categorie e il valore contiene i prezzi come questo:
Ingresso
row column value
0 21.08.2020 A 43
1 21.08.2020 A 36
2 21.08.2020 B 36
3 21.08.2020 C 28
4 22.08.2020 A 16
5 22.08.2020 B 40
6 22.08.2020 B 34
Qui, i column
valori non sono regolari come l'immagine sopra. Mancano alcuni valori di colonna per alcuni valori di riga. Come posso includere i nomi delle colonne nello stesso set di dati con il valore impostato su 0
? Nel dataframe di esempio sopra, column C
si verifica solo per row = 21.08.2020
:
Esiste una funzione panda che possa tenerne conto e includere 22.08.2020 C 0
?
Uscita desiderata
row column value
0 21.08.2020 A 43
1 21.08.2020 A 36
2 21.08.2020 B 36
3 21.08.2020 C 28
4 22.08.2020 A 16
5 22.08.2020 B 40
6 22.08.2020 B 34
7 22.08.2020 C 0
Ho provato un approccio con il recupero di tutto unique column values = ['A', 'B', 'C']
, quindi il ciclo di tutti i valori di riga e l'inserimento delle colonne mancanti con value = 0
, ma questo si è trasformato in un vero disastro molto velocemente. Quindi qualsiasi altro suggerimento sarebbe fantastico!
Modifica: da lungo a largo usando pd.pivot
L'uso pd.pivot_table(df1,index='row',columns='column',values='value')
trasformerà il dataframe di input sopra in:
column A B C
row
21.08.2020 39.5 36.0 28.0
22.08.2020 16.0 37.0 NaN
Qui NaN
è incluso di default per column=C
e row=22.08.2020
. Quindi ora resta il caso di fondere o ruotare questo dataframe nell'output desiderato senza far cadere il file NaN
.
Modifica 2: sample dataframe
import pandas as pd
df=pd.DataFrame({'row': {0: '21.08.2020',
1: '21.08.2020',
2: '21.08.2020',
3: '21.08.2020',
4: '22.08.2020',
5: '22.08.2020',
6: '22.08.2020'},
'column': {0: 'A', 1: 'A', 2: 'B', 3: 'C', 4: 'A', 5: 'B', 6: 'B'},
'value': {0: 43, 1: 36, 2: 36, 3: 28, 4: 16, 5: 40, 6: 34}})
Risposte
Questo è diverso dal precedente poiché abbiamo più valori per la stessa riga
df['key']=df.groupby(['row','column']).cumcount()
df1 = pd.pivot_table(df,index='row',columns=['key','column'],values='value')
df1 = df1.stack(level=[0,1],dropna=False).to_frame('value').reset_index()
df1 = df1[df1.key.eq(0) | df1['value'].notna()]
df1
Out[97]:
row key column value
0 21.08.2020 0 A 43.0
1 21.08.2020 0 B 36.0
2 21.08.2020 0 C 28.0
3 21.08.2020 1 A 36.0
6 22.08.2020 0 A 16.0
7 22.08.2020 0 B 40.0
8 22.08.2020 0 C NaN
10 22.08.2020 1 B 34.0
Ho trovato un approccio con pd.pivot()
in combinazione con unstack()
:
import pandas as pd
df=pd.DataFrame({'row': {0: '21.08.2020',
1: '21.08.2020',
2: '21.08.2020',
3: '21.08.2020',
4: '22.08.2020',
5: '22.08.2020',
6: '22.08.2020'},
'column': {0: 'A', 1: 'A', 2: 'B', 3: 'C', 4: 'A', 5: 'B', 6: 'B'},
'value': {0: 43, 1: 36, 2: 36, 3: 28, 4: 16, 5: 40, 6: 34}})
df1 = pd.pivot_table(df,index='row',columns='column',values='value').unstack().reset_index()
print(df1)
Produzione
column row 0
0 A 21.08.2020 39.5
1 A 22.08.2020 16.0
2 B 21.08.2020 36.0
3 B 22.08.2020 37.0
4 C 21.08.2020 28.0
5 C 22.08.2020 NaN
L' ordine delle colonne del dataframe è probabilmente incasinato ...
Ecco un approccio ingenuo: utilizza un ciclo for.
data = {'row': {0: '21.08.2020', 1: '21.08.2020', 2: '21.08.2020',
3: '21.08.2020', 4: '22.08.2020', 5: '22.08.2020',
6: '22.08.2020'},
'column': {0: 'A', 1: 'A', 2: 'B', 3: 'C', 4: 'A', 5: 'B', 6: 'B'},
'value': {0: 43, 1: 36, 2: 36, 3: 28, 4: 16, 5: 40, 6: 34}}
df = pd.DataFrame(data)
categories = set(df.column.unique())
tbl = pd.pivot_table(df[['row','column']],values='column',index='row',aggfunc=set)
missing = tbl.column.apply(categories.difference)
missing = filter(lambda x:x[1],missing.items())
d = collections.defaultdict(list)
#d = {'row':[],'column':[],'value':[]}
for row,col in missing:
for cat in col:
d['row'].append(row)
d['column'].append(cat)
d['value'].append(0)
df2 = df.append (pd.DataFrame (d)). reset_index ()
df2 = df.append(pd.DataFrame(d)).reset_index()
Ovviamente tutti i nuovi valori saranno alla fine e dovrebbe essere ordinato se questo è un problema.
Oggetti intermedi:
>>> tbl
column
row
21.08.2020 {A, B, C}
22.08.2020 {A, B}
>>> missing
row
21.08.2020 {}
22.08.2020 {C}
Name: column, dtype: object
>>>
Ecco un'alternative.it imposta l' row
e column
colonne come il nuovo indice, ottiene tutte le possibili combinazioni di valori nelle row
e column
colonne, e si unisce (come 'esterno' =) un dataframe vuota con le row
e column
combinazioni come il nuovo indice:
From itertools import product
new_index = product(set(df.row.array), set(df.column.array))
df = df.set_index(["row", "column"])
new_index = pd.DataFrame([], index=pd.Index(new_index, names=["row", "column"]))
df.join(new_index, how="outer").reset_index().astype({"value": "Int8"}) # if you are keen on nullable integers
row column value
0 21.08.2020 A 43
1 21.08.2020 A 36
2 21.08.2020 B 36
3 21.08.2020 C 28
4 22.08.2020 A 16
5 22.08.2020 B 40
6 22.08.2020 B 34
7 22.08.2020 C <NA>