Esto es obviamente simple, pero como un nuevo numpy me estoy atascando.
Tengo un archivo CSV que contiene 3 columnas, el estado, la identificación de la oficina y las ventas de esa oficina.
Quiero calcular el porcentaje de ventas por oficina en un estado determinado (el total de todos los porcentajes en cada estado es del 100%).
df = pd.DataFrame({'state': ['CA', 'WA', 'CO', 'AZ'] * 3,
'office_id': range(1, 7) * 2,
'sales': [np.random.randint(100000, 999999)
for _ in range(12)]})
df.groupby(['state', 'office_id']).agg({'sales': 'sum'})
Esto devuelve:
sales
state office_id
AZ 2 839507
4 373917
6 347225
CA 1 798585
3 890850
5 454423
CO 1 819975
3 202969
5 614011
WA 2 163942
4 369858
6 959285
Parece que no puedo entender cómo "alcanzar" el statenivel del groupbytotal para calcular el salestotal statede la fracción.

df['sales'] / df.groupby('state')['sales'].transform('sum')Parece ser la respuesta más clara.Respuestas:
La respuesta de Pablo H es justo que usted tendrá que hacer un segundo
groupbyobjeto, pero se puede calcular el porcentaje de una manera más simple - sólogroupbyelstate_officey dividir lasalescolumna por su suma. Copiando el comienzo de la respuesta de Paul H:Devoluciones:
fuente
xes una tabla de algún tipo, por lo100 * xque no tiene sentido intuitivamente (especialmente cuando algunas de las celdas contienen cadenas comoAZ, ...).state_officees una serie con un índice múltiple, por lo que es solo una columna cuyos valores son todos numéricos. Después de hacer el groupby, cada unoxes un subconjunto de esa columna. ¿Tiene sentido?level=0significaDebe crear un segundo grupo por objeto que se agrupe por estados y luego usar el
divmétodo:el
level='state'kwargdivle dice a los pandas que transmitan / se unan a la base de marcos de datos en los valores en elstatenivel del índice.fuente
divpero conlevel=["index1", "index2"]pero me dice esoJoin on level between two MultiIndex objects is ambiguous.Para ser conciso, usaría SeriesGroupBy:
Para varios grupos, debe usar transform (usando Radical's df ):
Esto parece ser un poco más eficaz que las otras respuestas (solo menos del doble de la velocidad de la respuesta de Radical, para mí ~ 0.08s).
fuente
Creo que esto necesita una evaluación comparativa. Usando el DataFrame original de OP,
1st Andy Hayden
Como comentó su respuesta, Andy aprovecha al máximo la vectorización y la indexación de pandas.
3,42 ms ± 16,7 µs por bucle
(media ± desviación estándar de 7 corridas, 100 bucles cada una)
2nd Paul H
4,66 ms ± 24,4 µs por bucle
(media ± desviación estándar de 7 corridas, 100 bucles cada una)
3er exp.
Esta es la respuesta más lenta ya que calcula
x.sum()para cada unoxen el nivel 0.Para mí, esta sigue siendo una respuesta útil, aunque no en su forma actual. Para un EDA rápido en conjuntos de datos más pequeños, le
applypermite usar el método de encadenamiento para escribir esto en una sola línea. Por lo tanto, eliminamos la necesidad de decidir el nombre de una variable, que en realidad es computacionalmente costoso para su recurso más valioso (¡su cerebro!).Aquí está la modificación,
10,6 ms ± 81,5 µs por bucle
(media ± desviación estándar de 7 corridas, 100 bucles cada una)
Así que a nadie le importan unos 6 ms en un pequeño conjunto de datos. Sin embargo, esto es una velocidad 3 veces mayor y, en un conjunto de datos más grande con un grupo de cardinalidad alto, esto hará una gran diferencia.
Agregando al código anterior, creamos un DataFrame con forma (12,000,000, 3) con 14412 categorías estatales y 600 office_ids,
Usando Andy,
2 s ± 10.4 ms por ciclo
(media ± estándar de desarrollo de 7 carreras, 1 ciclo cada una)
y exp1orer
19 s ± 77.1 ms por ciclo
(media ± estándar de desarrollo de 7 carreras, 1 ciclo cada una)
Así que ahora vemos que x10 se acelera en grandes conjuntos de datos de alta cardinalidad.
¡Asegúrate de usar estas tres respuestas en UV si estas es UV!
fuente
(Esta solución está inspirada en este artículo https://pbpython.com/pandas_transform.html )
Creo que la siguiente solución es la más simple (y probablemente la más rápida) usando
transformation:Entonces
transformation, usando , la solución es 1-liner:Y si imprimes:
fuente
transform('max')Sé que esta es una pregunta antigua, pero la respuesta de exp1orer es muy lenta para los conjuntos de datos con un gran número de grupos únicos (probablemente debido a la lambda). Construí a partir de su respuesta para convertirlo en un cálculo de matriz, ¡así que ahora es súper rápido! A continuación se muestra el código de ejemplo:
Cree el marco de datos de prueba con 50,000 grupos únicos
Cuando se agrupa se ve así:
Método de matriz para encontrar el porcentaje:
Este método dura aproximadamente ~ 0.15 segundos
Método de respuesta superior (usando la función lambda):
Este método tarda aproximadamente ~ 21 segundos en producir el mismo resultado.
El resultado:
fuente
Me doy cuenta de que ya hay buenas respuestas aquí.
Sin embargo, me gustaría contribuir con la mía, porque considero que para una pregunta simple y elemental como esta, debería haber una solución corta que sea comprensible de un vistazo.
También debería funcionar de manera que pueda agregar los porcentajes como una nueva columna, dejando el resto del marco de datos intacto. Por último, pero no menos importante, debe generalizarse de manera obvia en el caso de que haya más de un nivel de agrupación (por ejemplo, estado y país en lugar de solo estado).
El siguiente fragmento cumple estos criterios:
Tenga en cuenta que si todavía usa Python 2, deberá reemplazar la x en el denominador del término lambda por float (x).
fuente
* 100convertirlo en un porcentaje.groupbyobjeto temporal , es súper conciso y se lee de forma muy lógica de izquierda a derecha.La forma más elegante de encontrar porcentajes en columnas o índices es usar
pd.crosstab.Data de muestra
El marco de datos de salida es así
Simplemente especifique el índice, las columnas y los valores para agregar. La palabra clave normalizar calculará el% en el índice o las columnas según el contexto.
fuente
Puedes
sumel todoDataFramey dividir por elstatetotal:Devoluciones
Pero tenga en cuenta que esto solo funciona porque todas las columnas que no
statesean numéricas, lo que permite la suma de todo el DataFrame. Por ejemplo, si seoffice_idtrata de un carácter , aparece un error:fuente
groupbycolumna, son numéricas. Pero por lo demás es bastante elegante. ¿Hay alguna manera de hacerlo funcionar con otrasstrcolumnas?Creo que esto haría el truco en 1 línea:
fuente
La forma simple que he usado es una fusión después de que los 2 groupby hagan una división simple.
fuente
Devoluciones:
fuente
Como alguien que también está aprendiendo pandas, encontré las otras respuestas un poco implícitas, ya que los pandas ocultan la mayor parte del trabajo detrás de escena. A saber, cómo funciona la operación haciendo coincidir automáticamente los nombres de las columnas y los índices. Este código debe ser equivalente a una versión paso a paso de la respuesta aceptada de @ exp1orer
Con el
df, lo llamaré por el aliasstate_office_sales:state_total_salessestate_office_salesagrupa por sumas totales enindex level 0(extremo izquierdo).Debido a que los dos marcos de datos comparten un nombre de índice y un panda de nombre de columna, encontrarán las ubicaciones apropiadas a través de índices compartidos como:
Para ilustrar esto aún mejor, aquí hay un total parcial con un
XXque no tiene equivalente. Los pandas coincidirán con la ubicación en función de los nombres de índice y columna, donde no hay superposición de pandas lo ignorarán:Esto se vuelve muy claro cuando no hay índices o columnas compartidas. Aquí
missing_index_totalses igual astate_total_salesexcepto que no tiene un nombre de índice.fuente
Solución de una línea:
Esto devuelve una serie de relaciones por oficina: se puede usar por sí solo o se puede asignar al marco de datos original.
fuente