Nhiều tổng hợp của cùng một cột bằng cách sử dụng pandas GroupBy.agg ()


128

Có cách nào được tích hợp sẵn cho gấu trúc để áp dụng hai hàm tổng hợp khác nhau f1, f2cho cùng một cột df["returns"]mà không cần phải gọi agg()nhiều lần không?

Khung dữ liệu mẫu:

import pandas as pd
import datetime as dt

pd.np.random.seed(0)
df = pd.DataFrame({
         "date"    :  [dt.date(2012, x, 1) for x in range(1, 11)], 
         "returns" :  0.05 * np.random.randn(10), 
         "dummy"   :  np.repeat(1, 10)
}) 

Cách làm sai về mặt cú pháp nhưng đúng về mặt trực giác, sẽ là:

# Assume `f1` and `f2` are defined for aggregating.
df.groupby("dummy").agg({"returns": f1, "returns": f2})

Rõ ràng, Python không cho phép các khóa trùng lặp. Có cách nào khác để thể hiện đầu vào agg()không? Có lẽ một danh sách các bộ giá trị [(column, function)]sẽ hoạt động tốt hơn, để cho phép nhiều hàm được áp dụng cho cùng một cột? Nhưng agg()có vẻ như nó chỉ chấp nhận một từ điển.

Có giải pháp nào cho việc này ngoài việc xác định một chức năng phụ trợ chỉ áp dụng cả hai chức năng bên trong của nó không? (Dù sao thì điều này sẽ hoạt động với tổng hợp?)



2
Từ 0,25 trở đi, pandas cung cấp cú pháp trực quan hơn cho nhiều tổng hợp, cũng như đổi tên các cột đầu ra. Xem tài liệu về Tổng hợp được đặt tên .
cs95

FYI câu hỏi này đã được hỏi ngược lại trên gấu trúc 0.8.x vào 9/2012
smci 9/12/19

1
FYI, câu trả lời được chấp nhận cũng không được chấp nhận - đừng chuyển agg () a dict of dicts.
cs95

@ cs95: Tôi biết nó không được dùng nữa, tôi nói rằng SO đang trở nên ngổn ngang với các giải pháp cũ cũ từ các phiên bản cũ. SO không có cách nào để đánh dấu điều đó - ngoài nhận xét.
smci

Câu trả lời:


159

Bạn chỉ cần chuyển các hàm dưới dạng danh sách:

In [20]: df.groupby("dummy").agg({"returns": [np.mean, np.sum]})
Out[20]:         
           mean       sum
dummy                    
1      0.036901  0.369012

hoặc như một từ điển:

In [21]: df.groupby('dummy').agg({'returns':
                                  {'Mean': np.mean, 'Sum': np.sum}})
Out[21]: 
        returns          
           Mean       Sum
dummy                    
1      0.036901  0.369012

5
Có cách nào để chỉ định tên cột kết quả không?
Ben

3
@Ben Tôi nghĩ bạn phải đổi tên sau đó. Chẳng hạn bởi Tom Augspurger (xem ô 25)
Stewbaca

1
@Ben: Tôi đã thêm một ví dụ
bmu

10
@sparc_spread Truyền nhiều chức năng dưới dạng danh sách được mô tả kỹ trong tài liệu về gấu trúc . Việc đổi tên và chuyển nhiều hàm làm từ điển sẽ không còn được dùng trong phiên bản gấu trúc trong tương lai. Chi tiết có trong nhật ký thay đổi 0,20 , mà tôi cũng đã tóm tắt ở những nơi khác trên SO .
joelostblom

3
Nó đã được nói rồi, nhưng việc sử dụng từ điển để đổi tên các cột đầu ra theo độ tuổi không còn được dùng nữa. Thay vào đó, bạn có thể chỉ định một danh sách các bộ giá trị. Xem câu trả lời này.
cs95

101

TLDR; Pandas groupby.aggcó một cú pháp mới, dễ dàng hơn để chỉ định (1) tổng hợp trên nhiều cột và (2) nhiều tổng hợp trên một cột. Vì vậy, để làm điều này cho gấu trúc> = 0,25 , hãy sử dụng

df.groupby('dummy').agg(Mean=('returns', 'mean'), Sum=('returns', 'sum'))

           Mean       Sum
dummy                    
1      0.036901  0.369012

HOẶC LÀ

df.groupby('dummy')['returns'].agg(Mean='mean', Sum='sum')

           Mean       Sum
dummy                    
1      0.036901  0.369012

Gấu trúc> = 0,25: Tổ hợp được đặt tên

Pandas đã thay đổi hành vi GroupBy.aggủng hộ một cú pháp trực quan hơn để chỉ định các tập hợp được đặt tên. Xem phần 0,25 tài liệu về Cải tiến cũng như các vấn đề GitHub có liên quan GH18366GH26512 .

Từ tài liệu,

Để hỗ trợ tổng hợp theo từng cột cụ thể với quyền kiểm soát tên cột đầu ra, gấu trúc chấp nhận cú pháp đặc biệt trong GroupBy.agg(), được gọi là "tập hợp được đặt tên", trong đó

  • Các từ khóa là tên cột đầu ra
  • Các giá trị là các bộ giá trị có phần tử đầu tiên là cột để chọn và phần tử thứ hai là tập hợp để áp dụng cho cột đó. Pandas cung cấp cho pandas.NamedAgg nametuple với các trường ['column', 'aggfunc'] để làm rõ ràng hơn đối số là gì. Như thường lệ, tập hợp có thể là một bí danh có thể gọi hoặc một chuỗi.

Bây giờ bạn có thể chuyển một tuple thông qua các đối số từ khóa. Các bộ giá trị tuân theo định dạng của (<colName>, <aggFunc>).

