Considere el siguiente marco de datos:
A B C D
0 foo one 0.162003 0.087469
1 bar one -1.156319 -1.526272
2 foo two 0.833892 -1.666304
3 bar three -2.026673 -0.322057
4 foo two 0.411452 -0.954371
5 bar two 0.765878 -0.095968
6 foo one -0.654890 0.678091
7 foo three -1.789842 -1.130922
Los siguientes comandos funcionan:
> df.groupby('A').apply(lambda x: (x['C'] - x['D']))
> df.groupby('A').apply(lambda x: (x['C'] - x['D']).mean())
pero ninguno de los siguientes trabajos:
> df.groupby('A').transform(lambda x: (x['C'] - x['D']))
ValueError: could not broadcast input array from shape (5) into shape (5,3)
> df.groupby('A').transform(lambda x: (x['C'] - x['D']).mean())
TypeError: cannot concatenate a non-NDFrame object
¿Por qué? El ejemplo en la documentación parece sugerir que llamar transforma un grupo le permite a uno realizar el procesamiento de operaciones en filas:
# Note that the following suggests row-wise operation (x.mean is the column mean)
zscore = lambda x: (x - x.mean()) / x.std()
transformed = ts.groupby(key).transform(zscore)
En otras palabras, pensé que transform es esencialmente un tipo específico de aplicación (el que no se agrega). Donde me equivoco
Como referencia, a continuación se muestra la construcción del marco de datos original anterior:
df = pd.DataFrame({'A' : ['foo', 'bar', 'foo', 'bar',
'foo', 'bar', 'foo', 'foo'],
'B' : ['one', 'one', 'two', 'three',
'two', 'two', 'one', 'three'],
'C' : randn(8), 'D' : randn(8)})

