Làm cách nào để tạo một cột mới từ đầu ra của pandas groupby (). Sum ()?


82

Đang cố gắng tạo một cột mới từ groupbyphép tính. Trong đoạn mã dưới đây, tôi nhận được các giá trị được tính toán chính xác cho mỗi ngày (xem nhóm bên dưới) nhưng khi tôi cố gắng tạo một cột mới ( df['Data4']) với nó, tôi nhận được NaN. Vì vậy, tôi đang cố gắng tạo một cột mới trong khung dữ liệu với tổng của Data3tất cả các ngày và áp dụng cột đó cho mỗi hàng ngày. Ví dụ: 2015-05-08 nằm trong 2 hàng (tổng là 50 + 5 = 55) và trong cột mới này, tôi muốn có 55 trong cả hai hàng.

import pandas as pd
import numpy as np
from pandas import DataFrame

df = pd.DataFrame({
    'Date' : ['2015-05-08', '2015-05-07', '2015-05-06', '2015-05-05', '2015-05-08', '2015-05-07', '2015-05-06', '2015-05-05'], 
    'Sym'  : ['aapl', 'aapl', 'aapl', 'aapl', 'aaww', 'aaww', 'aaww', 'aaww'], 
    'Data2': [11, 8, 10, 15, 110, 60, 100, 40],
    'Data3': [5, 8, 6, 1, 50, 100, 60, 120]
})

group = df['Data3'].groupby(df['Date']).sum()

df['Data4'] = group

Câu trả lời:


189

Bạn muốn sử dụng transformđiều này sẽ trả về một Chuỗi có chỉ mục được căn chỉnh với df để sau đó bạn có thể thêm nó làm cột mới:

In [74]:

df = pd.DataFrame({'Date': ['2015-05-08', '2015-05-07', '2015-05-06', '2015-05-05', '2015-05-08', '2015-05-07', '2015-05-06', '2015-05-05'], 'Sym': ['aapl', 'aapl', 'aapl', 'aapl', 'aaww', 'aaww', 'aaww', 'aaww'], 'Data2': [11, 8, 10, 15, 110, 60, 100, 40],'Data3': [5, 8, 6, 1, 50, 100, 60, 120]})
​
df['Data4'] = df['Data3'].groupby(df['Date']).transform('sum')
df
Out[74]:
   Data2  Data3        Date   Sym  Data4
0     11      5  2015-05-08  aapl     55
1      8      8  2015-05-07  aapl    108
2     10      6  2015-05-06  aapl     66
3     15      1  2015-05-05  aapl    121
4    110     50  2015-05-08  aaww     55
5     60    100  2015-05-07  aaww    108
6    100     60  2015-05-06  aaww     66
7     40    120  2015-05-05  aaww    121

Điều gì sẽ xảy ra nếu chúng ta có nhóm thứ hai như trong đây: stackoverflow.com/a/40067099/281545
Mr_and_Mrs_D

@Mr_and_Mrs_D bạn sẽ phải đặt lại chỉ mục và thực hiện hợp nhất bên trái trên các cột chung trong trường hợp đó để thêm cột trở lại
EdChum

10
Ngoài ra, người ta có thể sử dụng df.groupby('Date')['Data3'].transform('sum')(mà tôi thấy dễ nhớ hơn một chút).
Cleb

39

Làm cách nào để tạo một cột mới với Groupby (). Sum ()?

Có hai cách - một cách đơn giản và một cách khác thú vị hơn một chút.


Mọi người yêu thích: GroupBy.transform()với'sum'

Câu trả lời của @Ed Chum có thể được đơn giản hóa một chút. Gọi DataFrame.groupbyhơn là Series.groupby. Điều này dẫn đến cú pháp đơn giản hơn.

# The setup.
df[['Date', 'Data3']]

         Date  Data3
0  2015-05-08      5
1  2015-05-07      8
2  2015-05-06      6
3  2015-05-05      1
4  2015-05-08     50
5  2015-05-07    100
6  2015-05-06     60
7  2015-05-05    120

df.groupby('Date')['Data3'].transform('sum')

0     55
1    108
2     66
3    121
4     55
5    108
6     66
7    121
Name: Data3, dtype: int64 

Nó nhanh hơn một chút,

df2 = pd.concat([df] * 12345)

%timeit df2['Data3'].groupby(df['Date']).transform('sum')
%timeit df2.groupby('Date')['Data3'].transform('sum')

10.4 ms ± 367 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
8.58 ms ± 559 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

Không độc đáo, nhưng đáng để bạn cân nhắc: GroupBy.sum()+Series.map()

Tôi tình cờ gặp một đặc điểm riêng thú vị trong API. Từ những gì tôi nói, bạn có thể tái tạo điều này trên bất kỳ phiên bản chính nào trên 0,20 (tôi đã thử nghiệm điều này trên 0,23 và 0,24). Có vẻ như bạn luôn có thể cắt bớt một vài mili giây thời gian transformnếu thay vào đó bạn sử dụng một chức năng trực tiếp của GroupByvà phát nó bằng cách sử dụng map:

df.Date.map(df.groupby('Date')['Data3'].sum())

0     55
1    108
2     66
3    121
4     55
5    108
6     66
7    121
Name: Date, dtype: int64

So sánh với

df.groupby('Date')['Data3'].transform('sum')

0     55
1    108
2     66
3    121
4     55
5    108
6     66
7    121
Name: Data3, dtype: int64

Thử nghiệm của tôi cho thấy maplà một chút nhanh hơn nếu bạn có thể đủ khả năng để sử dụng trực tiếp GroupBychức năng (ví dụ như mean, min, max, first, vv). Nó nhanh hơn hoặc ít hơn đối với hầu hết các tình huống chung lên đến khoảng ~ 200 nghìn bản ghi. Sau đó, hiệu suất thực sự phụ thuộc vào dữ liệu.

(Trái: v0.23, Phải: v0.24)

Thay thế tuyệt vời để biết và tốt hơn nếu bạn có khung nhỏ hơn với số lượng nhóm nhỏ hơn. . . nhưng tôi muốn giới thiệu transformnhư một sự lựa chọn đầu tiên. Nghĩ rằng điều này là giá trị chia sẻ dù sao.

Mã điểm chuẩn, để tham khảo:

import perfplot

perfplot.show(
    setup=lambda n: pd.DataFrame({'A': np.random.choice(n//10, n), 'B': np.ones(n)}),
    kernels=[
        lambda df: df.groupby('A')['B'].transform('sum'),
        lambda df:  df.A.map(df.groupby('A')['B'].sum()),
    ],
    labels=['GroupBy.transform', 'GroupBy.sum + map'],
    n_range=[2**k for k in range(5, 20)],
    xlabel='N',
    logy=True,
    logx=True
)

1
Điều này là tốt để biết! Bạn có phiền khi bao gồm (ít nhất là trong những chiếc perfplots trong tương lai) số phiên bản không? Sự khác biệt về hiệu suất là điều thú vị, nhưng xét cho cùng thì đây là những chi tiết triển khai có thể được hoàn thiện trong tương lai. Đặc biệt nếu các nhà phát triển ghi chú các bài viết của bạn.
jpp

@jpp yup thật công bằng! Đã thêm phiên bản. Điều này đã được thử nghiệm trên 0,23 nhưng tôi tin rằng sự khác biệt được nhìn thấy miễn là bạn có bất kỳ phiên bản nào trên 0,20.
cs95
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.