J'ai un dataframe avec un timeindex et 3 colonnes contenant les coordonnées d'un vecteur 3D:
x y z
ts
2014-05-15 10:38 0.120117 0.987305 0.116211
2014-05-15 10:39 0.117188 0.984375 0.122070
2014-05-15 10:40 0.119141 0.987305 0.119141
2014-05-15 10:41 0.116211 0.984375 0.120117
2014-05-15 10:42 0.119141 0.983398 0.118164
Je voudrais appliquer une transformation à chaque ligne qui renvoie également un vecteur
def myfunc(a, b, c):
do something
return e, f, g
mais si je le fais:
df.apply(myfunc, axis=1)
Je me retrouve avec une série Pandas dont les éléments sont des tuples. C'est parce qu'appliquer prendra le résultat de myfunc sans le décompresser. Comment puis-je changer myfunc pour obtenir un nouveau df avec 3 colonnes ?
Modifier:
Toutes les solutions ci-dessous fonctionnent. La solution Series autorise les noms de colonnes, la solution List semble s'exécuter plus rapidement.
def myfunc1(args):
e=args[0] + 2*args[1]
f=args[1]*args[2] +1
g=args[2] + args[0] * args[1]
return pd.Series([e,f,g], index=['a', 'b', 'c'])
def myfunc2(args):
e=args[0] + 2*args[1]
f=args[1]*args[2] +1
g=args[2] + args[0] * args[1]
return [e,f,g]
%timeit df.apply(myfunc1 ,axis=1)
100 loops, best of 3: 4.51 ms per loop
%timeit df.apply(myfunc2 ,axis=1)
100 loops, best of 3: 2.75 ms per loop
Renvoyez simplement une liste au lieu de Tuple.
In [81]: df
Out[81]:
x y z
ts
2014-05-15 10:38:00 0.120117 0.987305 0.116211
2014-05-15 10:39:00 0.117188 0.984375 0.122070
2014-05-15 10:40:00 0.119141 0.987305 0.119141
2014-05-15 10:41:00 0.116211 0.984375 0.120117
2014-05-15 10:42:00 0.119141 0.983398 0.118164
[5 rows x 3 columns]
In [82]: def myfunc(args):
....: e=args[0] + 2*args[1]
....: f=args[1]*args[2] +1
....: g=args[2] + args[0] * args[1]
....: return [e,f,g]
....:
In [83]: df.apply(myfunc ,axis=1)
Out[83]:
x y z
ts
2014-05-15 10:38:00 2.094727 1.114736 0.234803
2014-05-15 10:39:00 2.085938 1.120163 0.237427
2014-05-15 10:40:00 2.093751 1.117629 0.236770
2014-05-15 10:41:00 2.084961 1.118240 0.234512
2014-05-15 10:42:00 2.085937 1.116202 0.235327
Retournez Series
et il les mettra dans un DataFrame.
def myfunc(a, b, c):
do something
return pd.Series([e, f, g])
Cela a le bonus que vous pouvez donner des étiquettes à chacune des colonnes résultantes. Si vous renvoyez un DataFrame, il insère simplement plusieurs lignes pour le groupe.
Sur la base de l'excellent réponse de @ U2EF1, j'ai créé une fonction pratique qui applique une fonction spécifiée qui renvoie des tuples à un champ de trame de données et développe le résultat dans la trame de données.
def apply_and_concat(dataframe, field, func, column_names):
return pd.concat((
dataframe,
dataframe[field].apply(
lambda cell: pd.Series(func(cell), index=column_names))), axis=1)
Usage:
df = pd.DataFrame([1, 2, 3], index=['a', 'b', 'c'], columns=['A'])
print df
A
a 1
b 2
c 3
def func(x):
return x*x, x*x*x
print apply_and_concat(df, 'A', func, ['x^2', 'x^3'])
A x^2 x^3
a 1 1 1
b 2 4 8
c 3 9 27
J'espère que cela aide quelqu'un.
Trouvé une solution possible, en changeant myfunc pour retourner un np.array comme ceci:
import numpy as np
def myfunc(a, b, c):
do something
return np.array((e, f, g))
une meilleure solution?