import pandas as pd

pd.__version__                                                                                                                            
# '0.25.0.dev0+840.g989f912ee'

# Setup
df = pd.DataFrame({'kind': ['cat', 'dog', 'cat', 'dog'],
                   'height': [9.1, 6.0, 9.5, 34.0],
                   'weight': [7.9, 7.5, 9.9, 198.0]
})

df.groupby('kind').agg(
    max_height=('height', 'max'), min_weight=('weight', 'min'),)

      max_height  min_weight
kind                        
cat          9.5         7.9
dog         34.0         7.5

Ngoài ra, bạn có thể sử dụng pd.NamedAgg(về cơ bản là một tập hợp có tên) để làm cho mọi thứ rõ ràng hơn.

df.groupby('kind').agg(
    max_height=pd.NamedAgg(column='height', aggfunc='max'), 
    min_weight=pd.NamedAgg(column='weight', aggfunc='min')
)

      max_height  min_weight
kind                        
cat          9.5         7.9
dog         34.0         7.5

Nó thậm chí còn đơn giản hơn đối với Series, chỉ cần chuyển aggfunc vào một đối số từ khóa.

df.groupby('kind')['height'].agg(max_height='max', min_height='min')    

      max_height  min_height
kind                        
cat          9.5         9.1
dog         34.0         6.0       

Cuối cùng, nếu tên cột của bạn không phải là số nhận dạng python hợp lệ, hãy sử dụng từ điển với việc giải nén:

df.groupby('kind')['height'].agg(**{'max height': 'max', ...})

Gấu trúc <0,25

Trong các phiên bản gần đây hơn của gấu trúc dẫn đến 0,24, nếu sử dụng từ điển để chỉ định tên cột cho kết quả tổng hợp, bạn sẽ nhận được FutureWarning:

df.groupby('dummy').agg({'returns': {'Mean': 'mean', 'Sum': 'sum'}})
# FutureWarning: using a dict with renaming is deprecated and will be removed 
# in a future version

Việc sử dụng từ điển để đổi tên cột không được dùng trong v0.20. Trên các phiên bản gấu trúc gần đây hơn, điều này có thể được chỉ định đơn giản hơn bằng cách chuyển một danh sách các bộ giá trị. Nếu chỉ định các hàm theo cách này, tất cả các hàm cho cột đó cần phải được chỉ định dưới dạng bộ giá trị của các cặp (tên, hàm).

df.groupby("dummy").agg({'returns': [('op1', 'sum'), ('op2', 'mean')]})

        returns          
            op1       op2
dummy                    
1      0.328953  0.032895

Hoặc là,

df.groupby("dummy")['returns'].agg([('op1', 'sum'), ('op2', 'mean')])

            op1       op2
dummy                    
1      0.328953  0.032895

4
Đây phải là câu trả lời hàng đầu vì sử dụng một giải pháp rõ ràng và sạch sẽ hơn bằng cách sử dụng phiên bản giao diện mới hơn.
NKSHELL

Các ví dụ được sử dụng cho tập hợp được đặt tên không giải quyết được vấn đề ban đầu của việc sử dụng nhiều tập hợp trên cùng một cột. Ví dụ: bạn có thể tổng hợp theo cả chiều cao tối thiểu và tối đa mà không cần đặt trước cho df.groupby('kind')['height']?
người chiến thắng

1
@victor Tôi đã thêm TLDR ở đầu câu trả lời giải quyết trực tiếp câu hỏi. Và câu trả lời cho câu hỏi thứ hai của bạn là có, hãy xem phần chỉnh sửa câu trả lời của tôi.
cs95

Một mã chung chung hơn cho ví dụ cuối cùng về câu trả lời> = 0,25 của bạn để xử lý việc tổng hợp nhiều cột như thế này sẽ rất tuyệt. df.groupby("kind").agg(**{ 'max height': pd.NamedAgg(column='height', aggfunc=max), 'min weight': pd.NamedAgg(column='weight', aggfunc=min) })
Onur Ece

6

Có công việc nào như vầy không:

In [7]: df.groupby('dummy').returns.agg({'func1' : lambda x: x.sum(), 'func2' : lambda x: x.prod()})
Out[7]: 
              func2     func1
dummy                        
1     -4.263768e-16 -0.188565

2
Không, điều này không hoạt động. Nếu bạn nhìn vào chuỗi doc aggregatethì nó sẽ nói rõ ràng rằng khi a dictđược truyền, các khóa phải là tên cột. Vì vậy, ví dụ của bạn là một cái gì đó bạn đã nhập vào mà không kiểm tra lỗi này hoặc nếu không thì Pandas phá vỡ tài liệu của chính nó ở đây.
ely

N / MI không thấy cuộc gọi bổ sung returnsở đó. Vậy đây là phiên bản Series của tổng hợp? Tôi đang tìm cách tạo phiên bản DataFrame của tổng hợp và tôi muốn áp dụng một số tổng hợp khác nhau cho mỗi cột cùng một lúc.
ely

1
Hãy thử điều này: df.groupby ('dummy'). Agg ({'return': {'func1': lambda x: x.sum (), 'func2': lambda x: x.mean ()}})
Chang Cô ấy

Nó đưa ra một lỗi xác nhận không có thông báo. Từ giao diện của mã (pandas.core.internals.py, dòng 406-408, phiên bản 0.7.3), có vẻ như nó thực hiện kiểm tra ở cuối để đảm bảo rằng nó không trả về nhiều cột hơn số khóa trong phần đầu tiên lớp của từ điển tổng hợp.
ely

Hoạt động tốt trên tổng thể. Bạn muốn thử cập nhật?
Chang She,
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.