Pandalar her grupta en üstteki n kaydı alır


163

Diyelim ki böyle DataFrame pandaları var:

>>> df = pd.DataFrame({'id':[1,1,1,2,2,2,2,3,4],'value':[1,2,3,1,2,3,4,1,1]})
>>> df
   id  value
0   1      1
1   1      2
2   1      3
3   2      1
4   2      2
5   2      3
6   2      4
7   3      1
8   4      1

Bu gibi her id için ilk 2 kayıtları ile yeni bir DataFrame almak istiyorum:

   id  value
0   1      1
1   1      2
3   2      1
4   2      2
7   3      1
8   4      1

Ben grup sonra grup içindeki kayıtları numaralandırma ile yapabilirsiniz:

>>> dfN = df.groupby('id').apply(lambda x:x['value'].reset_index()).reset_index()
>>> dfN
   id  level_1  index  value
0   1        0      0      1
1   1        1      1      2
2   1        2      2      3
3   2        0      3      1
4   2        1      4      2
5   2        2      5      3
6   2        3      6      4
7   3        0      7      1
8   4        0      8      1
>>> dfN[dfN['level_1'] <= 1][['id', 'value']]
   id  value
0   1      1
1   1      2
3   2      1
4   2      2
7   3      1
8   4      1

Ama bunu yapmak için daha etkili / zarif bir yaklaşım var mı? Ayrıca her gruptaki sayı kayıtlarına daha zarif bir yaklaşım var (SQL pencere işlevi row_number () gibi ).



1
"top-n", aradığınız gibi "en üstteki / ilk / baş satırları" anlamına gelmez! "En büyük değerlere sahip n satır" anlamına gelir.
smci

Yanıtlar:


183

Denedin mi df.groupby('id').head(2)

Çıktı üretildi:

>>> df.groupby('id').head(2)
       id  value
id             
1  0   1      1
   1   1      2 
2  3   2      1
   4   2      2
3  7   3      1
4  8   4      1

(Verilerinize bağlı olarak daha önce sipariş vermeniz / sıralamanız gerekebileceğini unutmayın)

EDIT: Sorgulayan tarafından belirtildiği gibi df.groupby('id').head(2).reset_index(drop=True), çoklu dizini kaldırmak ve sonuçları düzleştirmek için kullanın.

>>> df.groupby('id').head(2).reset_index(drop=True)
    id  value
0   1      1
1   1      2
2   2      1
3   2      2
4   3      1
5   4      1

1
Evet, bence bu kadar. Bunu bir şekilde gözden kaçırdı. Grup içindeki kayıtları numaralandırmanın iyi bir yolunu biliyor musunuz?
Roman Pekar

4
Çıktı almak için ihtiyacım da var.reset_index(drop=True)
Roman Pekar

1
github.com/pydata/pandas/pull/5510 yeni birleştirildi; 0.13 olacak, tam olarak bunu yapmak için yeni bir yöntem denir cumcount(her gruptaki kayıtları sayı)
Jeff

1
@ İyi haber. Keşke Pandalara katkıda bulunmak için daha fazla zamanım olsaydı :(
Roman Pekar

3
Eğer başına 2 en küçük değerleri istiyorsanız, daha tam onun cevabını @dorvak yapmak için iddaha sonra yapmak df.sort_values(['id', 'value'], axis=0).groupby('id').head(2). Başka bir örnek, başına en büyük değeri idverir df.sort_values(['id', 'value'], axis=0).groupby('id').tail(1).
Elmex80s

133

0.14.1'den beri , şimdi bir nesne üzerinde nlargestve nsmallestüzerinde yapabilirsiniz groupby:

In [23]: df.groupby('id')['value'].nlargest(2)
Out[23]: 
id   
1   2    3
    1    2
2   6    4
    5    3
3   7    1
4   8    1
dtype: int64

Orada siz de orada orijinal indeksi almak hafif bir tuhaflık var, ama bu orijinal indeks ne bağlı olarak gerçekten yararlı olabileceğini oldu .

Eğer ilgilenmiyorsanız .reset_index(level=1, drop=True), ondan tamamen kurtulmak için yapabilirsiniz .

(Not: 0.17.1'den itibaren bunu bir DataFrameGroupBy üzerinde de yapabilirsiniz, ancak şimdilik sadece Seriesve ile çalışır SeriesGroupBy.)


Almak için bir yol var unique_limit(n)mı? İlk n benzersiz değeri istediğim gibi? Ben sorarsanız nlargestpahalı olabilecek tüm df
sıralar

2
Groupby'de bir toplu işlem yaptığınız durumlarda bu işe yaramaz mı? Örneğin, df.groupby([pd.Grouper(freq='M'), 'A'])['B'].count().nlargest(5, 'B') bu adil değil, her grup tarafından, tüm serilerde genel top 5 döndürür
geominded

Bu da artık mümkün açık olduğunu beyan DataFrameGroupByyanlış olduğu lar görünür, bağlı çekme isteği eklemek görünen nlargestbasit için DataFramesadece bu. Hangisi oldukça talihsizdir, çünkü birden fazla sütun seçmek isterseniz ne olur?
oulenz

7

Bazen tüm verileri önceden sıralamak çok zaman alır. İlk olarak gruplayabilir ve her grup için topk yapabiliriz:

g = df.groupby(['id']).apply(lambda x: x.nlargest(topk,['value'])).reset_index(drop=True)
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.