Als Benutzer mit beiden R
und habe python
ich diese Art von Frage ein paar Mal gesehen.
In R haben sie die eingebaute Funktion aus dem tidyr
aufgerufenen Paket unnest
. In Python
( pandas
) gibt es jedoch keine integrierte Funktion für diese Art von Frage.
Ich weiß, dass object
Spalten es type
immer schwierig machen, Daten mit einer pandas
Funktion zu konvertieren . Als ich die Daten so erhielt, fiel mir als erstes ein, die Spalten zu "glätten" oder zu entfernen.
Ich benutze pandas
und python
Funktionen für diese Art von Frage. Wenn Sie sich Sorgen über die Geschwindigkeit der oben genannten Lösungen machen, überprüfen Sie die Antwort von user3483203, da er verwendet numpy
und die meiste Zeit numpy
schneller ist. Ich empfehle Cpython
und numba
wenn Geschwindigkeit in Ihrem Fall wichtig ist.
Methode 0 [pandas> = 0.25]
Ab pandas 0.25 können Sie die folgende Funktion verwenden , wenn Sie nur eine Spalte auflösen explode
müssen:
df.explode('B')
A B
0 1 1
1 1 2
0 2 1
1 2 2
Methode 1
apply + pd.Series
(leicht zu verstehen, aber in Bezug auf die Leistung nicht empfohlen.)
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
Methode 2
Verwenden repeat
mit DataFrame
Konstruktor, erstellen Sie Ihren Datenrahmen (gut an Leistung, nicht gut an mehreren Spalten)
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
Methode 2.1
zum Beispiel neben A haben wir A.1 ..... An Wenn wir die obige Methode ( Methode 2 ) noch verwenden , ist es für uns schwierig, die Spalten einzeln neu zu erstellen.
Lösung: join
oder merge
mit dem index
Nach 'unnest' die einzelnen Spalten
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
Wenn Sie die Spaltenreihenfolge genau wie zuvor benötigen, fügen Sie sie reindex
am Ende hinzu.
s.join(df.drop('B',1),how='left').reindex(columns=df.columns)
Methode 3
erstellt das neulist
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
Wenn mehr als zwei Spalten vorhanden sind, verwenden Sie
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]
Methode 4
mit reindex
oderloc
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))
Methode 5,
wenn die Liste nur eindeutige Werte enthält:
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
Methode 6
mit numpy
hoher Leistung:
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
Methode 7
mit Basisfunktion itertools
cycle
und chain
: Reine Python-Lösung nur zum Spaß
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
Verallgemeinern auf mehrere Spalten
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]
Self-Def-Funktion:
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
Spaltenweise Unnesting
Alles oben genanntes Verfahren spricht über das vertikale Auseinanderschieben und explodiert, wenn Sie tun müssen , um die Liste aufwenden horizontal , Check mit pd.DataFrame
Konstruktor
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
Aktualisierte Funktion
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')
Testausgabe
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