Pandas: créer deux nouvelles colonnes dans une dataframe avec des valeurs calculées à partir d'une colonne préexistante
je travaille avec la bibliothèque pandas et je veux ajouter deux nouvelles colonnes à une dataframe df
avec n colonnes (n > 0).
Ces nouvelles colonnes résultat de l'application d'une fonction à l'une des colonnes dans le dataframe.
la fonction à appliquer est comme:
def calculate(x):
...operate...
return z, y
Une méthode pour la création d'une nouvelle colonne pour une fonction retournant seulement une valeur est:
df['new_col']) = df['column_A'].map(a_function)
donc, ce que je veux, et essayé sans succès ( * ), est quelque chose comme:
(df['new_col_zetas'], df['new_col_ys']) = df['column_A'].map(calculate)
Quelle est la meilleure façon d'y parvenir ? J'ai scanné la documentation sans aucune idée.
* * df['column_A'].map(calculate)
renvoie une série de pandas composée d'un tuple z, Y. Et essayer d'attribuer ceci à deux colonnes de dataframe produit une Valeurerror.*
2 réponses
Je n'utiliserais que zip
:
In [1]: from pandas import *
In [2]: def calculate(x):
...: return x*2, x*3
...:
In [3]: df = DataFrame({'a': [1,2,3], 'b': [2,3,4]})
In [4]: df
Out[4]:
a b
0 1 2
1 2 3
2 3 4
In [5]: df["A1"], df["A2"] = zip(*df["a"].map(calculate))
In [6]: df
Out[6]:
a b A1 A2
0 1 2 2 3
1 2 3 4 6
2 3 4 6 9
La première réponse est erronée à mon avis. Heureusement, personne n'importe en masse tous les pandas dans leur espace de nom avec from pandas import *
. En outre, la méthode map
devrait être réservée pour ces moments lors de la transmission d'un dictionnaire ou D'une série. Il peut prendre une fonction, mais c'est ce que apply
est utilisé pour.
donc, si vous devez utiliser l'approche ci-dessus, je l'écrirais comme ceci
df["A1"], df["A2"] = zip(*df["a"].apply(calculate))
il n'y a en fait aucune raison d'utiliser zip ici. Vous pouvez simplement faire ceci:
df["A1"], df["A2"] = calculate(df['a'])
cette seconde méthode est également beaucoup plus rapide sur les plus grandes images de données
df = pd.DataFrame({'a': [1,2,3] * 100000, 'b': [2,3,4] * 100000})
DataFrame created with rows 300,000
%timeit df["A1"], df["A2"] = calculate(df['a'])
2.65 ms ± 92.4 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
%timeit df["A1"], df["A2"] = zip(*df["a"].apply(calculate))
159 ms ± 5.24 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
60x plus rapide que zip
en général, éviter d'utiliser Appliquer
Apply N'est généralement pas beaucoup plus rapide qu'une itération sur une liste Python. Nous allons tester la performance d'un pour la boucle pour faire la même chose que ci-dessus
%%timeit
A1, A2 = [], []
for val in df['a']:
A1.append(val**2)
A2.append(val**3)
df['A1'] = A1
df['A2'] = A2
298 ms ± 7.14 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
donc c'est deux fois plus lent ce qui n'est pas une terrible régression de performance, mais si nous cythonisons ce qui précède, nous obtenons une bien meilleure performance. En supposant que vous utilisez ipython:
%load_ext cython
%%cython
cpdef power(vals):
A1, A2 = [], []
cdef double val
for val in vals:
A1.append(val**2)
A2.append(val**3)
return A1, A2
%timeit df['A1'], df['A2'] = power(df['a'])
72.7 ms ± 2.16 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
attribuer directement sans objet
vous pouvez obtenir encore plus d'améliorations de vitesse si vous utilisez les opérations vectorisées directes.
%timeit df['A1'], df['A2'] = df['a'] ** 2, df['a'] ** 3
5.13 ms ± 320 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
ceci profite des opérations vectorisées extrêmement rapides de num Py au lieu de nos boucles. Nous avons maintenant une accélération de 30x par rapport à l'original.
"l'essai de vitesse le plus simple avec apply
L'exemple ci-dessus devrait clairement montrer comment ralentir apply
peut être, mais juste pour son extra-clair, regardons l'exemple le plus simple. Une série de 10 millions de nombres avec et sans application
s = pd.Series(np.random.rand(10000000))
%timeit s.apply(calc)
3.3 s ± 57.4 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
sans application est 50x plus rapide
%timeit s ** 2
66 ms ± 2 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)