Làm cách nào để nhóm các hàng dataframe vào danh sách trong nhóm gấu trúc?


274

Tôi có một khung dữ liệu gấu trúc dfnhư:

a b
A 1
A 2
B 5
B 5
B 4
C 6

Tôi muốn nhóm theo cột đầu tiên và lấy cột thứ hai dưới dạng danh sách trong các hàng :

A [1,2]
B [5,5,4]
C [6]

Có thể làm một cái gì đó như thế này bằng cách sử dụng gấu trúc nhóm?

Câu trả lời:


393

Bạn có thể thực hiện việc này bằng cách sử dụng groupbynhóm trên cột quan tâm và sau đó apply listđến từng nhóm:

In [1]: df = pd.DataFrame( {'a':['A','A','B','B','B','C'], 'b':[1,2,5,5,4,6]})
        df

Out[1]: 
   a  b
0  A  1
1  A  2
2  B  5
3  B  5
4  B  4
5  C  6

In [2]: df.groupby('a')['b'].apply(list)
Out[2]: 
a
A       [1, 2]
B    [5, 5, 4]
C          [6]
Name: b, dtype: object

In [3]: df1 = df.groupby('a')['b'].apply(list).reset_index(name='new')
        df1
Out[3]: 
   a        new
0  A     [1, 2]
1  B  [5, 5, 4]
2  C        [6]

7
Điều này sẽ mất rất nhiều thời gian nếu bộ dữ liệu rất lớn, giả sử hàng triệu triệu hàng. Có cách nào nhanh hơn để làm điều này? Tuy nhiên, số lượng người lạ trong 'a' là khoảng 500 nghìn
Abhishek Thakur

6
Groupby nổi tiếng là chậm và đói bộ nhớ, những gì bạn có thể làm là sắp xếp theo cột A, sau đó tìm idxmin và idxmax (có thể lưu trữ này trong một lệnh) và sử dụng điều này để cắt khung dữ liệu của bạn sẽ nhanh hơn tôi nghĩ
EdChum

1
Khi tôi thử giải pháp này với vấn đề của mình (có nhiều cột cho nhómBy và nhóm), nó không hoạt động - gấu trúc đã gửi 'Chức năng không giảm'. Sau đó, tôi đã sử dụng tupletheo câu trả lời thứ hai ở đây: stackoverflow.com/questions/19530568/ . Xem câu trả lời thứ hai trong stackoverflow.com/questions/27439023/ để giải thích.
Andarin

Giải pháp này là tốt, nhưng có cách nào để lưu trữ bộ danh sách, nghĩa là tôi có thể loại bỏ các bản sao và sau đó lưu trữ không?
Sriram Arvind Lakshmanakumar

1
@PoeteMaudit Xin lỗi tôi không hiểu những gì bạn đang hỏi và đặt câu hỏi trong các bình luận là hình thức xấu trong SO. Bạn đang hỏi làm thế nào để ghép nhiều cột thành một danh sách?
EdChum

47

Nếu hiệu suất là quan trọng, hãy chuyển xuống mức numpy:

import numpy as np

df = pd.DataFrame({'a': np.random.randint(0, 60, 600), 'b': [1, 2, 5, 5, 4, 6]*100})

def f(df):
         keys, values = df.sort_values('a').values.T
         ukeys, index = np.unique(keys, True)
         arrays = np.split(values, index[1:])
         df2 = pd.DataFrame({'a':ukeys, 'b':[list(a) for a in arrays]})
         return df2

Các xét nghiệm:

In [301]: %timeit f(df)
1000 loops, best of 3: 1.64 ms per loop

In [302]: %timeit df.groupby('a')['b'].apply(list)
100 loops, best of 3: 5.26 ms per loop

8
Làm thế nào chúng ta có thể sử dụng điều này nếu chúng ta nhóm bằng hai hoặc nhiều khóa, ví dụ như .groupby([df.index.month, df.index.day])thay vì chỉ .groupby('a')?
ru111

