Pandas применить функцию, которая возвращает несколько значений в строки в pandas dataframe

У меня есть dataframe с индексом timeindex и 3 столбцами, содержащими координаты 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

Я хотел бы применить преобразование к каждой строке, которая также возвращает вектор

def myfunc(a, b, c):
    do something
    return e, f, g

но если я это сделаю:

df.apply(myfunc, axis=1)

В итоге я получаю серию Pandas, элементы которой являются кортежами. Это приложение beacause будет принимать результат myfunc без его распаковки. Как я могу изменить myfunc, чтобы получить новый df с тремя столбцами?

Edit:

Все нижеприведенные решения. Решение Series разрешает имена столбцов, похоже, что решение List выполняется быстрее.

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

Ответы

Ответ 1

Просто верните список вместо кортежа.

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

Ответ 2

Верните Series, и он поместит их в DataFrame.

def myfunc(a, b, c):
    do something
    return pd.Series([e, f, g])

У этого есть бонус, который вы можете дать ярлыкам для каждого из полученных столбцов. Если вы вернете DataFrame, он просто вставляет несколько строк для группы.

Ответ 3

Основываясь на отличном ответе на @U2EF1, я создал удобную функцию, которая применяет указанную функцию, которая возвращает кортежи в поле dataframe и расширяет результат вернитесь к фрейму данных.

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)

Использование:

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

Надеюсь, что это поможет кому-то.

Ответ 4

Нашел возможное решение, изменив myfunc, чтобы вернуть np.array следующим образом:

import numpy as np

def myfunc(a, b, c):
    do something
    return np.array((e, f, g))

любое лучшее решение?