grouper groupby examples example agg python pandas

groupby - python group by agg



Porcentaje de pandas del total con groupby (6)

Esto es obviamente simple, pero como un recién nacido numpy me estoy quedando atrapado.

Tengo un archivo CSV que contiene 3 columnas, el estado, el ID de Office 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 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 " groupby " al nivel state del groupby para sumar las sales de todo el state para calcular la fracción.


La forma simple que he usado es una fusión después de que los 2 groupby hacen una división simple.

import numpy as np import pandas as pd np.random.seed(0) df = pd.DataFrame({''state'': [''CA'', ''WA'', ''CO'', ''AZ''] * 3, ''office_id'': list(range(1, 7)) * 2, ''sales'': [np.random.randint(100000, 999999) for _ in range(12)]}) state_office = df.groupby([''state'', ''office_id''])[''sales''].sum().reset_index() state = df.groupby([''state''])[''sales''].sum().reset_index() state_office = state_office.merge(state, left_on=''state'', right_on =''state'', how = ''left'') state_office[''sales_ratio''] = 100*(state_office[''sales_x'']/state_office[''sales_y'']) state office_id sales_x sales_y sales_ratio 0 AZ 2 222579 1310725 16.981365 1 AZ 4 252315 1310725 19.250033 2 AZ 6 835831 1310725 63.768601 3 CA 1 405711 2098663 19.331879 4 CA 3 710581 2098663 33.858747 5 CA 5 982371 2098663 46.809373 6 CO 1 404137 1096653 36.851857 7 CO 3 217952 1096653 19.874290 8 CO 5 474564 1096653 43.273852 9 WA 2 535829 1543854 34.707233 10 WA 4 548242 1543854 35.511259 11 WA 6 459783 1543854 29.781508


Necesita hacer un segundo grupo por objeto que agrupe por estados y luego use el método div :

import numpy as np import pandas as pd np.random.seed(0) df = pd.DataFrame({''state'': [''CA'', ''WA'', ''CO'', ''AZ''] * 3, ''office_id'': list(range(1, 7)) * 2, ''sales'': [np.random.randint(100000, 999999) for _ in range(12)]}) state_office = df.groupby([''state'', ''office_id'']).agg({''sales'': ''sum''}) state = df.groupby([''state'']).agg({''sales'': ''sum''}) state_office.div(state, level=''state'') * 100 sales state office_id AZ 2 16.981365 4 19.250033 6 63.768601 CA 1 19.331879 3 33.858747 5 46.809373 CO 1 36.851857 3 19.874290 5 43.273852 WA 2 34.707233 4 35.511259 6 29.781508

el level=''state'' kwarg en div le dice a los pandas que transmitan / unan la base de datos en los valores en el nivel de state del índice.


Para ser conciso, usaría SeriesGroupBy:

In [11]: c = df.groupby([''state'', ''office_id''])[''sales''].sum().rename("count") In [12]: c Out[12]: state office_id AZ 2 925105 4 592852 6 362198 CA 1 819164 3 743055 5 292885 CO 1 525994 3 338378 5 490335 WA 2 623380 4 441560 6 451428 Name: count, dtype: int64 In [13]: c / c.groupby(level=0).sum() Out[13]: state office_id AZ 2 0.492037 4 0.315321 6 0.192643 CA 1 0.441573 3 0.400546 5 0.157881 CO 1 0.388271 3 0.249779 5 0.361949 WA 2 0.411101 4 0.291196 6 0.297703 Name: count, dtype: float64

Para múltiples grupos, debe usar la transformación (usando Radical''s df ):

In [21]: c = df.groupby(["Group 1","Group 2","Final Group"])["Numbers I want as percents"].sum().rename("count") In [22]: c / c.groupby(level=[0, 1]).transform("sum") Out[22]: Group 1 Group 2 Final Group AAHQ BOSC OWON 0.331006 TLAM 0.668994 MQVF BWSI 0.288961 FXZM 0.711039 ODWV NFCH 0.262395 ... Name: count, dtype: float64

Esto parece ser un poco más eficiente que las otras respuestas (apenas menos del doble de la velocidad de la respuesta de Radical, para mí ~ 0.08s).


Puede sum el DataFrame completo y dividir por el total del state :

# Copying setup from Paul H answer import numpy as np import pandas as pd np.random.seed(0) df = pd.DataFrame({''state'': [''CA'', ''WA'', ''CO'', ''AZ''] * 3, ''office_id'': list(range(1, 7)) * 2, ''sales'': [np.random.randint(100000, 999999) for _ in range(12)]}) # Add a column with the sales divided by state total sales. df[''sales_ratio''] = (df / df.groupby([''state'']).transform(sum))[''sales''] df

Devoluciones

office_id sales state sales_ratio 0 1 405711 CA 0.193319 1 2 535829 WA 0.347072 2 3 217952 CO 0.198743 3 4 252315 AZ 0.192500 4 5 982371 CA 0.468094 5 6 459783 WA 0.297815 6 1 404137 CO 0.368519 7 2 222579 AZ 0.169814 8 3 710581 CA 0.338587 9 4 548242 WA 0.355113 10 5 474564 CO 0.432739 11 6 835831 AZ 0.637686

Pero tenga en cuenta que esto solo funciona porque todas las columnas que no sean state son numéricas, lo que permite la suma de todo el DataFrame. Por ejemplo, si office_id es un caracter en su lugar, se obtiene un error:

