J'ai un dataframe où certaines cellules contiennent des listes de plusieurs valeurs. Plutôt que de stocker plusieurs valeurs dans une cellule, j'aimerais étendre le dataframe afin que chaque élément de la liste ait sa propre ligne (avec les mêmes valeurs dans toutes les autres colonnes). Donc si j'ai:
import pandas as pd
import numpy as np
df = pd.DataFrame(
{'trial_num': [1, 2, 3, 1, 2, 3],
'subject': [1, 1, 1, 2, 2, 2],
'samples': [list(np.random.randn(3).round(2)) for i in range(6)]
}
)
df
Out[10]:
samples subject trial_num
0 [0.57, -0.83, 1.44] 1 1
1 [-0.01, 1.13, 0.36] 1 2
2 [1.18, -1.46, -0.94] 1 3
3 [-0.08, -4.22, -2.05] 2 1
4 [0.72, 0.79, 0.53] 2 2
5 [0.4, -0.32, -0.13] 2 3
Comment puis-je convertir en forme longue, par exemple:
subject trial_num sample sample_num
0 1 1 0.57 0
1 1 1 -0.83 1
2 1 1 1.44 2
3 1 2 -0.01 0
4 1 2 1.13 1
5 1 2 0.36 2
6 1 3 1.18 0
# etc.
L'index n'est pas important, vous pouvez définir les colonnes existantes comme index et le classement final n'est pas important.
df.explode('samples')
pour résoudre ce problème.explode
ne peut supporter que l'explosion d'une colonne pour le moment.Réponses:
Résultat:
PS ici, vous pouvez trouver une solution un peu plus générique
MISE À JOUR: quelques explications: IMO, le moyen le plus simple de comprendre ce code est d'essayer de l'exécuter étape par étape:
dans la ligne suivante, nous répétons les valeurs dans une colonne
N
fois oùN
- est la longueur de la liste correspondante:cela peut être généralisé pour toutes les colonnes, contenant des valeurs scalaires:
en utilisant,
np.concatenate()
nous pouvons aplatir toutes les valeurs de lalist
colonne (samples
) et obtenir un vecteur 1D:mettre tout cela ensemble:
L'utilisation
pd.DataFrame()[df.columns]
garantit que nous sélectionnons les colonnes dans l'ordre d'origine ...la source
lst_col
entièrement les lignes qui ont une liste vide ; pour conserver ces lignes et les remplirlst_col
avecnp.nan
, vous pouvez simplement le fairedf[lst_col] = df[lst_col].apply(lambda x: x if len(x) > 0 else [np.nan])
avant d'utiliser cette méthode. Évidemment.mask
, ne renverra pas de listes, d'où le.apply
.Un peu plus longtemps que prévu:
Si vous voulez un index séquentiel, vous pouvez appliquer
reset_index(drop=True)
au résultat.mise à jour :
la source
df.apply(lambda x: pd.Series(x['samples']),axis=1)
pardf.samples.apply(pd.Series)
.df.explode()
comme indiqué ici.Pandas> = 0,25
Les méthodes Series et DataFrame définissent une
.explode()
méthode qui décompose les listes en lignes séparées. Consultez la section de documentation sur l' explosion d'une colonne de type liste .Notez que cela gère également les colonnes mixtes de listes et de scalaires, ainsi que les listes vides et les NaN de manière appropriée (c'est un inconvénient des
repeat
solutions basées sur la base de données).Cependant, vous devez noter que cela
explode
ne fonctionne que sur une seule colonne (pour le moment).PS: si vous cherchez à exploser une colonne de chaînes , vous devez d'abord diviser sur un séparateur, puis utiliser
explode
. Voir cette réponse (très) connexe de moi.la source
vous pouvez également utiliser
pd.concat
etpd.melt
pour cela:Enfin, si vous avez besoin, vous pouvez trier en fonction des trois premières colonnes.
la source
En essayant de travailler pas à pas sur la solution de Roman Pekar pour mieux la comprendre, j'ai proposé ma propre solution, qui permet
melt
d'éviter une partie de l'empilement déroutant et de la réinitialisation d'index. Je ne peux pas dire que c'est évidemment une solution plus claire:Sortie (évidemment, nous pouvons supprimer la colonne d'échantillons d'origine maintenant):
la source
Pour ceux qui recherchent une version de la réponse de Roman Pekar qui évite la dénomination manuelle des colonnes:
la source
J'ai trouvé que le moyen le plus simple était de:
samples
colonne en DataFrameMontré ici:
Il convient de noter que cela n'a peut-être fonctionné que parce que chaque essai a le même nombre d'échantillons (3). Quelque chose de plus intelligent peut être nécessaire pour les essais de différentes tailles d'échantillons.
la source
Réponse très tardive mais je veux ajouter ceci:
Une solution rapide utilisant vanilla Python qui prend également en charge la
sample_num
colonne dans l'exemple d'OP. Sur mon propre grand ensemble de données avec plus de 10 millions de lignes et un résultat avec 28 millions de lignes, cela ne prend que 38 secondes environ. La solution acceptée se décompose complètement avec cette quantité de données et conduit à unmemory error
sur mon système qui a 128 Go de RAM.la source
Aussi très tard, mais voici une réponse de Karvy1 qui a bien fonctionné pour moi si vous n'avez pas de pandas> = version 0.25: https://stackoverflow.com/a/52511166/10740287
Pour l'exemple ci-dessus, vous pouvez écrire:
Test de rapidité:
1,33 ms ± 74,8 µs par boucle (moyenne ± écart type de 7 analyses, 1000 boucles chacune)
4,9 ms ± 189 µs par boucle (moyenne ± écart type de 7 analyses, 100 boucles chacune)
1,38 ms ± 25 µs par boucle (moyenne ± écart type de 7 analyses, 1000 boucles chacune)
la source
Essayez ceci dans les pandas> = version 0.25
la source
.str.split(',')
carPrices
c'est déjà une liste.