Nhận (các) hàng có số lượng tối đa trong các nhóm bằng cách sử dụng nhóm


242

Làm cách nào để tìm tất cả các hàng trong khung dữ liệu gấu trúc có giá trị tối đa cho countcột, sau khi nhóm theo ['Sp','Mt']cột?

Ví dụ 1: dataFrame sau, mà tôi nhóm theo ['Sp','Mt']:

   Sp   Mt Value   count
0  MM1  S1   a      **3**
1  MM1  S1   n      2
2  MM1  S3   cb     5
3  MM2  S3   mk      **8**
4  MM2  S4   bg     **10**
5  MM2  S4   dgd      1
6  MM4  S2  rd     2
7  MM4  S2   cb      2
8  MM4  S2   uyi      **7**

Đầu ra dự kiến: lấy các hàng kết quả có số lượng tối đa giữa các nhóm, như:

0  MM1  S1   a      **3**
1 3  MM2  S3   mk      **8**
4  MM2  S4   bg     **10** 
8  MM4  S2   uyi      **7**

Ví dụ 2: khung dữ liệu này, mà tôi nhóm theo ['Sp','Mt']:

   Sp   Mt   Value  count
4  MM2  S4   bg     10
5  MM2  S4   dgd    1
6  MM4  S2   rd     2
7  MM4  S2   cb     8
8  MM4  S2   uyi    8

Đối với ví dụ trên, tôi muốn lấy tất cả các hàng countbằng max, trong mỗi nhóm, ví dụ:

MM2  S4   bg     10
MM4  S2   cb     8
MM4  S2   uyi    8

Định dạng nào là khung dữ liệu của bạn?
David Robinson

2
Tôi không hiểu Chính xác thì một nhóm là gì? Tại sao dòng thứ hai trong kết quả bắt đầu bằng 1 3?
Jo So


1
Câu trả lời này là giải pháp nhanh nhất tôi có thể tìm thấy: stackoverflow.com/a/21007047/778533
tommy.carstensen

Tương tự như câu hỏi này, bất cứ ai cũng có thể vui lòng trả lời câu hỏi này: stackoverflow.com/questions/62069465/ cảm ơn.
DS_Abc

Câu trả lời:


323
In [1]: df
Out[1]:
    Sp  Mt Value  count
0  MM1  S1     a      3
1  MM1  S1     n      2
2  MM1  S3    cb      5
3  MM2  S3    mk      8
4  MM2  S4    bg     10
5  MM2  S4   dgd      1
6  MM4  S2    rd      2
7  MM4  S2    cb      2
8  MM4  S2   uyi      7

In [2]: df.groupby(['Mt'], sort=False)['count'].max()
Out[2]:
Mt
S1     3
S3     8
S4    10
S2     7
Name: count

Để có được các chỉ số của DF gốc, bạn có thể làm:

In [3]: idx = df.groupby(['Mt'])['count'].transform(max) == df['count']

In [4]: df[idx]
Out[4]:
    Sp  Mt Value  count
0  MM1  S1     a      3
3  MM2  S3    mk      8
4  MM2  S4    bg     10
8  MM4  S2   uyi      7

Lưu ý rằng nếu bạn có nhiều giá trị tối đa cho mỗi nhóm, tất cả sẽ được trả về.

Cập nhật

Rất có thể đây là điều mà OP đang yêu cầu:

In [5]: df['count_max'] = df.groupby(['Mt'])['count'].transform(max)

In [6]: df
Out[6]:
    Sp  Mt Value  count  count_max
0  MM1  S1     a      3          3
1  MM1  S1     n      2          3
2  MM1  S3    cb      5          8
3  MM2  S3    mk      8          8
4  MM2  S4    bg     10         10
5  MM2  S4   dgd      1         10
6  MM4  S2    rd      2          7
7  MM4  S2    cb      2          7
8  MM4  S2   uyi      7          7

@ Zelazny7, có cách nào để áp dụng câu trả lời này để áp dụng cho việc nhóm theo một cột và sau đó nhìn vào 2 cột và thực hiện tối đa trong số chúng để có được số lớn hơn trong hai? Tôi không thể làm việc đó được. Những gì tôi hiện có là: def Greater (Hợp nhất, MaximumA, MaximumB): a = Merge [MaximumA] b = Merge [MaximumB] return max (a, b) Merger.groupby ("Search_Term"). Áp dụng (Lớn hơn, "Ratio_x "," Ratio_y ")
mathlover

