J'ai un pandas dataframe (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 fractionner toutes les colonnes qui contiennent des n-uplets. Par exemple, je veux remplacer la colonne LCV
par les colonnes LCV-a
et LCV-b
.
Comment puis je faire ça?
Vous pouvez faire cela en faisant pd.DataFrame(col.tolist())
sur cette colonne:
In [2]: df = pd.DataFrame({'a':[1,2], 'b':[(1,2), (3,4)]})
In [3]: df
Out[3]:
a b
0 1 (1, 2)
1 2 (3, 4)
In [4]: df['b'].tolist()
Out[4]: [(1, 2), (3, 4)]
In [5]: pd.DataFrame(df['b'].tolist(), index=df.index)
Out[5]:
0 1
0 1 2
1 3 4
In [6]: df[['b1', 'b2']] = pd.DataFrame(df['b'].tolist(), index=df.index)
In [7]: df
Out[7]:
a b b1 b2
0 1 (1, 2) 1 2
1 2 (3, 4) 3 4
Remarque: dans une version antérieure, cette réponse recommandait d'utiliser df['b'].apply(pd.Series)
au lieu de pd.DataFrame(df['b'].tolist(), index=df.index)
. Cela fonctionne aussi bien (parce que chaque tuple est une série, qui est alors vue comme une ligne d’un cadre de données), mais est plus lent/utilise plus de mémoire que la version tolist
, comme l’indiquent les autres réponses ici. (merci à @denfromufa).
J'ai mis à jour cette réponse pour que la réponse la plus visible soit celle qui convient le mieux.
Sur des jeux de données beaucoup plus volumineux, j’ai constaté que .apply()
est quelques ordres plus lent que pd.DataFrame(df['b'].values.tolist(), index=df.index)
Ce problème de performance a été résolu dans GitHub, bien que je ne sois 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
Je sais que cela a été fait il y a quelque temps, mais une mise en garde de la deuxième solution:
pd.DataFrame(df['b'].values.tolist())
est qu'il va explicitement ignorer l'index et ajouter un index séquentiel par défaut, alors que la réponse acceptée
apply(pd.Series)
ne sera pas, puisque le résultat de apply conservera l'index de ligne. Tandis que l'ordre est initialement conservé de la matrice d'origine, pandas essaiera de faire correspondre les indications des deux cadres de données.
Cela peut être très important si vous essayez de définir les lignes dans un tableau indexé numériquement et que pandas essaiera automatiquement de faire correspondre l'index du nouveau tableau à l'ancien et causera une certaine distorsion dans l'ordre.
Une meilleure solution hybride consisterait à définir l’index du cadre 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 veillant à ce que l'ordre et l'indexation soient conservés sur le résultat.
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
L'accesseur str
disponible pour pandas.Series
objets de dtype == object
est en fait un itératif.
Supposons un 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 en assigner comme d’autres iterables:
var0, var1 = 'xy'
print(var0, var1)
x y
Donc, dans une 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
Seulement un peu plus compliqué, nous pouvons utiliser Zip
pour créer un itérable similaire
df['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
Signification, ne mute pas l'existant df
Cela fonctionne parce que assign
prend des arguments de mot-clé, les mots-clés correspondant aux noms de colonne nouveaux (ou existants) et les valeurs correspondant aux valeurs de la nouvelle colonne. Vous pouvez utiliser un dictionnaire et le décompresser avec **
et le faire agir comme argument de mot clé. Il s’agit donc d’une manière intelligente d’affecter une nouvelle colonne nommée 'g'
c'est le premier élément de la df.col.str
itérable et 'h'
c'est le deuxième élément de la 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
list
Avec une compréhension de liste moderne et un déballage variable.
Note: également en ligne avec 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 en mutation serait
df[['e', 'f']] = pd.DataFrame([*df.col], df.index)
Utilisez celui défini ci-dessus
%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)