transformdebe devolver un número, una fila o la misma forma que el argumento. si es un número, el número se establecerá en todos los elementos del grupo, si es una fila, se transmitirá a todas las filas del grupo. En su código, la función lambda devuelve una columna que no se puede transmitir al grupo.zscore),transformrecibe una función lambda que supone que cada unoxes un elemento dentro delgroup, y también devuelve un valor por elemento en el grupo. ¿Qué me estoy perdiendo?applypasa todo el df, perotransformpasa cada columna individualmente como una Serie. 2)applypuede devolver cualquier salida de forma (escalar / Serie / Marco de datos / matriz / lista ...), mientras quetransformdebe devolver una secuencia (Serie 1D / matriz / lista) de la misma longitud que el grupo. Es por eso que el OPapply()no necesitatransform(). Esta es una buena pregunta ya que el documento no explicó ambas diferencias claramente. (similar a la distinciónapply/map/applymapu otras cosas ...)Respuestas:
Dos diferencias principales entre
applyytransformHay dos diferencias principales entre los métodos groupby
transformyapply.applyimplícitamente pasa todas las columnas para cada grupo como un DataFrame a la función personalizada.transformpasa cada columna para cada grupo individualmente como una Serie a la función personalizada.applypuede devolver un escalar, o una Serie o un Marco de datos (o una matriz numpy o incluso una lista) .transformdebe devolver una secuencia (una serie, matriz o lista unidimensional) de la misma longitud que el grupo .Por lo tanto,
transformfunciona solo en una serie a la vez yapplyfunciona en todo el DataFrame a la vez.Inspeccionar la función personalizada
Puede ayudar bastante inspeccionar la entrada a su función personalizada que se pasa
applyotransform.Ejemplos
Creemos algunos datos de muestra e inspeccionemos los grupos para que puedan ver de qué estoy hablando:
Creemos una función personalizada simple que imprima el tipo del objeto pasado implícitamente y luego genere un error para que se pueda detener la ejecución.
Ahora pasemos esta función tanto al groupby
applycomo a lostransformmétodos para ver qué objeto se le pasa:Como puede ver, un DataFrame se pasa a la
inspectfunción. Quizás se pregunte por qué el tipo, DataFrame, se imprimió dos veces. Pandas dirige el primer grupo dos veces. Hace esto para determinar si hay una manera rápida de completar el cálculo o no. Este es un detalle menor del que no debe preocuparse.Ahora, hagamos lo mismo con
transformSe pasa una serie, un objeto Pandas totalmente diferente.
Por lo tanto,
transformsolo se permite trabajar con una sola serie a la vez. Es no imposible para que actúe en dos columnas al mismo tiempo. Entonces, si intentamos restar la columnaadesde elbinterior de nuestra función personalizada, obtendríamos un errortransform. Vea abajo:Obtenemos un KeyError cuando los pandas intentan encontrar el índice de la Serie
aque no existe. Puede completar esta operaciónapplyya que tiene todo el DataFrame:El resultado es una serie y un poco confuso ya que se mantiene el índice original, pero tenemos acceso a todas las columnas.
Mostrar el objeto pandas pasado
Puede ayudar aún más mostrar todo el objeto pandas dentro de la función personalizada, para que pueda ver exactamente con qué está operando. Puede usar
printdeclaraciones de Me gusta usar ladisplayfunción delIPython.displaymódulo para que los marcos de datos se generen correctamente en HTML en un cuaderno jupyter:Captura de pantalla:
La transformación debe devolver una secuencia dimensional única del mismo tamaño que el grupo
La otra diferencia es que
transformdebe devolver una secuencia dimensional única del mismo tamaño que el grupo. En este caso particular, cada grupo tiene dos filas, por lo quetransformdebe devolver una secuencia de dos filas. Si no es así, se genera un error:El mensaje de error no es realmente descriptivo del problema. Debe devolver una secuencia de la misma longitud que el grupo. Entonces, una función como esta funcionaría:
Devolver un solo objeto escalar también funciona para
transformSi devuelve un solo escalar de su función personalizada,
transformlo usará para cada una de las filas del grupo:fuente
npno está definido. Supongo que los principiantes agradecerían si incluyeraimport numpy as npen su respuesta.Como me sentí igualmente confundido con la
.transformoperación vs..applyencontré algunas respuestas que arrojan algo de luz sobre el tema. Esta respuesta, por ejemplo, fue muy útil.Mi conclusión hasta ahora es que
.transformfuncionará (o tratará) conSeries(columnas) aisladas unas de otras . Lo que esto significa es que en tus dos últimas llamadas:Solicitó
.transformtomar valores de dos columnas y 'it' en realidad no los 've' a ambos al mismo tiempo (por así decirlo).transformmirará las columnas del marco de datos una por una y devolverá una serie (o grupo de series) 'hecha' de escalares que se repiten variaslen(input_column)veces.Entonces, este escalar, que debería usarse
.transformpara hacer elSeriesresultado de alguna función de reducción aplicada en una entradaSeries(y solo en UNA serie / columna a la vez).Considere este ejemplo (en su marco de datos):
rendirá:
Que es exactamente lo mismo que si lo usaras solo en una columna a la vez:
flexible:
Tenga
.applyen cuenta que en el último ejemplo (df.groupby('A')['C'].apply(zscore)) funcionaría exactamente de la misma manera, pero fallaría si intentara usarlo en un marco de datos:da error:
Entonces, ¿dónde más es
.transformútil? El caso más simple es tratar de asignar los resultados de la función de reducción al marco de datos original.flexible:
Tratando el mismo con
.applydaríaNaNsensum_C. Porque.applydevolvería un reducidoSeries, que no sabe cómo transmitir de nuevo:dando:
También hay casos en los que
.transformse usa para filtrar los datos:Espero que esto agregue un poco más de claridad.
fuente
.transform()también podría usarse para completar valores perdidos. Especialmente si desea transmitir la media grupal o la estadística grupal a losNaNvalores de ese grupo. Desafortunadamente, la documentación de los pandas tampoco me fue útil..groupby().filter()hace lo mismo. Gracias por tu explicación,.apply()y también.transform()me confunde mucho.df.groupby().transform()no puede funcionar para un subgrupo df, siempre obtengo el errorValueError: transform must return a scalar value for each groupporquetransformve las columnas una por unaVoy a usar un fragmento muy simple para ilustrar la diferencia:
El DataFrame se ve así:
Hay 3 ID de clientes en esta tabla, cada cliente realizó tres transacciones y pagó 1,2,3 dólares cada vez.
Ahora, quiero encontrar el pago mínimo realizado por cada cliente. Hay dos formas de hacerlo:
Utilizando
apply:grouping.min ()
El regreso se ve así:
Utilizando
transform:grouping.transform (min)
El regreso se ve así:
Ambos métodos devuelven un
Seriesobjeto, pero ellengthdel primero es 3 y ellengthdel segundo es 9.Si desea responder
What is the minimum price paid by each customer, entonces elapplymétodo es el más adecuado para elegir.Si quieres responder
What is the difference between the amount paid for each transaction vs the minimum payment, entonces quieres usartransform, porque:Applyno funciona aquí simplemente porque devuelve una Serie de tamaño 3, pero la longitud del df original es 9. No puede integrarlo fácilmente al df original.fuente
es como
o
fuente