[英]Pandas groupby apply vs transform with specific functions
我不明白groupby
+ transform
操作可以接受哪些函數。 通常情況下,我最終只是猜測,測試,還原直到某些工作正常,但我覺得應該有一種系統的方法來確定解決方案是否有效。
這是一個最小的例子。 首先讓我們使用groupby
+ apply
with set
:
df = pd.DataFrame({'a': [1,2,3,1,2,3,3], 'b':[1,2,3,1,2,3,3], 'type':[1,0,1,0,1,0,1]})
g = df.groupby(['a', 'b'])['type'].apply(set)
print(g)
a b
1 1 {0, 1}
2 2 {0, 1}
3 3 {0, 1}
這工作正常,但我希望在原始數據幀的新列中按set
計算得到的結果set
。 所以我嘗試使用transform
:
df['g'] = df.groupby(['a', 'b'])['type'].transform(set)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
---> 23 df['g'] = df.groupby(['a', 'b'])['type'].transform(set)
TypeError: int() argument must be a string, a bytes-like object or a number, not 'set'
這是我在Pandas v0.19.0中看到的錯誤。 在v0.23.0中,我看到TypeError: 'set' type is unordered
。 當然,我可以映射一個特定的索引來實現我的結果:
g = df.groupby(['a', 'b'])['type'].apply(set)
df['g'] = df.set_index(['a', 'b']).index.map(g.get)
print(df)
a b type g
0 1 1 1 {0, 1}
1 2 2 0 {0, 1}
2 3 3 1 {0, 1}
3 1 1 0 {0, 1}
4 2 2 1 {0, 1}
5 3 3 0 {0, 1}
6 3 3 1 {0, 1}
但我認為transform
的好處是避免這種顯式映射。 我哪里做錯了?
我相信,首先,使用這些功能有一定的直覺空間,因為它們非常有意義。
在您的第一個結果中,您實際上並沒有嘗試轉換您的值,而是聚合它們(這將按照您的預期方式工作)。
但是進入代碼, transform
文檔就是這樣說的
返回與組塊大小相同或可廣播到組塊大小的結果。
當你這樣做
df.groupby(['a', 'b'])['type'].transform(some_func)
實際上,您正在使用some_func
函數將每個組中的每個pd.Series
對象轉換為新對象。 但事實是,這個新對象應該具有與組相同的大小, 或者可以廣播到塊的大小。
因此,如果使用tuple
或list
轉換系列,則基本上將轉換對象
0 1
1 2
2 3
dtype: int64
成
[1,2,3]
但請注意,這些值現在已分配回各自的索引,這就是您在transform
操作中看不到差異的原因。 來自pd.Series
的.iloc[0]
值的pd.Series
現在將具有變換列表中的[1,2,3][0]
值(同樣適用於元組)等。請注意, 排序和大小這里很重要,因為否則你可能搞亂你的組並且轉換不起作用(這正是為什么set
不是一個適當的函數來使用就是這種情況)。
引用文本的第二部分說“可以播放到組塊的大小”。
這意味着您還可以將pd.Series
轉換為可在所有行中使用的對象。 例如
df.groupby(['a', 'b'])['type'].transform(lambda k: 50)
會工作。 為什么? 即使50
不可迭代,也可以在初始pd.Series
所有位置重復使用該值進行廣播 。
為什么你apply
使用套裝?
因為apply
方法在結果中沒有這個大小約束。 它實際上有三種不同的結果類型,它可以推斷您是要擴展 , 縮小還是廣播結果。 請注意,您無法減少轉換*
默認情況下(
result_type=None
),從應用函數的返回類型推斷出最終返回類型。 result_type:{'expand','reduce','broadcast',None},默認無這些僅在axis=1
(列)時起作用:
'expand':類似列表的結果將變為列。
'reduce':盡可能返回系列,而不是擴展類似列表的結果。 這與'expand'相反。
'broadcast':結果將廣播到DataFrame的原始形狀,原始索引和列將被保留。
轉換的結果僅限於某些類型。 [例如它不能list
, set
, Series
等 - 這是不正確的 ,謝謝@RafaelC評論]我不認為這是記錄,但在檢查groupby.py
和series.py
的源代碼時series.py
你可以找到這些類型的限制。
來自groupby
文檔
transform
方法返回一個對象,該對象的索引與被分組的對象相同(大小相同)。 轉換函數必須:
- 返回與組塊大小相同或可廣播到組塊大小的結果 (例如,標量,grouped.transform(lambda x:x.iloc [-1]))。
在組塊上逐列操作。 使用chunk.apply將轉換應用於第一個組塊。
不對組塊執行就地操作。 應將組塊視為不可變組,並且對組塊的更改可能會產生意外結果。 例如,使用fillna時,inplace必須為False(grouped.transform(lambda x:x.fillna(inplace = False)))。
(可選)對整個組塊進行操作。 如果支持,則從第二個塊開始使用快速路徑。
免責聲明:我得到了不同的錯誤( pandas
版本0.23.1):
df['g'] = df.groupby(['a', 'b'])['type'].transform(set)
File "***/lib/python3.6/site-packages/pandas/core/groupby/groupby.py", line 3661, in transform
s = klass(res, indexer) s = klass(res, indexer)
File "***/lib/python3.6/site-packages/pandas/core/series.py", line 242, in __init__
"".format(data.__class__.__name__))
TypeError: 'set' type is unordered
將組轉換為集合后, pandas
無法將其廣播到Series
,因為它是無序的(並且具有與組塊不同的維度)。 如果我們強制它進入一個列表,它將變得與組塊大小相同,並且我們每行只得到一個值。 答案是將它包裝在一些容器中,因此對象的最終大小將變為1,然后pandas
將能夠廣播它:
df['g'] = df.groupby(['a', 'b'])['type'].transform(lambda x: np.array(set(x)))
print(df)
a b type g
0 1 1 1 {0, 1}
1 2 2 0 {0, 1}
2 3 3 1 {0, 1}
3 1 1 0 {0, 1}
4 2 2 1 {0, 1}
5 3 3 0 {0, 1}
6 3 3 1 {0, 1}
為什么我選擇np.array
作為容器? 因為series.py
(第205:206行)傳遞此類型而不進一步檢查。 所以我相信這種行為將在未來版本中保留。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.