2013-03-07 3 views
9

Предположим, у меня есть dataframe следующим образом:Применение различных функций для различных элементов в объекте группы: Python панды

In [1]: test_dup_df 

Out[1]: 
        exe_price exe_vol flag 
2008-03-13 14:41:07 84.5 200  yes 
2008-03-13 14:41:37 85.0 10000 yes 
2008-03-13 14:41:38 84.5 69700 yes 
2008-03-13 14:41:39 84.5 1200 yes 
2008-03-13 14:42:00 84.5 1000 yes 
2008-03-13 14:42:08 84.5 300  yes 
2008-03-13 14:42:10 84.5 88100 yes 
2008-03-13 14:42:10 84.5 11900 yes 
2008-03-13 14:42:15 84.5 5000 yes 
2008-03-13 14:42:16 84.5 3200 yes 

Я хочу, чтобы сгруппировать дубликата данные во время 14:42:10 и применять различные функции exe_price и exe_vol (например, , суммируйте exe_vol и вычислите средневзвешенное значение объема exe_price). Я знаю, что я могу сделать

In [2]: grouped = test_dup_df.groupby(level=0) 

сгруппировать повторяющиеся индексы, а затем использовать first() или last() функции, чтобы получить первый или последний ряд, но это не совсем то, что я хочу.

Есть ли способ группировки, а затем применить различные (написанные мной) функции к значениям в разных столбцах?

+0

это не дало бы мне два отдельных dataframe? Я хотел бы иметь его в одной фрейме данных (аналогично выводу grouped.first() или grouped.last()). Может быть, я что-то пропустил? – kunitomo

ответ

1

Не очень знакомы с pandas, но в чистом NumPy вы могли бы сделать:

tot_vol = np.sum(grouped['exe_vol']) 
avg_price = np.average(grouped['exe_price'], weights=grouped['exe_vol']) 
+0

Благодарим вас за быстрый ответ. Мне интересно, так как мой «сгруппированный» теперь является объектом panda DataFrameGroupBy, я не могу реально применить свою функцию напрямую, не так ли? – kunitomo

+0

Возможно, вы можете сделать это в другой фреймворк данных как [здесь] (http://stackoverflow.com/questions/10373660/converting-a-pandas-groupby-object-to-dataframe) – askewchan

+0

Я вижу. Большое спасибо. – kunitomo

4

Мне нравится ответ @ waitingkuo, потому что это очень ясно и читаемым.

Я все равно придерживаюсь этого, потому что он кажется более быстрым - по крайней мере, с версией Pandas 0.10.0. Ситуация may (hopefully) change in the future, поэтому повторите тест, особенно если вы используете другую версию Pandas.

import pandas as pd 
import io 
import timeit 

data = '''\ 
date time  exe_price exe_vol flag 
2008-03-13 14:41:07 84.5 200  yes 
2008-03-13 14:41:37 85.0 10000 yes 
2008-03-13 14:41:38 84.5 69700 yes 
2008-03-13 14:41:39 84.5 1200 yes 
2008-03-13 14:42:00 84.5 1000 yes 
2008-03-13 14:42:08 84.5 300  yes 
2008-03-13 14:42:10 10 88100 yes 
2008-03-13 14:42:10 100 11900 yes 
2008-03-13 14:42:15 84.5 5000 yes 
2008-03-13 14:42:16 84.5 3200 yes''' 

df = pd.read_table(io.BytesIO(data), sep='\s+', parse_dates=[[0, 1]], 
        index_col=0) 


def func(subf): 
    exe_vol = subf['exe_vol'].sum() 
    exe_price = ((subf['exe_price']*subf['exe_vol']).sum() 
       /exe_vol) 
    flag = True 
    return pd.Series([exe_price, exe_vol, flag], 
        index=['exe_price', 'exe_vol', 'flag']) 
    # return exe_price 

def using_apply(): 
    return df.groupby(df.index).apply(func) 

def using_helper_column(): 
    df['weight'] = df['exe_price'] * df['exe_vol'] 
    grouped = df.groupby(level=0, group_keys=True) 
    result = grouped.agg({'weight': 'sum', 'exe_vol': 'sum'}) 
    result['exe_price'] = result['weight']/result['exe_vol'] 
    result['flag'] = True 
    result = result.drop(['weight'], axis=1) 
    return result 

result = using_apply() 
print(result) 
result = using_helper_column() 
print(result) 

time_apply = timeit.timeit('m.using_apply()', 
         'import __main__ as m ', 
         number=1000) 
time_helper = timeit.timeit('m.using_helper_column()', 
         'import __main__ as m ', 
         number=1000) 
print('using_apply: {t}'.format(t = time_apply)) 
print('using_helper_column: {t}'.format(t = time_helper)) 

дает

     exe_vol exe_price flag 
date_time          
2008-03-13 14:41:07  200  84.50 True 
2008-03-13 14:41:37 10000  85.00 True 
2008-03-13 14:41:38 69700  84.50 True 
2008-03-13 14:41:39  1200  84.50 True 
2008-03-13 14:42:00  1000  84.50 True 
2008-03-13 14:42:08  300  84.50 True 
2008-03-13 14:42:10 100000  20.71 True 
2008-03-13 14:42:15  5000  84.50 True 
2008-03-13 14:42:16  3200  84.50 True 

с timeit контрольных показателей:

using_apply: 3.0081038475 
using_helper_column: 1.35300707817 
+0

Большое спасибо! PS: надеюсь, что создание нового фреймворка не займет много памяти, так как у меня есть 2 миллиона + строка ... – kunitomo

+0

@kunitomo: похоже, что я ошибаюсь - waitingkuo показывает способ объединения на несколько столбцов. – unutbu

+0

Это все еще в два раза быстрее в pandas 0.18 и python 3.4.5. – naught101

12

Применить свою собственную функцию:

In [12]: def func(x): 
      exe_price = (x['exe_price']*x['exe_vol']).sum()/x['exe_vol'].sum() 
      exe_vol = x['exe_vol'].sum() 
      flag = True   
      return Series([exe_price, exe_vol, flag], index=['exe_price', 'exe_vol', 'flag']) 


In [13]: test_dup_df.groupby(test_dup_df.index).apply(func) 
Out[13]: 
        exe_price exe_vol flag 
date_time         
2008-03-13 14:41:07  84.5  200 True 
2008-03-13 14:41:37  85 10000 True 
2008-03-13 14:41:38  84.5 69700 True 
2008-03-13 14:41:39  84.5 1200 True 
2008-03-13 14:42:00  84.5 1000 True 
2008-03-13 14:42:08  84.5  300 True 
2008-03-13 14:42:10  20.71 100000 True 
2008-03-13 14:42:15  84.5 5000 True 
2008-03-13 14:42:16  84.5 3200 True 
+0

Это замечательно! Какова цель 'flag = True'? – unutbu

+0

Эта функция должна возвращать Серию, которая содержит все столбцы, аналогичные оригинальной. Я не знаю, что означает флаг, поэтому просто верните True. – waitingkuo

+0

О, как глупо меня. В любом случае, спасибо за этот ответ. – unutbu

Смежные вопросы