25

Một cách thuận tiện để đạt được điều này sẽ là:

df.groupby('a').agg({'b':lambda x: list(x)})

Xem xét cách viết Tập hợp tùy chỉnh: https://www.kaggle.com/akshaysehgal/how-to-group-by-aggregate-USE-py


5
lambda args: f(args)tương đương vớif
BallpointBen

6
Thật ra, chỉ cần agg(list)là đủ. Cũng xem tại đây .
cs95

!! Tôi chỉ đang googling cho một số cú pháp và nhận ra máy tính xách tay của riêng tôi đã được tham chiếu cho giải pháp lol. Cảm ơn đã liên kết này. Chỉ cần thêm, vì 'danh sách' không phải là một hàm chuỗi, bạn sẽ phải sử dụng nó với ứng dụng df.groupby('a').apply(list)hoặc sử dụng nó với agg như một phần của lệnh df.groupby('a').agg({'b':list}). Bạn cũng có thể sử dụng nó với lambda (mà tôi khuyên dùng) vì bạn có thể làm nhiều hơn với nó. Ví dụ: df.groupby('a').agg({'c':'first', 'b': lambda x: x.unique().tolist()})cho phép bạn áp dụng một hàm chuỗi cho col c và một hàm danh sách duy nhất sau đó cho col b.
Akshay Sehgal

21

Như bạn đã nói groupbyphương pháp của một pd.DataFrameđối tượng có thể thực hiện công việc.

Thí dụ

 L = ['A','A','B','B','B','C']
 N = [1,2,5,5,4,6]

 import pandas as pd
 df = pd.DataFrame(zip(L,N),columns = list('LN'))


 groups = df.groupby(df.L)

 groups.groups
      {'A': [0, 1], 'B': [2, 3, 4], 'C': [5]}

trong đó đưa ra và mô tả chỉ số khôn ngoan của các nhóm.

Để lấy các phần tử của các nhóm đơn lẻ, bạn có thể làm, ví dụ

 groups.get_group('A')

     L  N
  0  A  1
  1  A  2

  groups.get_group('B')

     L  N
  2  B  5
  3  B  5
  4  B  4

21

Để giải quyết vấn đề này cho một số cột của khung dữ liệu:

In [5]: df = pd.DataFrame( {'a':['A','A','B','B','B','C'], 'b':[1,2,5,5,4,6],'c'
   ...: :[3,3,3,4,4,4]})

In [6]: df
Out[6]: 
   a  b  c
0  A  1  3
1  A  2  3
2  B  5  3
3  B  5  4
4  B  4  4
5  C  6  4

In [7]: df.groupby('a').agg(lambda x: list(x))
Out[7]: 
           b          c
a                      
A     [1, 2]     [3, 3]
B  [5, 5, 4]  [3, 4, 4]
C        [6]        [4]

Câu trả lời này được lấy cảm hứng từ câu trả lời của Anamika Modi . Cảm ơn bạn!


12

Sử dụng bất kỳ sau đây groupbyaggcông thức nấu ăn.

# Setup
df = pd.DataFrame({
  'a': ['A', 'A', 'B', 'B', 'B', 'C'],
  'b': [1, 2, 5, 5, 4, 6],
  'c': ['x', 'y', 'z', 'x', 'y', 'z']
})
df

   a  b  c
0  A  1  x
1  A  2  y
2  B  5  z
3  B  5  x
4  B  4  y
5  C  6  z

Để tổng hợp nhiều cột dưới dạng danh sách, hãy sử dụng bất kỳ mục nào sau đây:

df.groupby('a').agg(list)
df.groupby('a').agg(pd.Series.tolist)

           b          c
a                      
A     [1, 2]     [x, y]
B  [5, 5, 4]  [z, x, y]
C        [6]        [z]

Để chỉ liệt kê một nhóm duy nhất, chuyển đổi nhóm thành một SeriesGroupByđối tượng, sau đó gọi SeriesGroupBy.agg. Sử dụng,