3
@ Zelazny7 Tôi đang sử dụng idxphương pháp thứ hai . Nhưng, tôi chỉ có thể đủ khả năng ở một mức tối đa cho mỗi nhóm (và dữ liệu của tôi có một vài lần trùng lặp tối đa). Có cách nào để khắc phục điều này với giải pháp của bạn?
Người hâm mộ số một của Bjork

thực sự, điều đó không làm việc cho tôi Tôi không thể theo dõi vấn đề, vì khung dữ liệu nếu thoát lớn, nhưng giải pháp của @Rani hoạt động tốt
Ladenkov Vladislav

Xin chào Zealzny, Nếu tôi muốn lấy 3 hàng tối đa hàng đầu thay vì một giá trị tối đa, Làm cách nào tôi có thể điều chỉnh mã của mình?
Zephyr

transformPhương thức có thể có hiệu năng nhóm khi tập dữ liệu đủ lớn, lấy giá trị tối đa trước sau đó hợp nhất các tệp dữ liệu sẽ tốt hơn.
Rừng Chen

169

Bạn có thể sắp xếp dataFrame theo số lượng và sau đó loại bỏ trùng lặp. Tôi nghĩ nó dễ hơn:

df.sort_values('count', ascending=False).drop_duplicates(['Sp','Mt'])

4
Rất đẹp! Nhanh chóng với các khung hình lớn (hàng 25k)
Nolan Conaway

2
Đối với những người hơi mới với Python, bạn sẽ cần gán biến này cho một biến mới, nó không thay đổi biến df hiện tại.
Tyler

1
@Samir hoặc sử dụng inplace = Truelàm đối số chodrop_duplicates
TMrtSmith

5
Đây là một câu trả lời tuyệt vời khi chỉ cần một trong các hàng có cùng giá trị tối đa, tuy nhiên nó sẽ không hoạt động như mong đợi nếu tôi cần tất cả các hàng có giá trị tối đa.
Rừng Chen

1
@WoodsChen, nó giảm các bản sao của [sp, mt], do đó trong ví dụ của bạn, đầu ra chỉ có một hàng.
Rani

54

Giải pháp dễ dàng là áp dụng hàm: idxmax () để lấy chỉ số của các hàng có giá trị tối đa. Điều này sẽ lọc ra tất cả các hàng có giá trị tối đa trong nhóm.

In [365]: import pandas as pd

In [366]: df = pd.DataFrame({
'sp' : ['MM1', 'MM1', 'MM1', 'MM2', 'MM2', 'MM2', 'MM4', 'MM4','MM4'],
'mt' : ['S1', 'S1', 'S3', 'S3', 'S4', 'S4', 'S2', 'S2', 'S2'],
'val' : ['a', 'n', 'cb', 'mk', 'bg', 'dgb', 'rd', 'cb', 'uyi'],
'count' : [3,2,5,8,10,1,2,2,7]
})

In [367]: df                                                                                                       
Out[367]: 
   count  mt   sp  val
0      3  S1  MM1    a
1      2  S1  MM1    n
2      5  S3  MM1   cb
3      8  S3  MM2   mk
4     10  S4  MM2   bg
5      1  S4  MM2  dgb
6      2  S2  MM4   rd
7      2  S2  MM4   cb
8      7  S2  MM4  uyi


### Apply idxmax() and use .loc() on dataframe to filter the rows with max values:
In [368]: df.loc[df.groupby(["sp", "mt"])["count"].idxmax()]                                                       
Out[368]: 
   count  mt   sp  val
0      3  S1  MM1    a
2      5  S3  MM1   cb
3      8  S3  MM2   mk
4     10  S4  MM2   bg
8      7  S2  MM4  uyi

### Just to show what values are returned by .idxmax() above:
In [369]: df.groupby(["sp", "mt"])["count"].idxmax().values                                                        
Out[369]: array([0, 2, 3, 4, 8])

4
Người hỏi ở đây chỉ định "I want to get ALL the rows where count equals max in each group", trong khi idxmax Return[s] index of first occurrence of maximum over requested axis"theo các tài liệu (0,21).
Công suất tối đa

