2013-03-07 37 views
9

Giả sử tôi có một dataframe như sau:Áp dụng các chức năng khác nhau đến các mục khác nhau trong nhóm đối tượng: Python gấu trúc

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 

Tôi muốn nhóm một dữ liệu trùng lặp lúc 14:42:10 và áp dụng các chức năng khác nhau để exe_priceexe_vol (ví dụ , cộng số exe_vol và tính trung bình theo khối lượng là exe_price). Tôi biết rằng tôi có thể làm

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

vào nhóm các chỉ số trùng lặp và sau đó sử dụng first() hoặc last() chức năng để có được một trong hai người đầu tiên hoặc hàng cuối cùng nhưng điều này là không thực sự những gì tôi muốn.

Có cách nào để nhóm và sau đó áp dụng các hàm khác nhau (được viết bởi tôi) cho các giá trị trong cột khác nhau không?

+0

Này sẽ không cho tôi hai dataframe riêng biệt? Tôi muốn có nó trong một dataframe (tương tự như một đầu ra của grouped.first() hoặc grouped.last()). Tôi có thể thiếu một cái gì đó ?? – kunitomo

Trả lời

1

Không khủng khiếp quen thuộc với pandas, nhưng trong tinh khiết NumPy bạn có thể làm:

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

Cảm ơn bạn đã phản hồi nhanh chóng. Tôi tự hỏi vì 'nhóm' của tôi bây giờ là một đối tượng DataFrameGroupBy gấu trúc, tôi thực sự không thể áp dụng trực tiếp fucntion của bạn? – kunitomo

+0

Có lẽ bạn có thể biến nó thành một khung dữ liệu khác như [ở đây] (http://stackoverflow.com/questions/10373660/converting-a-pandas-groupby-object-to-dataframe) – askewchan

+0

Tôi hiểu. Cảm ơn nhiều. – kunitomo

4

Tôi thích câu trả lời @ waitingkuo vì nó là rất rõ ràng và dễ đọc.

Tôi vẫn giữ nguyên điều này vì nó có vẻ nhanh hơn - ít nhất là với phiên bản Pandas 0.10.0. Tình huống may (hopefully) change in the future, vì vậy hãy đảm bảo chạy lại điểm chuẩn đặc biệt nếu bạn đang sử dụng phiên bản Pandas khác.

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)) 

mang

     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 

với tiêu chuẩn timeit của:

using_apply: 3.0081038475 
using_helper_column: 1.35300707817 
+0

Cảm ơn bạn rất nhiều! PS: hy vọng tạo ra một dataframe mới sẽ không mất nhiều bộ nhớ kể từ khi tôi có 2 triệu + hàng ... – kunitomo

+0

@ kunitomo: Có vẻ như tôi sai - waitkuo cho thấy một cách để tổng hợp trên nhiều cột. – unutbu

+0

Điều này vẫn nhanh gấp hai lần trong gấu trúc 0,18 và trăn 3.4.5. – naught101

12

Áp dụng chức năng riêng của bạn:

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

Điều này thật tuyệt! Mục đích của 'flag = True' là gì? – unutbu

+0

Chức năng đó sẽ trả về một Chuỗi chứa tất cả các cột giống như cột gốc. Tôi không biết cờ nghĩa là gì, vì vậy chỉ cần trả về True. – waitingkuo

+0

Ồ, thật ngớ ngẩn của tôi. Dù sao, cảm ơn bạn cho câu trả lời này. – unutbu

Các vấn đề liên quan