python pandas transform pandas-groupby

python - Los pandas transforman un comportamiento inconsistente para la lista



pandas-groupby (3)

Creo que es un error en los pandas. ¿Puedes abrir un boleto en su página de Github por favor?

Al principio pensé que podría serlo, porque la list simplemente no se maneja correctamente como argumento para .transform , pero si lo hago:

def create_list(obj): print(type(obj)) return obj.to_list() df.groupby([''label''])[[''wave'']].transform(create_list)

Obtengo el mismo resultado inesperado. Sin embargo, si se usa el método agg , funciona directamente:

df.groupby([''label''])[''wave''].agg(list) Out[179]: label a [1] b [2, 3] c [4] Name: wave, dtype: object

No puedo imaginar que este sea un comportamiento intencionado.

Por cierto. También encuentro sospechoso el comportamiento diferente, que se muestra si aplica tupla a una serie agrupada y un marco de datos agrupado. Por ejemplo, si la transform se aplica a una serie en lugar de un DataFrame, el resultado tampoco es una serie que contenga listas, sino una serie que contenga ints (recuerde para [[''wave'']] que crea una transform(tuple) marco de datos de una sola columna transform(tuple) tuplas devueltas):

df.groupby([''label''])[''wave''].transform(tuple) Out[177]: 0 1 1 2 2 3 3 4 Name: wave, dtype: int64

Si vuelvo a hacer eso con agg lugar de transform , funciona tanto para [''wave''] como [[''wave'']]

Estaba usando la versión 0.25.0 en un sistema ubuntu X86_64 para mis pruebas.

Tengo un fragmento de muestra que funciona como se esperaba:

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)

El resultado es:

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,)

Funciona de manera análoga, si en lugar de tuple en la transformación doy set, frozenset, dict , pero si doy la list obtengo un resultado completamente inesperado:

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

Hay una solución alternativa para obtener el resultado esperado:

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]

Pensé en la mutabilidad / inmutabilidad (lista / tupla) pero para set / frozenset es consistente.

La pregunta es ¿por qué funciona de esta manera?


Dado que los DataFrames están diseñados principalmente para manejar datos 2D, incluir matrices en lugar de valores escalares podría tropezar con una advertencia como esta.

pd.DataFrame.trasnform se implementa originalmente sobre .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

Sin embargo, transform siempre devuelve un DataFrame que debe tener la misma longitud que self, que es esencialmente la entrada.

Cuando haces una función .agg en el DataFrame , funciona bien:

df.groupby(''label'')[''wave''].agg(list) label a [1] b [2, 3] c [4] Name: wave, dtype: object

El problema se introduce cuando transform intenta devolver una Series con la misma longitud.

En el proceso para transformar un elemento groupby que es un segmento de self y luego concatenar esto nuevamente, las listas se descomprimen en la misma longitud de índice que @Allen mencionó.

Sin embargo, cuando no se alinean, no se desempaque:

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]

Una solución alternativa a este problema podría ser evitar la 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]


Me he encontrado con un problema similar antes. Creo que el problema subyacente es que cuando el número de elementos en la lista coincide con el número de registros en el grupo, intenta desempaquetar la lista para que cada elemento de la lista se asigne a un registro en el grupo.

Por ejemplo, esto hará que la lista se descomprima, ya que la longitud de la lista coincide con la longitud de cada grupo:

df.groupby([''label''])[[''wave'']].transform(lambda x: list(x)) wave 0 1 1 2 2 3 3 4

Sin embargo, si la longitud de la lista no es la misma que la de cada grupo, obtendrá el comportamiento deseado:

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]

Creo que este es un efecto secundario de la funcionalidad de desempaquetado de la lista.