1
Đây là một giải pháp tuyệt vời, nhưng đối với một vấn đề khác
Carlos Souza

33

Đã thử giải pháp được đề xuất bởi Zelazny trên DataFrame tương đối lớn (~ 400k hàng), tôi thấy nó rất chậm. Đây là một giải pháp thay thế mà tôi thấy để chạy các đơn đặt hàng có cường độ nhanh hơn trên tập dữ liệu của mình.

df = pd.DataFrame({
    'sp' : ['MM1', 'MM1', 'MM1', 'MM2', 'MM2', 'MM2', 'MM4', 'MM4', 'MM4'],
    'mt' : ['S1', 'S1', 'S3', 'S3', 'S4', 'S4', 'S2', 'S2', 'S2'],
    'val' : ['a', 'n', 'cb', 'mk', 'bg', 'dgb', 'rd', 'cb', 'uyi'],
    'count' : [3,2,5,8,10,1,2,2,7]
    })

df_grouped = df.groupby(['sp', 'mt']).agg({'count':'max'})

df_grouped = df_grouped.reset_index()

df_grouped = df_grouped.rename(columns={'count':'count_max'})

df = pd.merge(df, df_grouped, how='left', on=['sp', 'mt'])

df = df[df['count'] == df['count_max']]

1
thực sự điều này nhanh hơn nhiều biến đổi dường như là chậm cho dữ liệu lớn.
goh

1
Bạn có thể thêm ý kiến ​​để giải thích mỗi dòng làm gì?
tommy.carstensen

fwiw: Tôi đã tìm thấy giải pháp có vẻ thanh lịch hơn từ @ Zelazny7 mất nhiều thời gian để thực thi cho bộ ~ 100K hàng của tôi, nhưng hàng này chạy khá nhanh. (Tôi đang chạy phiên bản 0.13.0 lỗi thời, có thể gây ra sự chậm chạp).
Roland

2
Nhưng làm điều này df[df['count'] == df['count_max']]sẽ mất các hàng NaN, cũng như các câu trả lời ở trên.
Qy Zuo

Tôi đặc biệt khuyên bạn nên sử dụng phương pháp này, đối với các khung dữ liệu lớn hơn, việc sử dụng .appy () hoặc .agg () sẽ nhanh hơn nhiều.
Touya D. Serdan

18

Bạn có thể không cần phải làm với nhóm bằng cách sử dụng sort_values+drop_duplicates

df.sort_values('count').drop_duplicates(['Sp','Mt'],keep='last')
Out[190]: 
    Sp  Mt Value  count
0  MM1  S1     a      3
2  MM1  S3    cb      5
8  MM4  S2   uyi      7
3  MM2  S3    mk      8
4  MM2  S4    bg     10

Cũng gần như logic bằng cách sử dụng tail

df.sort_values('count').groupby(['Sp', 'Mt']).tail(1)
Out[52]: 
    Sp  Mt Value  count
0  MM1  S1     a      3
2  MM1  S3    cb      5
8  MM4  S2   uyi      7
3  MM2  S3    mk      8
4  MM2  S4    bg     10

Đây không chỉ là một thứ tự cường độ nhanh hơn các giải pháp khác (ít nhất là đối với trường hợp sử dụng của tôi), nó còn có thêm lợi ích của việc xâu chuỗi đơn giản như là một phần của việc xây dựng khung dữ liệu gốc.
Clay

Tôi đã gãi đầu suy nghĩ chắc chắn điều này thật đơn giản, cảm ơn vì câu trả lời tuyệt vời của bạn như mọi khi ông Wen.
Datanovice

7

Đối với tôi, giải pháp đơn giản nhất sẽ là giữ giá trị khi số lượng bằng với mức tối đa. Do đó, lệnh một dòng sau là đủ:

df[df['count'] == df.groupby(['Mt'])['count'].transform(max)]

4

Sử dụng groupbyidxmaxphương pháp:

  1. chuyển col datesang datetime:

    df['date']=pd.to_datetime(df['date'])
  2. lấy chỉ số maxcủa cột date, sau groupyby ad_id:

    idx=df.groupby(by='ad_id')['date'].idxmax()
  3. lấy dữ liệu mong muốn:

    df_max=df.loc[idx,]

Hết [54]:

