J'ai un extrait d'extrait qui fonctionne comme prévu:
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)
Le résultat est:
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,)
Cela fonctionne de façon analogue, si au lieu de Tuple
en transformation, je donne set, frozenset, dict
, mais si je donne list
j'obtiens un résultat complètement inattendu:
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
Il existe une solution de contournement pour obtenir le résultat attendu:
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]
J'ai pensé à la mutabilité/immuabilité (liste/Tuple) mais pour set/frozenset c'est cohérent.
La question est pourquoi cela fonctionne de cette façon?
J'ai rencontré un problème similaire auparavant. Je pense que le problème sous-jacent est que lorsque le nombre d'éléments dans la liste correspond au nombre d'enregistrements dans le groupe, il essaie de décompresser la liste afin que chaque élément de la liste mappe vers un enregistrement dans le groupe.
Par exemple, cela entraînera la décompression de la liste, car le len de la liste correspond à la longueur de chaque groupe:
df.groupby(['label'])[['wave']].transform(lambda x: list(x))
wave
0 1
1 2
2 3
3 4
Cependant, si la longueur de la liste n'est pas la même que pour chaque groupe, vous obtiendrez le comportement souhaité:
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]
Je pense que c'est un effet secondaire de la fonctionnalité de déballage de la liste.
Étant donné que DataFrames
est principalement conçu pour gérer des données 2D, y compris des tableaux au lieu de valeurs scalaires peuvent tomber sur une mise en garde comme celle-ci.
pd.DataFrame.trasnform
est à l'origine implémenté au-dessus de .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
Cependant, transform
renvoie toujours un DataFrame qui doit avoir la même longueur que self, qui est essentiellement l'entrée.
Lorsque vous faites un .agg
sur la fonction DataFrame
, cela fonctionne très bien:
df.groupby('label')['wave'].agg(list)
label
a [1]
b [2, 3]
c [4]
Name: wave, dtype: object
Le problème est introduit lorsque transform
essaie de renvoyer un Series
de même longueur.
Dans le processus de transformation d'un élément groupby
qui est une tranche de self
, puis de concaténation à nouveau, les listes sont décompressées à la même longueur d'index que @Allen mentionné.
Cependant, lorsqu'ils ne s'alignent pas, ne vous décompressez pas:
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]
Une solution de contournement que ce problème pourrait éviter 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]
Je pense que c'est un bug chez les pandas. Pouvez-vous ouvrir un ticket sur leur github page s'il vous plaît?
Au début, je pensais que ce pourrait être le cas, car list
n'est tout simplement pas traité correctement comme argument de .transform
, Mais si je le fais:
def create_list(obj):
print(type(obj))
return obj.to_list()
df.groupby(['label'])[['wave']].transform(create_list)
J'obtiens le même résultat inattendu. Si toutefois la méthode agg
est utilisée, elle fonctionne directement:
df.groupby(['label'])['wave'].agg(list)
Out[179]:
label
a [1]
b [2, 3]
c [4]
Name: wave, dtype: object
Je ne peux pas imaginer que ce soit un comportement voulu.
Btw. Je trouve également le comportement différent suspect, qui apparaît si vous appliquez Tuple à une série groupée et à une trame de données groupée. Par exemple. si transform
est appliqué à une série au lieu d'un DataFrame, le résultat n'est pas non plus une série contenant des listes, mais une série contenant ints
(rappelez-vous pour [['wave']]
qui en crée une -frame dataframe transform(Tuple)
en effet retourné des tuples):
df.groupby(['label'])['wave'].transform(Tuple)
Out[177]:
0 1
1 2
2 3
3 4
Name: wave, dtype: int64
Si je refais ça avec agg
au lieu de transform
ça marche pour ['wave']
Et [['wave']]
J'utilisais la version 0.25.0 sur un système ubuntu X86_64 pour mes tests.