Panda: come includere tutte le colonne per tutte le righe anche se il valore manca in un dataframe con un formato lungo?

Aug 23 2020

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 columnvalori 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 Csi 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=Ce 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

2 BENY Aug 23 2020 at 06:32

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
1 vestland Aug 23 2020 at 06:09

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

1 wwii Aug 23 2020 at 06:40

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
>>>
1 sammywemmy Aug 23 2020 at 10:44

Ecco un'alternative.it imposta l' rowe columncolonne come il nuovo indice, ottiene tutte le possibili combinazioni di valori nelle rowe columncolonne, e si unisce (come 'esterno' =) un dataframe vuota con le rowe columncombinazioni 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>