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 state
nivel del groupby
total para calcular el sales
total state
de 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
groupby
objeto, pero se puede calcular el porcentaje de una manera más simple - sólogroupby
elstate_office
y dividir lasales
columna por su suma. Copiando el comienzo de la respuesta de Paul H:Devoluciones:
fuente
x
es una tabla de algún tipo, por lo100 * x
que no tiene sentido intuitivamente (especialmente cuando algunas de las celdas contienen cadenas comoAZ
, ...).state_office
es 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 unox
es un subconjunto de esa columna. ¿Tiene sentido?level=0
significaDebe crear un segundo grupo por objeto que se agrupe por estados y luego usar el
div
método:el
level='state'
kwargdiv
le dice a los pandas que transmitan / se unan a la base de marcos de datos en los valores en elstate
nivel del índice.fuente
div
pero 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 unox
en 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
apply
permite 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
* 100
convertirlo en un porcentaje.groupby
objeto 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
sum
el todoDataFrame
y dividir por elstate
total:Devoluciones
Pero tenga en cuenta que esto solo funciona porque todas las columnas que no
state
sean numéricas, lo que permite la suma de todo el DataFrame. Por ejemplo, si seoffice_id
trata de un carácter , aparece un error:fuente
groupby
columna, son numéricas. Pero por lo demás es bastante elegante. ¿Hay alguna manera de hacerlo funcionar con otrasstr
columnas?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_sales
sestate_office_sales
agrupa 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
XX
que 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_totals
es igual astate_total_sales
excepto 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