簡體   English   中英

Pandas groupby應用vs轉換具有特定功能

[英]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對象轉換為新對象。 但事實是,這個新對象應該具有與組相同的大小, 或者可以廣播到塊的大小。

因此,如果使用tuplelist轉換系列,則基本上將轉換對象

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 (列)時起作用:

  1. 'expand':類似列表的結果將變為列。

  2. 'reduce':盡可能返回系列,而不是擴展類似列表的結果。 這與'expand'相反。

  3. 'broadcast':結果將廣播到DataFrame的原始形狀,原始索引和列將被保留。

轉換的結果僅限於某些類型。 [例如它不能listsetSeries等 - 這是不正確的 ,謝謝@RafaelC評論]我不認為這是記錄,但在檢查groupby.pyseries.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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM