期待どおりに機能するサンプルスニペットがあります。
import pandas as pd
df = pd.DataFrame(data={'label': ['a', 'b', 'b', 'c'], 'wave': [1, 2, 3, 4], 'y': [0,0,0,0]})
df['new'] = df.groupby(['label'])[['wave']].transform(Tuple)
結果は次のとおりです。
label wave y new
0 a 1 0 (1,)
1 b 2 0 (2, 3)
2 b 3 0 (2, 3)
3 c 4 0 (4,)
変換でTuple
の代わりにset, frozenset, dict
を指定すると、同様に機能しますが、list
を指定すると、完全に予期しない結果が得られます。
df['new'] = df.groupby(['label'])[['wave']].transform(list)
label wave y new
0 a 1 0 1
1 b 2 0 2
2 b 3 0 3
3 c 4 0 4
期待される結果を得るための回避策があります:
df['new'] = df.groupby(['label'])[['wave']].transform(Tuple)['wave'].apply(list)
label wave y new
0 a 1 0 [1]
1 b 2 0 [2, 3]
2 b 3 0 [2, 3]
3 c 4 0 [4]
可変性/不変性(リスト/タプル)について考えましたが、セット/フローズンセットでは一貫しています。
問題は、なぜこのように機能するのかということです。
以前に同様の問題に遭遇したことがあります。私が考える根本的な問題は、リストの要素の数がグループのレコードの数と一致すると、リストの各要素がグループのレコードにマップされるようにリストをアンパックしようとすることです。
たとえば、リストのlenが各グループの長さと一致するため、これによりリストがアンパックされます。
df.groupby(['label'])[['wave']].transform(lambda x: list(x))
wave
0 1
1 2
2 3
3 4
ただし、リストの長さが各グループと同じでない場合は、目的の動作が得られます。
df.groupby(['label'])[['wave']].transform(lambda x: list(x)+[0])
wave
0 [1, 0]
1 [2, 3, 0]
2 [2, 3, 0]
3 [4, 0]
これは、リストの展開機能の副作用だと思います。
DataFrames
は主に2Dデータを処理するように設計されているため、スカラー値の代わりに配列を含めると、このような警告に遭遇する可能性があります。
pd.DataFrame.trasnform
は元々.agg
の上に実装されています:
# pandas/core/generic.py
@Appender(_shared_docs["transform"] % dict(axis="", **_shared_doc_kwargs))
def transform(self, func, *args, **kwargs):
result = self.agg(func, *args, **kwargs)
if is_scalar(result) or len(result) != len(self):
raise ValueError("transforms cannot produce " "aggregated results")
return result
ただし、transform
は常に、本質的に入力である自分自身と同じ長さを持つ必要があるDataFrameを返します。
DataFrame
で.agg
関数を実行すると、正常に機能します。
df.groupby('label')['wave'].agg(list)
label
a [1]
b [2, 3]
c [4]
Name: wave, dtype: object
この問題は、transform
が同じ長さのSeries
を返そうとすると発生します。
groupby
からのスライスであるself
要素を変換し、これを再度連結するプロセスでは、リストは@Allenと同じ長さのインデックスにアンパックされます。
ただし、それらが揃わない場合は、解凍しないでください。
df.groupby(['label'])[['wave']].transform(lambda x: list(x) + [1])
wave
0 [1, 1]
1 [2, 3, 1]
2 [2, 3, 1]
3 [4, 1]
この問題の回避策はtransform
を回避することです:
df = pd.DataFrame(data={'label': ['a', 'b', 'b', 'c'], 'wave': [1, 2, 3, 4], 'y': [0,0,0,0]})
df = df.merge(df.groupby('label')['wave'].agg(list).rename('new'), on='label')
df
label wave y new
0 a 1 0 [1]
1 b 2 0 [2, 3]
2 b 3 0 [2, 3]
3 c 4 0 [4]
それはパンダのバグだと思います。 the github ページでチケットを開けますか?
list
が_.transform
_への引数として正しく処理されていないため、最初はそうであると考えましたが、そうした場合:
_def create_list(obj):
print(type(obj))
return obj.to_list()
df.groupby(['label'])[['wave']].transform(create_list)
_
同じ予期しない結果になります。ただし、agg
メソッドを使用すると、直接機能します。
_df.groupby(['label'])['wave'].agg(list)
Out[179]:
label
a [1]
b [2, 3]
c [4]
Name: wave, dtype: object
_
これが意図された動作であるとは思えません。
ところでまた、グループ化されたシリーズおよびグループ化されたデータフレームにタプルを適用した場合に表示される、異なる動作が疑わしいこともわかりました。例えば。 transform
がDataFrameではなくシリーズに適用される場合、結果もリストを含むシリーズではなく、ints
を含むシリーズです(_[['wave']]
_を覚えておくと、 columed dataframe transform(Tuple)
実際に返されたタプル):
_df.groupby(['label'])['wave'].transform(Tuple)
Out[177]:
0 1
1 2
2 3
3 4
Name: wave, dtype: int64
_
agg
の代わりにtransform
を使用してこれをもう一度行うと、_['wave']
_と_[['wave']]
_の両方で機能します
私のテストでは、ubuntu X86_64システムでバージョン0.25.0を使用していました。