Pandas groupby: Làm thế nào để có được một nhóm các chuỗi


122

Tôi có một khung dữ liệu như thế này:

   A         B       C
0  1  0.749065    This
1  2  0.301084      is
2  3  0.463468       a
3  4  0.643961  random
4  1  0.866521  string
5  2  0.120737       !

Kêu gọi

In [10]: print df.groupby("A")["B"].sum()

sẽ trở lại

A
1    1.615586
2    0.421821
3    0.463468
4    0.643961

Bây giờ tôi muốn làm "tương tự" cho cột "C". Bởi vì cột đó chứa các chuỗi, sum () không hoạt động (mặc dù bạn có thể nghĩ rằng nó sẽ nối các chuỗi). Những gì tôi thực sự muốn xem là danh sách hoặc tập hợp các chuỗi cho mỗi nhóm, tức là

A
1    {This, string}
2    {is, !}
3    {a}
4    {random}

Tôi đã cố gắng tìm cách để làm điều này.

Series.unique () ( http://pandas.pydata.org/pandas-docs/stable/generated/pandas.Series.unique.html ) không hoạt động, mặc dù

df.groupby("A")["B"]

là một

pandas.core.groupby.SeriesGroupBy object

vì vậy tôi đã hy vọng bất kỳ phương pháp Series nào sẽ hoạt động. Có ý kiến ​​gì không?

Câu trả lời:


178
In [4]: df = read_csv(StringIO(data),sep='\s+')

In [5]: df
Out[5]: 
   A         B       C
0  1  0.749065    This
1  2  0.301084      is
2  3  0.463468       a
3  4  0.643961  random
4  1  0.866521  string
5  2  0.120737       !

In [6]: df.dtypes
Out[6]: 
A      int64
B    float64
C     object
dtype: object

Khi bạn áp dụng chức năng của riêng mình, không có loại trừ tự động các cột không phải là số. Đây là chậm hơn, tuy nhiên, so với việc áp dụng .sum()đếngroupby

In [8]: df.groupby('A').apply(lambda x: x.sum())
Out[8]: 
   A         B           C
A                         
1  2  1.615586  Thisstring
2  4  0.421821         is!
3  3  0.463468           a
4  4  0.643961      random

sum theo mặc định nối

In [9]: df.groupby('A')['C'].apply(lambda x: x.sum())
Out[9]: 
A
1    Thisstring
2           is!
3             a
4        random
dtype: object

Bạn có thể làm rất nhiều điều bạn muốn

In [11]: df.groupby('A')['C'].apply(lambda x: "{%s}" % ', '.join(x))
Out[11]: 
A
1    {This, string}
2           {is, !}
3               {a}
4          {random}
dtype: object

Làm điều này trên toàn bộ khung hình, một nhóm tại một thời điểm. Chìa khóa là trả lại mộtSeries

def f(x):
     return Series(dict(A = x['A'].sum(), 
                        B = x['B'].sum(), 
                        C = "{%s}" % ', '.join(x['C'])))

In [14]: df.groupby('A').apply(f)
Out[14]: 
   A         B               C
A                             
1  2  1.615586  {This, string}
2  4  0.421821         {is, !}
3  3  0.463468             {a}
4  4  0.643961        {random}

Có vẻ như các hoạt động này hiện đã được vecto hóa loại bỏ sự cần thiết applylambdas. Tôi đến đây tự hỏi tại sao pandasthực sự nối và không trả về lỗi khi tính tổng các chuỗi.
NelsonGon

1
Nếu bạn đang cố gắng nối các chuỗi và thêm một ký tự vào giữa, giải pháp .agg do @voithos đề xuất bên dưới nhanh hơn nhiều so với .app được đề xuất ở đây. Trong thử nghiệm của tôi, tôi đã nhanh hơn gấp 5-10 lần.
Doubledown

70

Bạn có thể sử dụng applyphương pháp để áp dụng một hàm tùy ý cho dữ liệu được nhóm. Vì vậy, nếu bạn muốn một bộ, hãy áp dụng set. Nếu bạn muốn có một danh sách, hãy đăng ký list.

>>> d
   A       B
0  1    This
1  2      is
2  3       a
3  4  random
4  1  string
5  2       !
>>> d.groupby('A')['B'].apply(list)
A
1    [This, string]
2           [is, !]
3               [a]
4          [random]
dtype: object

Nếu bạn muốn thứ gì đó khác, chỉ cần viết một hàm thực hiện những gì bạn muốn và sau đó applylàm điều đó.


Hoạt động tốt, nhưng Cột A bị thiếu.
Vineesh TP

@VineeshTP: Cột A được sử dụng làm cột nhóm, vì vậy nó nằm trong chỉ mục, như bạn có thể thấy trong ví dụ. Bạn có thể lấy lại nó dưới dạng một cột bằng cách sử dụng .reset_index().
BrenBarn

30

Bạn có thể sử dụng hàm aggregate(hoặc agg) để nối các giá trị. (Mã chưa được kiểm tra)

df.groupby('A')['B'].agg(lambda col: ''.join(col))

Nó thật sự có hiệu quả. Kinh ngạc. Như @voithos đã đề cập đến "chưa được kiểm tra", tôi không lạc quan lắm. Một chút, tôi đã thử nghiệm phiên bản của anh ấy như một mục nhập trong từ điển tổng hợp và nó hoạt động như dự định: .agg ({'tp': 'sum', 'BaseWgt': 'max', 'TP_short': lambda col: ',' .join (col)}) Made ngày của tôi
matthhias

2
Nếu bạn đang cố gắng nối các chuỗi lại với nhau bằng một số loại dấu phân tách, tôi thấy đề xuất .agg này nhanh hơn nhiều so với .apply. Đối với tập dữ liệu 600k + chuỗi văn bản, tôi nhận được kết quả giống hệt nhanh hơn 5-10 lần.
Doubledown

14

Bạn có thể thử điều này:

df.groupby('A').agg({'B':'sum','C':'-'.join})

2
Từ đánh giá: bạn có thể vui lòng thêm giải thích cho câu trả lời của bạn không?
toti08,

1
Groupby được áp dụng trên cột 'A' và với hàm agg, tôi có thể sử dụng các hàm khác nhau trên các cột khác nhau, nói rằng tổng các phần tử trong cột 'C', nối các phần tử trong cột 'C' trong khi chèn dấu '-' giữa các từ
user3241146

8

một giải pháp đơn giản sẽ là:

>>> df.groupby(['A','B']).c.unique().reset_index()

đây phải là câu trả lời đúng. được bạn trả lời rõ ràng. cảm ơn rất nhiều!
imsrgadich 27/07/18

Nếu trong trường hợp ai đó quan tâm đến việc nối nội dung của danh sách thành một chuỗi df.groupby(['A','B']).c.unique().apply(lambda x: ';'.join(x)).reset_index()
Vivek-Ananth

8

Tổng hợp được đặt tên với pandas >= 0.25.0

Kể từ phiên bản gấu trúc 0,25.0, chúng tôi đã đặt tên cho các tập hợp để chúng tôi có thể nhóm lại, tổng hợp và đồng thời gán các tên mới cho các cột của mình. Bằng cách này, chúng tôi sẽ không nhận được các cột MultiIndex và tên cột có ý nghĩa hơn dựa trên dữ liệu mà chúng chứa:


tổng hợp và nhận danh sách các chuỗi

grp = df.groupby('A').agg(B_sum=('B','sum'),
                          C=('C', list)).reset_index()

print(grp)
   A     B_sum               C
0  1  1.615586  [This, string]
1  2  0.421821         [is, !]
2  3  0.463468             [a]
3  4  0.643961        [random]

tổng hợp và nối các chuỗi

grp = df.groupby('A').agg(B_sum=('B','sum'),
                          C=('C', ', '.join)).reset_index()

print(grp)
   A     B_sum             C
0  1  1.615586  This, string
1  2  0.421821         is, !
2  3  0.463468             a
3  4  0.643961        random

6

Nếu bạn muốn ghi đè cột B trong khung dữ liệu, điều này sẽ hoạt động:

    df = df.groupby('A',as_index=False).agg(lambda x:'\n'.join(x))

2

Theo câu trả lời hay của @ Erfan, hầu hết trong phân tích các giá trị tổng hợp, bạn muốn các kết hợp độc nhất có thể có của các giá trị ký tự hiện có này:

unique_chars = lambda x: ', '.join(x.unique())
(df
 .groupby(['A'])
 .agg({'C': unique_chars}))
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.