ad_id  price       date
7     22      2 2018-06-11
6     23      2 2018-06-22
2     24      2 2018-06-30
3     28      5 2018-06-22

2
df = pd.DataFrame({
'sp' : ['MM1', 'MM1', 'MM1', 'MM2', 'MM2', 'MM2', 'MM4', 'MM4','MM4'],
'mt' : ['S1', 'S1', 'S3', 'S3', 'S4', 'S4', 'S2', 'S2', 'S2'],
'val' : ['a', 'n', 'cb', 'mk', 'bg', 'dgb', 'rd', 'cb', 'uyi'],
'count' : [3,2,5,8,10,1,2,2,7]
})

df.groupby(['sp', 'mt']).apply(lambda grp: grp.nlargest(1, 'count'))

2

Nhận ra rằng "áp dụng" "mạnh nhất" cho đối tượng nhóm hoạt động tốt như sau:

Lợi thế bổ sung - cũng có thể tìm nạp các giá trị n hàng đầu nếu được yêu cầu:

In [85]: import pandas as pd

In [86]: df = pd.DataFrame({
    ...: 'sp' : ['MM1', 'MM1', 'MM1', 'MM2', 'MM2', 'MM2', 'MM4', 'MM4','MM4'],
    ...: 'mt' : ['S1', 'S1', 'S3', 'S3', 'S4', 'S4', 'S2', 'S2', 'S2'],
    ...: 'val' : ['a', 'n', 'cb', 'mk', 'bg', 'dgb', 'rd', 'cb', 'uyi'],
    ...: 'count' : [3,2,5,8,10,1,2,2,7]
    ...: })

## Apply nlargest(1) to find the max val df, and nlargest(n) gives top n values for df:
In [87]: df.groupby(["sp", "mt"]).apply(lambda x: x.nlargest(1, "count")).reset_index(drop=True)
Out[87]:
   count  mt   sp  val
0      3  S1  MM1    a
1      5  S3  MM1   cb
2      8  S3  MM2   mk
3     10  S4  MM2   bg
4      7  S2  MM4  uyi

2

Hãy thử sử dụng "nlargest" trên đối tượng nhóm. Ưu điểm của việc sử dụng nlargest là nó trả về chỉ mục của các hàng trong đó "mục lớn nhất" được tìm nạp từ đó. Lưu ý: chúng tôi cắt phần tử thứ hai (1) của chỉ mục vì chỉ mục của chúng tôi trong trường hợp này bao gồm các bộ dữ liệu (ví dụ: (s1, 0)).

df = pd.DataFrame({
'sp' : ['MM1', 'MM1', 'MM1', 'MM2', 'MM2', 'MM2', 'MM4', 'MM4','MM4'],
'mt' : ['S1', 'S1', 'S3', 'S3', 'S4', 'S4', 'S2', 'S2', 'S2'],
'val' : ['a', 'n', 'cb', 'mk', 'bg', 'dgb', 'rd', 'cb', 'uyi'],
'count' : [3,2,5,8,10,1,2,2,7]
})

d = df.groupby('mt')['count'].nlargest(1) # pass 1 since we want the max

df.iloc[[i[1] for i in d.index], :] # pass the index of d as list comprehension

nhập mô tả hình ảnh ở đây


1

Tôi đã sử dụng kiểu chức năng này cho nhiều hoạt động nhóm:

df = pd.DataFrame({
   'Sp' : ['MM1', 'MM1', 'MM1', 'MM2', 'MM2', 'MM2', 'MM4', 'MM4', 'MM4'],
   'Mt' : ['S1', 'S1', 'S3', 'S3', 'S4', 'S4', 'S2', 'S2', 'S2'],
   'Val' : ['a', 'n', 'cb', 'mk', 'bg', 'dgb', 'rd', 'cb', 'uyi'],
   'Count' : [3,2,5,8,10,1,2,2,7]
})

df.groupby('Mt')\
  .apply(lambda group: group[group.Count == group.Count.max()])\
  .reset_index(drop=True)

    sp  mt  val  count
0  MM1  S1    a      3
1  MM4  S2  uyi      7
2  MM2  S3   mk      8
3  MM2  S4   bg     10

.reset_index(drop=True) đưa bạn trở lại chỉ mục ban đầu bằng cách bỏ chỉ mục nhóm.

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.