Je sais que les object
colonnes type
rendent les données difficiles à convertir avec une pandas
fonction. Lorsque j'ai reçu des données comme celles-ci, la première chose qui m'est venue à l'esprit a été d '«aplatir» ou de désnoisonner les colonnes.
J'utilise pandas
et des python
fonctions pour ce type de question. Si vous vous inquiétez de la vitesse des solutions ci-dessus, consultez la réponse de user3483203 , car il utilise numpy
et la plupart du temps numpy
est plus rapide. Je recommande Cpython
et numba
si la vitesse compte.
Méthode 0 [pandas> = 0.25]
À partir de pandas 0.25 , si vous n'avez besoin d'exploser qu'une colonne, vous pouvez utiliser la pandas.DataFrame.explode
fonction:
df.explode('B')
A B
0 1 1
1 1 2
0 2 1
1 2 2
Étant donné un dataframe avec un vide list
ou un NaN
dans la colonne. Une liste vide ne causera pas de problème, mais un NaN
devra être rempli avec unlist
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
Méthode 1
apply + pd.Series
(facile à comprendre mais en termes de performances déconseillée.)
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
Méthode 2
En utilisant repeat
avec le DataFrame
constructeur, recréez votre dataframe (bon pour les performances, pas bon pour plusieurs colonnes)
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
Par
exemple, la méthode 2.1 en plus de A, nous avons A.1 ..... An Si nous utilisons toujours la méthode ( Méthode 2 ) ci-dessus, il nous est difficile de recréer les colonnes une par une.
Solution: join
ou merge
avec la index
suite 'unnest' les colonnes simples
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
Si vous avez besoin que l'ordre des colonnes soit exactement le même qu'avant, ajoutez reindex
à la fin.
s.join(df.drop('B',1),how='left').reindex(columns=df.columns)
Méthode 3
recréer lelist
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
Si plus de deux colonnes, utilisez
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]
Méthode 4 en
utilisant reindex
ouloc
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))
Méthode 5
lorsque la liste ne contient que des valeurs uniques:
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
Méthode 6
utilisant numpy
pour de hautes performances:
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
Méthode 7
utilisant la fonction de base itertools
cycle
et chain
: Solution python pure juste pour le plaisir
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
Généralisation à plusieurs colonnes
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]
Fonction self-def:
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
Désincorporation par colonne
Toute la méthode ci-dessus parle du désemboîtement vertical et de l'explosion.Si vous avez besoin de dépenser la liste horizontalement , vérifiez avec le pd.DataFrame
constructeur
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
Fonction mise à jour
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')
Sortie de test
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