df.groupby('a').agg({'b': list})  # 4.42 ms 
df.groupby('a')['b'].agg(list)    # 2.76 ms - faster

a
A       [1, 2]
B    [5, 5, 4]
C          [6]
Name: b, dtype: object

các phương pháp trên có đảm bảo giữ gìn trật tự không? nghĩa là các phần tử từ cùng một hàng (nhưng các cột, bc khác nhau trong mã của bạn ở trên) sẽ có cùng chỉ mục trong danh sách kết quả?
Kai

@Kai oh, câu hỏi hay. Có và không. GroupBy sắp xếp đầu ra theo các giá trị khóa cá mú. Tuy nhiên, loại này thường ổn định nên thứ tự tương đối cho mỗi nhóm được giữ nguyên. Để vô hiệu hóa hoàn toàn hành vi sắp xếp, sử dụng groupby(..., sort=False). Ở đây, sẽ không có gì khác biệt vì tôi đang nhóm trên cột A đã được sắp xếp.
cs95

tôi xin lỗi, tôi không hiểu câu trả lời của bạn Bạn có thể giải thích chi tiết hơn Tôi nghĩ rằng đây xứng đáng là câu hỏi của riêng mình ..
Kai

1
Đây là một câu trả lời rất tốt! Có cách nào để làm cho các giá trị của danh sách trở nên độc đáo không? một cái gì đó như .agg (pd.Series.tolist.unique) có thể?
Federico Gentile

1
@FedericoGentile bạn có thể sử dụng lambda. Đây là một cách:df.groupby('a')['b'].agg(lambda x: list(set(x)))
cs95

7

Nếu tìm kiếm một danh sách duy nhất trong khi nhóm nhiều cột, điều này có thể giúp:

df.groupby('a').agg(lambda x: list(set(x))).reset_index()

2

Hãy để chúng tôi sử dụng df.groupbyvới danh sách và nhà Seriesxây dựng

pd.Series({x : y.b.tolist() for x , y in df.groupby('a')})
Out[664]: 
A       [1, 2]
B    [5, 5, 4]
C          [6]
dtype: object

2

Đó là thời gian để sử dụng aggthay vì apply.

Khi nào

df = pd.DataFrame( {'a':['A','A','B','B','B','C'], 'b':[1,2,5,5,4,6], 'c': [1,2,5,5,4,6]})

Nếu bạn muốn nhiều cột xếp vào danh sách, kết quả là pd.DataFrame

df.groupby('a')[['b', 'c']].agg(list)
# or 
df.groupby('a').agg(list)

Nếu bạn muốn một cột trong danh sách, kết quả là ps.Series

df.groupby('a')['b'].agg(list)
#or
df.groupby('a')['b'].apply(list)

Lưu ý, kết quả pd.DataFramechậm hơn khoảng 10 lần so với kết quả ps.Serieskhi bạn chỉ tổng hợp cột đơn, sử dụng nó trong trường hợp nhiều màu.


0

Ở đây tôi đã nhóm các phần tử với "|" như một dấu phân cách

    import pandas as pd

    df = pd.read_csv('input.csv')

    df
    Out[1]:
      Area  Keywords
    0  A  1
    1  A  2
    2  B  5
    3  B  5
    4  B  4
    5  C  6

    df.dropna(inplace =  True)
    df['Area']=df['Area'].apply(lambda x:x.lower().strip())
    print df.columns
    df_op = df.groupby('Area').agg({"Keywords":lambda x : "|".join(x)})

    df_op.to_csv('output.csv')
    Out[2]:
    df_op
    Area  Keywords

    A       [1| 2]
    B    [5| 5| 4]
    C          [6]

0

Cách dễ nhất mà tôi thấy không đạt được hầu hết điều tương tự ít nhất là cho một cột tương tự như câu trả lời của Anamika chỉ với cú pháp tuple cho hàm tổng hợp.

df.groupby('a').agg(b=('b','unique'), c=('c','unique'))
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.