どの関数がgroupby
+ transform
操作に受け入れられるのかわかりません。多くの場合、推測、テスト、何かが機能するまで元に戻すだけですが、ソリューションが機能するかどうかを決定する体系的な方法があるはずです。
最小限の例を示します。最初にgroupby
でapply
+ 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
が必要です。だから私は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ではなく、aggregateしようとしています(これはあなたが意図した方法)。
しかし、コードに入ると、transform
のドキュメントは、
グループチャンクと同じサイズか、グループチャンクのサイズにブロードキャスト可能な結果を返します。
するとき
df.groupby(['a', 'b'])['type'].transform(some_func)
実際には各グループの各pd.Series
オブジェクトを変換するはsome_func
関数を使用して新しいオブジェクトになります。ただし、この新しいオブジェクトは、グループと同じサイズにする必要があります[〜#〜] or [〜#〜]チャンクのサイズにブロードキャスト可能である必要があります。
したがって、Tuple
またはlist
を使用してシリーズを変換する場合、基本的にオブジェクトを変換します
0 1
1 2
2 3
dtype: int64
に
[1,2,3]
ただし、これらの値はそれぞれのインデックスに割り当てられているため、transform
操作に違いがないことがわかります。 .iloc[0]
からpd.Series
値を持つ行は、変換リストから[1,2,3][0]
値を持つようになります(タプルにも同じことが当てはまります)など。orderingおよびsizeここでは重要です。そうしないと、グループを台無しにしてしまうと変換が機能しなくなるからです(これがまさにset
この場合、使用する適切な関数ではありません)。
引用されたテキストの2番目の部分には、「グループチャンクのサイズにブロードキャスト可能」とあります。
つまり、pd.Series
をすべての行で使用できるオブジェクトに変換することもできます。例えば
df.groupby(['a', 'b'])['type'].transform(lambda k: 50)
動作します。どうして? 50
は反復可能ではありませんが、最初のpd.Series
のすべての位置でこの値を繰り返し使用することでbroadcastableです。
Setを使用してapply
できるのはなぜですか?
apply
メソッドには、結果にsizeのこの制約がないためです。実際にはthreeの異なる結果タイプがあり、expand、reduceまたはbroadcast結果。変換ではreduceできないことに注意してください*
デフォルト(
result_type=None
)では、最終的な戻り値の型は、適用された関数の戻り値の型から推測されます。 result_type:{'expand'、 'reduce'、 'broadcast'、None}、デフォルトなしこれらはaxis=1
(列)の場合にのみ機能します:
「展開」:リストのような結果が列に変換されます。
'reduce':リストのような結果を展開するのではなく、可能であればシリーズを返します。これは「展開」の反対です。
「ブロードキャスト」:結果はDataFrameの元の形状にブロードキャストされ、元のインデックスと列は保持されます。
変換の結果は特定のタイプに制限されます。 [たとえば、list
、set
、Series
などにすることはできません。-不正確、コメントありがとうございます@RafaelC]これは文書化されていないと思いますが、groupby.py
とseries.py
のソースコードを調べるときこれらのタイプの制限を見つけることができます。
groupby
から ドキュメント
transform
メソッドは、グループ化されるオブジェクトと同じ(同じサイズ)インデックスが付けられたオブジェクトを返します。変換関数は以下を行う必要があります。
- グループチャンクと同じサイズ、またはグループチャンクのサイズにブロードキャスト可能な結果(たとえば、スカラー、grouped.transform(ラムダx:x.iloc [-1]))。
グループチャンクで列ごとに操作します。変換は、chunk.applyを使用して最初のグループチャンクに適用されます。
グループチャンクでインプレース操作を実行しません。グループチャンクは不変として扱う必要があり、グループチャンクを変更すると予期しない結果が生じる可能性があります。たとえば、fillnaを使用する場合、inplaceはFalseでなければなりません(grouped.transform(lambda x:x.fillna(inplace = False)))。
(オプション)グループチャンク全体で動作します。これがサポートされている場合、2番目のチャンクから始まる高速パスが使用されます。
免責事項:別のエラーが発生しました(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つの値のみを取得します。答えは、コンテナにラップすることです。したがって、オブジェクトの結果のサイズは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行目)は、さらにチェックすることなくこのタイプを渡すためです。したがって、この動作は将来のバージョンでも保持されると考えています。