Pandas groupby: วิธีการรวมสายอักขระ


122

ฉันมี dataframe แบบนี้:

   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 [10]: print df.groupby("A")["B"].sum()

จะกลับมา

A
1    1.615586
2    0.421821
3    0.463468
4    0.643961

ตอนนี้ฉันต้องการทำ "เหมือนกัน" สำหรับคอลัมน์ "C" เนื่องจากคอลัมน์นั้นมีสตริง sum () จึงไม่ทำงาน (แม้ว่าคุณอาจคิดว่ามันจะเชื่อมสตริงเข้าด้วยกันก็ตาม) สิ่งที่ฉันอยากเห็นจริงๆคือรายการหรือชุดของสตริงสำหรับแต่ละกลุ่มนั่นคือ

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

ฉันพยายามหาวิธีการนี้มาตลอด

Series.unique () ( http://pandas.pydata.org/pandas-docs/stable/generated/pandas.Series.unique.html ) ไม่ทำงานแม้ว่า

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

คือ

pandas.core.groupby.SeriesGroupBy object

ดังนั้นฉันหวังว่าวิธี Series ใด ๆ จะใช้ได้ผล ความคิดใด ๆ ?

คำตอบ:


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

เมื่อคุณใช้ฟังก์ชันของคุณเองจะไม่มีการยกเว้นคอลัมน์ที่ไม่ใช่ตัวเลขโดยอัตโนมัติ แม้ว่าจะช้ากว่าการใช้งาน.sum()กับไฟล์groupby

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 โดยค่าเริ่มต้นเชื่อมต่อกัน

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

คุณสามารถทำสิ่งที่คุณต้องการได้มากมาย

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

ทำสิ่งนี้กับทั้งเฟรมทีละกลุ่ม คีย์คือการส่งคืนไฟล์Series

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}

ดูเหมือนว่าการดำเนินการเหล่านี้จะถูกทำให้เป็นเวกเตอร์โดยไม่จำเป็นต้องใช้applyและlambdas ฉันมาที่นี่โดยสงสัยว่าทำไมจึงเชื่อมต่อpandasจริงและไม่ส่งคืนข้อผิดพลาดในการสรุปสตริง
NelsonGon

1
หากคุณกำลังพยายามต่อสตริงและเพิ่มอักขระระหว่างนั้นโซลูชัน. agg ที่แนะนำโดย @voithos ด้านล่างจะเร็วกว่าคำแนะนำ. ใช้ที่นี่มาก ในการทดสอบของฉันฉันเร็วขึ้น 5-10 เท่า
Doubledown

70

คุณสามารถใช้applyวิธีการเพื่อใช้ฟังก์ชันที่กำหนดเองกับข้อมูลที่จัดกลุ่ม setดังนั้นหากคุณต้องการชุดใช้ 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

ถ้าคุณต้องการสิ่งอื่นเพียงแค่เขียนฟังก์ชั่นที่ไม่สิ่งที่คุณต้องการแล้วapplyว่า


ทำงานได้ดี แต่คอลัมน์ A หายไป
Vineesh TP

@VineeshTP: คอลัมน์ A ถูกใช้เป็นคอลัมน์การจัดกลุ่มดังนั้นจึงอยู่ในดัชนีดังที่คุณเห็นในตัวอย่าง คุณสามารถนำมันกลับมาเป็นคอลัมน์ได้โดยใช้.reset_index().
BrenBarn

30

คุณอาจสามารถใช้ฟังก์ชันaggregate(หรือagg) เพื่อเชื่อมค่าต่างๆเข้าด้วยกัน (รหัสที่ยังไม่ทดสอบ)

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

มันใช้งานได้จริง น่าอัศจรรย์ ดังที่ @voithos กล่าวถึง "ยังไม่ทดลอง" ฉันไม่ได้มองโลกในแง่ดีมากนัก บิตฉันทดสอบเวอร์ชันของเขาเป็นรายการในพจนานุกรม agg และทำงานได้ตามที่ตั้งใจไว้: .agg ({'tp': 'sum', 'BaseWgt': 'max', 'TP_short': lambda col: ',' .join (col)}) ทำวันของฉัน
matthhias

2
หากคุณกำลังพยายามต่อสตริงเข้ากับตัวคั่นบางประเภทฉันพบว่าคำแนะนำ. agg นี้เร็วกว่า. apply มาก สำหรับชุดข้อมูล 600k + สตริงข้อความฉันได้ผลลัพธ์ที่เหมือนกันเร็วขึ้น 5-10 เท่า
ดับเบิ้ลดาวน์

14

คุณสามารถลองสิ่งนี้:

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

2
จากการตรวจสอบ: คุณช่วยอธิบายเพิ่มเติมในคำตอบของคุณได้ไหม
toti08

1
Groupby ถูกนำไปใช้กับคอลัมน์ 'A' และด้วยฟังก์ชัน agg ฉันสามารถใช้ฟังก์ชันที่แตกต่างกันในคอลัมน์ที่แตกต่างกันกล่าวรวมองค์ประกอบในคอลัมน์ 'C' ต่อองค์ประกอบในคอลัมน์ 'C' ในขณะที่แทรก '-' ระหว่างคำ
user3241146

8

วิธีง่ายๆคือ:

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

นี่น่าจะเป็นคำตอบที่ถูกต้อง ช่วยให้คุณตอบได้อย่างหมดจด ขอบคุณมาก!
imsrgadich

เผื่อว่าจะมีคนสนใจเข้าร่วมรายการเป็นสตริง df.groupby(['A','B']).c.unique().apply(lambda x: ';'.join(x)).reset_index()
วิเวก - อนันต์

8

การรวมชื่อด้วย pandas >= 0.25.0

เนื่องจากแพนด้าเวอร์ชัน 0.25.0 เราได้ตั้งชื่อการรวมที่เราสามารถจัดกลุ่มรวมและกำหนดชื่อใหม่ให้กับคอลัมน์ของเราได้ในเวลาเดียวกัน ด้วยวิธีนี้เราจะไม่ได้รับคอลัมน์ MultiIndex และชื่อคอลัมน์มีความหมายมากขึ้นเมื่อพิจารณาจากข้อมูลที่มี:


รวมและรับรายการสตริง

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]

รวมและเข้าร่วมสตริง

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

หากคุณต้องการเขียนทับคอลัมน์ B ใน dataframe สิ่งนี้ควรได้ผล:

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

2

ทำตามคำตอบที่ดีของ @ Erfan เวลาส่วนใหญ่ในการวิเคราะห์ค่ารวมคุณต้องการชุดค่าผสมที่เป็นไปได้เฉพาะของค่าอักขระที่มีอยู่เหล่านี้:

unique_chars = lambda x: ', '.join(x.unique())
(df
 .groupby(['A'])
 .agg({'C': unique_chars}))
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.