Come utente con entrambi Re python, ho visto questo tipo di domanda un paio di volte.
In R, hanno la funzione incorporata dal pacchetto tidyrchiamata unnest. Ma in Python( pandas) non esiste una funzione incorporata per questo tipo di domanda.
So che le objectcolonne typerendono sempre i dati difficili da convertire con una pandasfunzione '. Quando ho ricevuto i dati in questo modo, la prima cosa che mi è venuta in mente è stata di "appiattire" o rendere meno visibili le colonne.
Sto usando pandase pythonfunzioni per questo tipo di domanda. Se sei preoccupato per la velocità delle soluzioni di cui sopra, controlla la risposta di user3483203, poiché sta usando numpye la maggior parte delle volte numpyè più veloce. Mi raccomando Cpythone numbase la velocità è importante nel tuo caso.
Metodo 0 [pandas> = 0.25]
A partire da panda 0.25 , se devi esplodere solo una colonna, puoi utilizzare la explodefunzione:
df.explode('B')
A B
0 1 1
1 1 2
0 2 1
1 2 2
Metodo 1
apply + pd.Series (facile da capire ma in termini di prestazioni non consigliato.)
df.set_index('A').B.apply(pd.Series).stack().reset_index(level=0).rename(columns={0:'B'})
Out[463]:
A B
0 1 1
1 1 2
0 2 1
1 2 2
Metodo 2
Utilizzando repeatcon il DataFramecostruttore, ricrea il tuo dataframe (buono per le prestazioni, non buono per più colonne)
df=pd.DataFrame({'A':df.A.repeat(df.B.str.len()),'B':np.concatenate(df.B.values)})
df
Out[465]:
A B
0 1 1
0 1 2
1 2 1
1 2 2
Metodo 2.1
per esempio oltre ad A abbiamo A.1 ..... An Se usiamo ancora il metodo ( Metodo 2 ) sopra, è difficile per noi ricreare le colonne una per una.
Soluzione: joino mergecon il indexdopo "non più" le singole colonne
s=pd.DataFrame({'B':np.concatenate(df.B.values)},index=df.index.repeat(df.B.str.len()))
s.join(df.drop('B',1),how='left')
Out[477]:
B A
0 1 1
0 2 1
1 1 2
1 2 2
Se hai bisogno che l'ordine delle colonne sia esattamente lo stesso di prima, aggiungi reindexalla fine.
s.join(df.drop('B',1),how='left').reindex(columns=df.columns)
Metodo 3:
ricrea il filelist
pd.DataFrame([[x] + [z] for x, y in df.values for z in y],columns=df.columns)
Out[488]:
A B
0 1 1
1 1 2
2 2 1
3 2 2
Se più di due colonne, utilizzare
s=pd.DataFrame([[x] + [z] for x, y in zip(df.index,df.B) for z in y])
s.merge(df,left_on=0,right_index=True)
Out[491]:
0 1 A B
0 0 1 1 [1, 2]
1 0 2 1 [1, 2]
2 1 1 2 [1, 2]
3 1 2 2 [1, 2]
Metodo 4
utilizzando reindex oloc
df.reindex(df.index.repeat(df.B.str.len())).assign(B=np.concatenate(df.B.values))
Out[554]:
A B
0 1 1
0 1 2
1 2 1
1 2 2
#df.loc[df.index.repeat(df.B.str.len())].assign(B=np.concatenate(df.B.values))
Metodo 5
quando l'elenco contiene solo valori univoci:
df=pd.DataFrame({'A':[1,2],'B':[[1,2],[3,4]]})
from collections import ChainMap
d = dict(ChainMap(*map(dict.fromkeys, df['B'], df['A'])))
pd.DataFrame(list(d.items()),columns=df.columns[::-1])
Out[574]:
B A
0 1 1
1 2 1
2 3 2
3 4 2
Metodo 6
utilizzo numpyper prestazioni elevate:
newvalues=np.dstack((np.repeat(df.A.values,list(map(len,df.B.values))),np.concatenate(df.B.values)))
pd.DataFrame(data=newvalues[0],columns=df.columns)
A B
0 1 1
1 1 2
2 2 1
3 2 2
Metodo 7
utilizzando la funzione di base itertools cyclee chain: Soluzione Python pura solo per divertimento
from itertools import cycle,chain
l=df.values.tolist()
l1=[list(zip([x[0]], cycle(x[1])) if len([x[0]]) > len(x[1]) else list(zip(cycle([x[0]]), x[1]))) for x in l]
pd.DataFrame(list(chain.from_iterable(l1)),columns=df.columns)
A B
0 1 1
1 1 2
2 2 1
3 2 2
Generalizzazione a più colonne
df=pd.DataFrame({'A':[1,2],'B':[[1,2],[3,4]],'C':[[1,2],[3,4]]})
df
Out[592]:
A B C
0 1 [1, 2] [1, 2]
1 2 [3, 4] [3, 4]
Funzione di auto-definizione:
def unnesting(df, explode):
idx = df.index.repeat(df[explode[0]].str.len())
df1 = pd.concat([
pd.DataFrame({x: np.concatenate(df[x].values)}) for x in explode], axis=1)
df1.index = idx
return df1.join(df.drop(explode, 1), how='left')
unnesting(df,['B','C'])
Out[609]:
B C A
0 1 1 1
0 2 2 1
1 3 3 2
1 4 4 2
Disannidamento per colonna
Tutto il metodo sopra sta parlando del disinnesto verticale e dell'esplosione, se hai bisogno di spendere l'elenco in orizzontale , controlla con il pd.DataFramecostruttore
df.join(pd.DataFrame(df.B.tolist(),index=df.index).add_prefix('B_'))
Out[33]:
A B C B_0 B_1
0 1 [1, 2] [1, 2] 1 2
1 2 [3, 4] [3, 4] 3 4
Funzione aggiornata
def unnesting(df, explode, axis):
if axis==1:
idx = df.index.repeat(df[explode[0]].str.len())
df1 = pd.concat([
pd.DataFrame({x: np.concatenate(df[x].values)}) for x in explode], axis=1)
df1.index = idx
return df1.join(df.drop(explode, 1), how='left')
else :
df1 = pd.concat([
pd.DataFrame(df[x].tolist(), index=df.index).add_prefix(x) for x in explode], axis=1)
return df1.join(df.drop(explode, 1), how='left')
Uscita di prova
unnesting(df, ['B','C'], axis=0)
Out[36]:
B0 B1 C0 C1 A
0 1 2 1 2 1
1 3 4 3 4 2