Wiem, że objectkolumny typeutrudniają konwersję danych za pomocą pandasfunkcji. Kiedy otrzymałem takie dane, pierwszą rzeczą, która przyszła mi do głowy, było „spłaszczenie” lub odkształcenie kolumn.
Używam pandasi pythonfunkcji dla tego typu pytań. Jeśli martwisz się o szybkość powyższych rozwiązań, sprawdź odpowiedź użytkownika3483203 , ponieważ używa numpyi przez większość czasu numpyjest szybsza. Polecam Cpythoni numbajeśli liczy się szybkość.
Metoda 0 [pandy> = 0,25]
Zaczynając od pand 0,25 , jeśli chcesz rozbić tylko jedną kolumnę, możesz użyć pandas.DataFrame.explodefunkcji:
df.explode('B')
A B
0 1 1
1 1 2
0 2 1
1 2 2
Biorąc pod uwagę ramkę danych z pustą listlub NaNw kolumnie. Pusta lista nie spowoduje problemu, ale NaNwolę należy wypełnićlist
df = pd.DataFrame({'A': [1, 2, 3, 4],'B': [[1, 2], [1, 2], [], np.nan]})
df.B = df.B.fillna({i: [] for i in df.index}) # replace NaN with []
df.explode('B')
A B
0 1 1
0 1 2
1 2 1
1 2 2
2 3 NaN
3 4 NaN
Metoda 1
apply + pd.Series (łatwa do zrozumienia, ale pod względem wydajności nie jest zalecana).
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
Metoda 2
Używając repeatz DataFramekonstruktorem, ponownie utwórz ramkę danych (dobra pod względem wydajności, niezbyt dobra w wielu kolumnach)
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
Na przykład metoda 2.1 oprócz A mamy A.1 ..... An Jeśli nadal używamy powyższej metody ( Metoda 2 ), trudno jest nam odtworzyć kolumny jedna po drugiej.
Rozwiązanie: joinalbo mergez indexpo „unnest” pojedynczych kolumn
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
Jeśli chcesz, aby kolejność kolumn była dokładnie taka sama jak poprzednio, dodaj reindexna końcu.
s.join(df.drop('B',1),how='left').reindex(columns=df.columns)
Metoda 3:
Odtwórz pliklist
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
Jeśli więcej niż dwie kolumny, użyj
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]
Metoda 4
przy użyciu reindex lubloc
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))
Metoda 5,
gdy lista zawiera tylko unikalne wartości:
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
Metoda 6
stosując numpyo wysokiej wydajności:
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
Metoda 7
wykorzystująca funkcję podstawową itertools cyclei chain: Czyste rozwiązanie w Pythonie dla zabawy
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
Uogólnianie na wiele kolumn
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]
Funkcja samoobrony:
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
Unnesting kolumnowy
Cała powyższa metoda mówi o pionowym rozebraniu i eksplozji.Jeśli potrzebujesz rozłożyć listę w poziomie , Sprawdź pd.DataFramekonstruktorem
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
Zaktualizowana funkcja
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')
Wyjście testowe
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