J'ai un dataframe pandas (ce n'est qu'un petit morceau)
>>> d1
y norm test y norm train len(y_train) len(y_test) \
0 64.904368 116.151232 1645 549
1 70.852681 112.639876 1645 549
SVR RBF \
0 (35.652207342877873, 22.95533537448393)
1 (39.563683797747622, 27.382483096332511)
LCV \
0 (19.365430594452338, 13.880062435173587)
1 (19.099614489458364, 14.018867136617146)
RIDGE CV \
0 (4.2907610988480362, 12.416745648065584)
1 (4.18864306788194, 12.980833914392477)
RF \
0 (9.9484841581029428, 16.46902345373697)
1 (10.139848213735391, 16.282141345406522)
GB \
0 (0.012816232716538605, 15.950164822266007)
1 (0.012814519804493328, 15.305745202851712)
ET DATA
0 (0.00034337162272515505, 16.284800366214057) j2m
1 (0.00024811554516431878, 15.556506191784194) j2m
>>>
Je veux diviser toutes les colonnes contenant des tuples. Par exemple, je souhaite remplacer la colonne LCV
par les colonnes LCV-a
et LCV-b
.
Comment puis je faire ça?
Sur des ensembles de données beaucoup plus volumineux, j'ai trouvé que
.apply()
quelques commandes étaient plus lentes quepd.DataFrame(df['b'].values.tolist(), index=df.index)
Ce problème de performances a été résolu dans GitHub, même si je ne suis pas d'accord avec cette décision:
https://github.com/pandas-dev/pandas/issues/11615
EDIT: basé sur cette réponse: https://stackoverflow.com/a/44196843/2230844
la source
pd.DataFrame(df['b'].tolist())
sans le.values
semble fonctionner très bien aussi. (Et merci, votre solution est beaucoup plus rapide que.apply()
)L'
str
accesseur qui est disponible pour lespandas.Series
objets dedtype == object
est en fait un itérable.Supposons que
pandas.DataFrame
df
:df = pd.DataFrame(dict(col=[*zip('abcdefghij', range(10, 101, 10))])) df col 0 (a, 10) 1 (b, 20) 2 (c, 30) 3 (d, 40) 4 (e, 50) 5 (f, 60) 6 (g, 70) 7 (h, 80) 8 (i, 90) 9 (j, 100)
Nous pouvons tester si c'est un itérable
from collections import Iterable isinstance(df.col.str, Iterable) True
Nous pouvons ensuite l'assigner comme nous le faisons pour d'autres itérables:
var0, var1 = 'xy' print(var0, var1) x y
La solution la plus simple
Donc, en une seule ligne, nous pouvons affecter les deux colonnes
df['a'], df['b'] = df.col.str df col a b 0 (a, 10) a 10 1 (b, 20) b 20 2 (c, 30) c 30 3 (d, 40) d 40 4 (e, 50) e 50 5 (f, 60) f 60 6 (g, 70) g 70 7 (h, 80) h 80 8 (i, 90) i 90 9 (j, 100) j 100
Solution plus rapide
Seulement un peu plus compliqué, nous pouvons utiliser
zip
pour créer un itérable similairedf['c'], df['d'] = zip(*df.col) df col a b c d 0 (a, 10) a 10 a 10 1 (b, 20) b 20 b 20 2 (c, 30) c 30 c 30 3 (d, 40) d 40 d 40 4 (e, 50) e 50 e 50 5 (f, 60) f 60 f 60 6 (g, 70) g 70 g 70 7 (h, 80) h 80 h 80 8 (i, 90) i 90 i 90 9 (j, 100) j 100 j 100
En ligne
Signification, ne pas muter l'existant
df
Cela fonctionne car
assign
prend des arguments de mot-clé où les mots-clés sont les noms de colonne nouveaux (ou existants) et les valeurs seront les valeurs de la nouvelle colonne. Vous pouvez utiliser un dictionnaire et le décompresser avec**
et le faire agir comme arguments de mot-clé. C'est donc une façon intelligente d'attribuer une nouvelle colonne nommée'g'
qui est le premier élément de l'df.col.str
itérable et'h'
qui est le deuxième élément de l'df.col.str
itérable.df.assign(**dict(zip('gh', df.col.str))) col g h 0 (a, 10) a 10 1 (b, 20) b 20 2 (c, 30) c 30 3 (d, 40) d 40 4 (e, 50) e 50 5 (f, 60) f 60 6 (g, 70) g 70 7 (h, 80) h 80 8 (i, 90) i 90 9 (j, 100) j 100
Ma version de l'
list
approcheAvec une compréhension de liste moderne et un déballage variable.
Remarque: également en ligne en utilisant
join
df.join(pd.DataFrame([*df.col], df.index, [*'ef'])) col g h 0 (a, 10) a 10 1 (b, 20) b 20 2 (c, 30) c 30 3 (d, 40) d 40 4 (e, 50) e 50 5 (f, 60) f 60 6 (g, 70) g 70 7 (h, 80) h 80 8 (i, 90) i 90 9 (j, 100) j 100
La version mutante serait
df[['e', 'f']] = pd.DataFrame([*df.col], df.index)
Test de temps naïf
DataFrame courtUtilisez celui défini ci-dessus
Long DataFrame%timeit df.assign(**dict(zip('gh', df.col.str))) %timeit df.assign(**dict(zip('gh', zip(*df.col)))) %timeit df.join(pd.DataFrame([*df.col], df.index, [*'gh'])) 1.16 ms ± 21.5 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each) 635 µs ± 18.7 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each) 795 µs ± 42.5 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
10 ^ 3 fois plus grand
df = pd.concat([df] * 1000, ignore_index=True) %timeit df.assign(**dict(zip('gh', df.col.str))) %timeit df.assign(**dict(zip('gh', zip(*df.col)))) %timeit df.join(pd.DataFrame([*df.col], df.index, [*'gh'])) 11.4 ms ± 1.53 ms per loop (mean ± std. dev. of 7 runs, 100 loops each) 2.1 ms ± 41.4 µs per loop (mean ± std. dev. of 7 runs, 100 loops each) 2.33 ms ± 35.1 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
la source
df['a'], df['b'] = df.col.str
:)Je pense qu'un moyen plus simple est:
>>> import pandas as pd >>> df = pd.DataFrame({'a':[1,2], 'b':[(1,2), (3,4)]}) >>> df a b 0 1 (1, 2) 1 2 (3, 4) >>> df['b_a']=df['b'].str[0] >>> df['b_b']=df['b'].str[1] >>> df a b b_a b_b 0 1 (1, 2) 1 2 1 2 (3, 4) 3 4
la source
str
représentation d'unpd.Series
objet. Pouvez-vous expliquer comment cela fonctionne?!Je sais que cela date d'il y a quelque temps, mais une mise en garde concernant la deuxième solution:
pd.DataFrame(df['b'].values.tolist())
est qu'il supprimera explicitement l'index et ajoutera un index séquentiel par défaut, alors que la réponse acceptée
ne le sera pas, car le résultat de apply conservera l'index de ligne. Alors que l'ordre est initialement conservé à partir du tableau d'origine, les pandas essaieront de faire correspondre les indices des deux dataframes.
Cela peut être très important si vous essayez de définir les lignes dans un tableau indexé numériquement, et les pandas essaieront automatiquement de faire correspondre l'index du nouveau tableau à l'ancien, et provoqueront une certaine distorsion dans l'ordre.
Une meilleure solution hybride consisterait à définir l'indice de la trame de données d'origine sur le nouveau, c'est-à-dire
pd.DataFrame(df['b'].values.tolist(), index=df.index)
Ce qui conservera la rapidité d'utilisation de la deuxième méthode tout en garantissant que l'ordre et l'indexation sont conservés sur le résultat.
la source