python - help - pandas group by multiple columns
AgregaciĆ³n en pandas (1)
Pregunta 1
¿Cómo realizar la agregación con pandas?
Documentación de agregación ampliada.
Las funciones de agregación son las que reducen la dimensión de los objetos devueltos. Significa que las Series / DataFrame de salida tienen menos o las mismas filas que las originales. Algunas funciones comunes de agregación se tabulan a continuación:
Function Description mean() Compute mean of groups sum() Compute sum of group values size() Compute group sizes count() Compute count of group std() Standard deviation of groups var() Compute variance of groups sem() Standard error of the mean of groups describe() Generates descriptive statistics first() Compute first of group values last() Compute last of group values nth() Take nth value, or a subset if n is a list min() Compute min of group values max() Compute max of group values
np.random.seed(123)
df = pd.DataFrame({''A'' : [''foo'', ''foo'', ''bar'', ''foo'', ''bar'', ''foo''],
''B'' : [''one'', ''two'', ''three'',''two'', ''two'', ''one''],
''C'' : np.random.randint(5, size=6),
''D'' : np.random.randint(5, size=6),
''E'' : np.random.randint(5, size=6)})
print (df)
A B C D E
0 foo one 2 3 0
1 foo two 4 1 0
2 bar three 2 1 1
3 foo two 1 0 3
4 bar two 3 1 4
5 foo one 2 1 0
Agregación por columnas filtradas y funciones implementadas por cython :
df1 = df.groupby([''A'', ''B''], as_index=False)[''C''].sum()
print (df1)
A B C
0 bar three 2
1 bar two 3
2 foo one 4
3 foo two 5
La función de agregación se usa para todas las columnas sin especificar en la función
groupby
, aquí columnas
A, B
:
df2 = df.groupby([''A'', ''B''], as_index=False).sum()
print (df2)
A B C D E
0 bar three 2 1 1
1 bar two 3 1 4
2 foo one 4 4 0
3 foo two 5 1 3
También puede especificar solo algunas columnas utilizadas para la agregación en la lista después de la función
groupby
:
df3 = df.groupby([''A'', ''B''], as_index=False)[''C'',''D''].sum()
print (df3)
A B C D
0 bar three 2 1
1 bar two 3 1
2 foo one 4 4
3 foo two 5 1
Los mismos resultados utilizando la función
DataFrameGroupBy.agg
:
df1 = df.groupby([''A'', ''B''], as_index=False)[''C''].agg(''sum'')
print (df1)
A B C
0 bar three 2
1 bar two 3
2 foo one 4
3 foo two 5
df2 = df.groupby([''A'', ''B''], as_index=False).agg(''sum'')
print (df2)
A B C D E
0 bar three 2 1 1
1 bar two 3 1 4
2 foo one 4 4 0
3 foo two 5 1 3
Para las funciones multiplicadas aplicadas para una columna, use la lista de
tuple
s - nombres de nuevas columnas y funciones agregadas:
df4 = (df.groupby([''A'', ''B''])[''C'']
.agg([(''average'',''mean''),(''total'',''sum'')])
.reset_index())
print (df4)
A B average total
0 bar three 2.0 2
1 bar two 3.0 3
2 foo one 2.0 4
3 foo two 2.5 5
Si desea pasar múltiples funciones es posible pasar la
list
de
tuple
s:
df5 = (df.groupby([''A'', ''B''])
.agg([(''average'',''mean''),(''total'',''sum'')]))
print (df5)
C D E
average total average total average total
A B
bar three 2.0 2 1.0 1 1.0 1
two 3.0 3 1.0 1 4.0 4
foo one 2.0 4 2.0 4 0.0 0
two 2.5 5 0.5 1 1.5 3
A continuación, obtenga
MultiIndex
en columnas:
print (df5.columns)
MultiIndex(levels=[[''C'', ''D'', ''E''], [''average'', ''total'']],
labels=[[0, 0, 1, 1, 2, 2], [0, 1, 0, 1, 0, 1]])
Y para convertir a columnas, aplanar
MultiIndex
use
map
con
join
:
df5.columns = df5.columns.map(''_''.join)
df5 = df5.reset_index()
print (df5)
A B C_average C_total D_average D_total E_average E_total
0 bar three 2.0 2 1.0 1 1.0 1
1 bar two 3.0 3 1.0 1 4.0 4
2 foo one 2.0 4 2.0 4 0.0 0
3 foo two 2.5 5 0.5 1 1.5 3
Otra solución es pasar la lista de funciones agregadas, luego aplanar
MultiIndex
y para otros nombres de columnas use
str.replace
:
df5 = df.groupby([''A'', ''B'']).agg([''mean'',''sum''])
df5.columns = (df5.columns.map(''_''.join)
.str.replace(''sum'',''total'')
.str.replace(''mean'',''average''))
df5 = df5.reset_index()
print (df5)
A B C_average C_total D_average D_total E_average E_total
0 bar three 2.0 2 1.0 1 1.0 1
1 bar two 3.0 3 1.0 1 4.0 4
2 foo one 2.0 4 2.0 4 0.0 0
3 foo two 2.5 5 0.5 1 1.5 3
Si desea especificar cada columna con función agregada, pase el
dictionary
separado:
df6 = (df.groupby([''A'', ''B''], as_index=False)
.agg({''C'':''sum'',''D'':''mean''})
.rename(columns={''C'':''C_total'', ''D'':''D_average''}))
print (df6)
A B C_total D_average
0 bar three 2 1.0
1 bar two 3 1.0
2 foo one 4 2.0
3 foo two 5 0.5
También puede pasar la función personalizada:
def func(x):
return x.iat[0] + x.iat[-1]
df7 = (df.groupby([''A'', ''B''], as_index=False)
.agg({''C'':''sum'',''D'': func})
.rename(columns={''C'':''C_total'', ''D'':''D_sum_first_and_last''}))
print (df7)
A B C_total D_sum_first_and_last
0 bar three 2 2
1 bar two 3 2
2 foo one 4 4
3 foo two 5 1
Pregunta 2
Sin DataFrame después de la agregación! ¿Que pasó?
Agregación por 2 o más columnas:
df1 = df.groupby([''A'', ''B''])[''C''].sum()
print (df1)
A B
bar three 2
two 3
foo one 4
two 5
Name: C, dtype: int32
Primero verifique el
Index
y el
type
de objeto pandas:
print (df1.index)
MultiIndex(levels=[[''bar'', ''foo''], [''one'', ''three'', ''two'']],
labels=[[0, 0, 1, 1], [1, 2, 0, 2]],
names=[''A'', ''B''])
print (type(df1))
<class ''pandas.core.series.Series''>
Hay 2 soluciones para obtener la
MultiIndex Series
en columnas:
-
agregar parámetro
as_index=False
df1 = df.groupby([''A'', ''B''], as_index=False)[''C''].sum()
print (df1)
A B C
0 bar three 2
1 bar two 3
2 foo one 4
3 foo two 5
-
utilizar
Series.reset_index
:
df1 = df.groupby([''A'', ''B''])[''C''].sum().reset_index()
print (df1)
A B C
0 bar three 2
1 bar two 3
2 foo one 4
3 foo two 5
Si se agrupan por una columna:
df2 = df.groupby(''A'')[''C''].sum()
print (df2)
A
bar 5
foo 9
Name: C, dtype: int32
... obtener
Series
con
Index
:
print (df2.index)
Index([''bar'', ''foo''], dtype=''object'', name=''A'')
print (type(df2))
<class ''pandas.core.series.Series''>
Y la solución es la misma que en la
MultiIndex Series
:
df2 = df.groupby(''A'', as_index=False)[''C''].sum()
print (df2)
A C
0 bar 5
1 foo 9
df2 = df.groupby(''A'')[''C''].sum().reset_index()
print (df2)
A C
0 bar 5
1 foo 9
Pregunta 3
¿Cómo agregar principalmente columnas de cadenas (para
list
s,
tuple
s,
strings with separator
)?
df = pd.DataFrame({''A'' : [''a'', ''c'', ''b'', ''b'', ''a'', ''c'', ''b''],
''B'' : [''one'', ''two'', ''three'',''two'', ''two'', ''one'', ''three''],
''C'' : [''three'', ''one'', ''two'', ''two'', ''three'',''two'', ''one''],
''D'' : [1,2,3,2,3,1,2]})
print (df)
A B C D
0 a one three 1
1 c two one 2
2 b three two 3
3 b two two 2
4 a two three 3
5 c one two 1
6 b three one 2
En lugar de la función agregada es posible la
list
pases,
tuple
,
set
para convertir la columna:
df1 = df.groupby(''A'')[''B''].agg(list).reset_index()
print (df1)
A B
0 a [one, two]
1 b [three, two, three]
2 c [two, one]
La alternativa es utilizar
GroupBy.apply
:
df1 = df.groupby(''A'')[''B''].apply(list).reset_index()
print (df1)
A B
0 a [one, two]
1 b [three, two, three]
2 c [two, one]
Para convertir a cadenas con separador use
.join
solo si columna de cadena:
df2 = df.groupby(''A'')[''B''].agg('',''.join).reset_index()
print (df2)
A B
0 a one,two
1 b three,two,three
2 c two,one
Si la columna numérica usa la función lambda con
astype
para convertir a la
string
s:
df3 = (df.groupby(''A'')[''D'']
.agg(lambda x: '',''.join(x.astype(str)))
.reset_index())
print (df3)
A D
0 a 1,3
1 b 3,2,2
2 c 2,1
Otra solución es convertir a cadenas antes de
groupby
:
df3 = (df.assign(D = df[''D''].astype(str))
.groupby(''A'')[''D'']
.agg('',''.join).reset_index())
print (df3)
A D
0 a 1,3
1 b 3,2,2
2 c 2,1
Para convertir todas las columnas, no pase ninguna lista de columnas después de
groupby
.
No hay una columna
D
porque
la exclusión automática de las columnas ''molestas''
significa que todas las columnas numéricas están excluidas.
df4 = df.groupby(''A'').agg('',''.join).reset_index()
print (df4)
A B C
0 a one,two three,three
1 b three,two,three two,two,one
2 c two,one one,two
Por lo tanto, es necesario convertir todas las columnas en cadenas y luego obtener todas las columnas:
df5 = (df.groupby(''A'')
.agg(lambda x: '',''.join(x.astype(str)))
.reset_index())
print (df5)
A B C D
0 a one,two three,three 1,3
1 b three,two,three two,two,one 3,2,2
2 c two,one one,two 2,1
Pregunta 4
¿Cómo agregar cuentas?
df = pd.DataFrame({''A'' : [''a'', ''c'', ''b'', ''b'', ''a'', ''c'', ''b''],
''B'' : [''one'', ''two'', ''three'',''two'', ''two'', ''one'', ''three''],
''C'' : [''three'', np.nan, np.nan, ''two'', ''three'',''two'', ''one''],
''D'' : [np.nan,2,3,2,3,np.nan,2]})
print (df)
A B C D
0 a one three NaN
1 c two NaN 2.0
2 b three NaN 3.0
3 b two two 2.0
4 a two three 3.0
5 c one two NaN
6 b three one 2.0
Función
GroupBy.size
para el
size
de cada grupo:
df1 = df.groupby(''A'').size().reset_index(name=''COUNT'')
print (df1)
A COUNT
0 a 2
1 b 3
2 c 2
Función
GroupBy.count
excluye valores perdidos:
df2 = df.groupby(''A'')[''C''].count().reset_index(name=''COUNT'')
print (df2)
A COUNT
0 a 2
1 b 2
2 c 1
La función se debe utilizar para varias columnas para contar valores no perdidos:
df3 = df.groupby(''A'').count().add_suffix(''_COUNT'').reset_index()
print (df3)
A B_COUNT C_COUNT D_COUNT
0 a 2 2 1
1 b 3 2 3
2 c 2 1 1
La función relacionada
Series.value_counts
devuelve el tamaño del objeto que contiene recuentos de valores únicos en orden descendente, de modo que el primer elemento es el elemento que ocurre con mayor frecuencia.
Excluye los valores de
NaN
por defecto.
df4 = (df[''A''].value_counts()
.rename_axis(''A'')
.reset_index(name=''COUNT''))
print (df4)
A COUNT
0 b 3
1 a 2
2 c 2
Si desea la misma salida, como usar la función
groupby
+
size
agregue
Series.sort_index
:
df5 = (df[''A''].value_counts()
.sort_index()
.rename_axis(''A'')
.reset_index(name=''COUNT''))
print (df5)
A COUNT
0 a 2
1 b 3
2 c 2
Pregunta 5
¿Cómo crear una nueva columna rellenada por valores agregados?
El método
GroupBy.transform
devuelve un objeto indexado del mismo tamaño (el mismo tamaño) que el que se está agrupando.
documentation pandas para más información.
np.random.seed(123)
df = pd.DataFrame({''A'' : [''foo'', ''foo'', ''bar'', ''foo'', ''bar'', ''foo''],
''B'' : [''one'', ''two'', ''three'',''two'', ''two'', ''one''],
''C'' : np.random.randint(5, size=6),
''D'' : np.random.randint(5, size=6)})
print (df)
A B C D
0 foo one 2 3
1 foo two 4 1
2 bar three 2 1
3 foo two 1 0
4 bar two 3 1
5 foo one 2 1
df[''C1''] = df.groupby(''A'')[''C''].transform(''sum'')
df[''C2''] = df.groupby([''A'',''B''])[''C''].transform(''sum'')
df[[''C3'',''D3'']] = df.groupby(''A'')[''C'',''D''].transform(''sum'')
df[[''C4'',''D4'']] = df.groupby([''A'',''B''])[''C'',''D''].transform(''sum'')
print (df)
A B C D C1 C2 C3 D3 C4 D4
0 foo one 2 3 9 4 9 5 4 4
1 foo two 4 1 9 5 9 5 5 1
2 bar three 2 1 5 2 5 2 2 1
3 foo two 1 0 9 5 9 5 5 1
4 bar two 3 1 5 3 5 2 3 1
5 foo one 2 1 9 4 9 5 4 4
- ¿Cómo realizar la agregación con pandas?
- Sin DataFrame después de la agregación! ¿Que pasó?
-
¿Cómo agregar principalmente columnas de cadenas (para
list
s,tuple
s,strings with separator
)? - ¿Cómo agregar cuentas?
- ¿Cómo crear una nueva columna rellenada por valores agregados?
He visto estas preguntas recurrentes sobre varias caras de la funcionalidad agregada de los pandas. La mayor parte de la información relacionada con la agregación y sus diversos casos de uso hoy en día está fragmentada en docenas de publicaciones mal redactadas y sin búsquedas. El objetivo aquí es recopilar algunos de los puntos más importantes para la posteridad.
Esta Q / A está destinada a ser la próxima entrega de una serie de guías de usuario útiles:
- Cómo hacer pivotar un marco de datos ,
- Pandas concat
- ¿Cómo opero un DataFrame con una serie para cada columna?
- Pandas fusionando 101
Tenga en cuenta que esta publicación no pretende ser un sustituto de la documentación sobre agregación y sobre groupby , ¡así que lea esto también!