df.office_id = df.office_id.astype(str) df[''sales_ratio''] = (df / df.groupby([''state'']).transform(sum))[''sales'']

TypeError: tipo (s) de operandos no soportados para /: ''str'' y ''str''


Sé que esta es una pregunta antigua, pero exp1orer''s respuesta exp1orer''s es muy lenta para los conjuntos de datos con un gran número de grupos únicos (probablemente debido a la lambda). Construí su respuesta para convertirla en un cálculo de matriz, ¡así que ahora es súper rápido! A continuación se muestra el código de ejemplo:

Crea el dataframe de prueba con 50,000 grupos únicos

import random import string import pandas as pd import numpy as np np.random.seed(0) # This is the total number of groups to be created NumberOfGroups = 50000 # Create a lot of groups (random strings of 4 letters) Group1 = [''''.join(random.choice(string.ascii_uppercase) for _ in range(4)) for x in range(NumberOfGroups/10)]*10 Group2 = [''''.join(random.choice(string.ascii_uppercase) for _ in range(4)) for x in range(NumberOfGroups/2)]*2 FinalGroup = [''''.join(random.choice(string.ascii_uppercase) for _ in range(4)) for x in range(NumberOfGroups)] # Make the numbers NumbersForPercents = [np.random.randint(100, 999) for _ in range(NumberOfGroups)] # Make the dataframe df = pd.DataFrame({''Group 1'': Group1, ''Group 2'': Group2, ''Final Group'': FinalGroup, ''Numbers I want as percents'': NumbersForPercents})

Cuando se agrupa, se ve así:

Numbers I want as percents Group 1 Group 2 Final Group AAAH AQYR RMCH 847 XDCL 182 DQGO ALVF 132 AVPH 894 OVGH NVOO 650 VKQP 857 VNLY HYFW 884 MOYH 469 XOOC GIDS 168 HTOY 544 AACE HNXU RAXK 243 YZNK 750 NOYI NYGC 399 ZYCI 614 QKGK CRLF 520 UXNA 970 TXAR MLNB 356 NMFJ 904 VQYG NPON 504 QPKQ 948 ... [50000 rows x 1 columns]

Método de matriz para encontrar el porcentaje:

# Initial grouping (basically a sorted version of df) PreGroupby_df = df.groupby(["Group 1","Group 2","Final Group"]).agg({''Numbers I want as percents'': ''sum''}).reset_index() # Get the sum of values for the "final group", append "_Sum" to it''s column name, and change it into a dataframe (.reset_index) SumGroup_df = df.groupby(["Group 1","Group 2"]).agg({''Numbers I want as percents'': ''sum''}).add_suffix(''_Sum'').reset_index() # Merge the two dataframes Percents_df = pd.merge(PreGroupby_df, SumGroup_df) # Divide the two columns Percents_df["Percent of Final Group"] = Percents_df["Numbers I want as percents"] / Percents_df["Numbers I want as percents_Sum"] * 100 # Drop the extra _Sum column Percents_df.drop(["Numbers I want as percents_Sum"], inplace=True, axis=1)

Este método toma aproximadamente ~ 0.15 segundos

Método de respuesta superior (usando la función lambda):

state_office = df.groupby([''Group 1'',''Group 2'',''Final Group'']).agg({''Numbers I want as percents'': ''sum''}) state_pcts = state_office.groupby(level=[''Group 1'',''Group 2'']).apply(lambda x: 100 * x / float(x.sum()))

Este método toma aproximadamente ~ 21 segundos para producir el mismo resultado.

El resultado:

Group 1 Group 2 Final Group Numbers I want as percents Percent of Final Group 0 AAAH AQYR RMCH 847 82.312925 1 AAAH AQYR XDCL 182 17.687075 2 AAAH DQGO ALVF 132 12.865497 3 AAAH DQGO AVPH 894 87.134503 4 AAAH OVGH NVOO 650 43.132050 5 AAAH OVGH VKQP 857 56.867950 6 AAAH VNLY HYFW 884 65.336290 7 AAAH VNLY MOYH 469 34.663710 8 AAAH XOOC GIDS 168 23.595506 9 AAAH XOOC HTOY 544 76.404494


La respuesta de Paul H es correcta, tendrá que hacer un segundo groupby objeto, pero puede calcular el porcentaje de una manera más simple: solo groupby la state_office y dividir la columna de sales por su suma. Copiando el comienzo de la respuesta de Paul H:

# From Paul H import numpy as np import pandas as pd np.random.seed(0) df = pd.DataFrame({''state'': [''CA'', ''WA'', ''CO'', ''AZ''] * 3, ''office_id'': list(range(1, 7)) * 2, ''sales'': [np.random.randint(100000, 999999) for _ in range(12)]}) state_office = df.groupby([''state'', ''office_id'']).agg({''sales'': ''sum''}) # Change: groupby state_office and divide by sum state_pcts = state_office.groupby(level=0).apply(lambda x: 100 * x / float(x.sum()))

Devoluciones:

sales state office_id AZ 2 16.981365 4 19.250033 6 63.768601 CA 1 19.331879 3 33.858747 5 46.809373 CO 1 36.851857 3 19.874290 5 43.273852 WA 2 34.707233 4 35.511259 6 29.781508