Para terminar con la serie Groupby, esta vez vamos a agrupar unos datos por varias columnas y además vamos a obtener la suma de un campo y la cuenta de otro.
Como siempre, los datos aquí.
La tabla contiene las mismas columnas que en el post anterior:
- ZONA: Zona geográfica donde está situada la planta correspondiente.
- PLANTA: Nombre de la planta.
- TIPO: Clase de climatización.
- EQUIPO: Código único del equipo.
- KW_FRIO: Capacidad de frío del equipo en kW.
Con Pandas:
import pandas as pd df = pd.read_csv('potencias_frio.csv', sep=";", decimal=",") grouped_df = df.groupby(['ZONA', 'PLANTA', 'TIPO']).agg( {'EQUIPO': 'count', 'KW_FRIO': 'sum'}).reset_index() grouped_df.columns = ['ZONA', 'PLANTA', 'TIPO', 'SUMA KW FRIO', 'NUM_EQUIPOS'] print (grouped_df)
ZONA PLANTA TIPO SUMA KW FRIO NUM_EQUIPOS 0 ESTE BRAVA CONFORT 7881 139 1 ESTE BRAVA TECNICO 1930 35 2 NORTE GORBEA CONFORT 2619 47 3 NORTE GORBEA TECNICO 3341 56 4 SUR TARIFA CONFORT 991 21 5 SUR TARIFA TECNICO 1072 31
Si no utilizamos Pandas:
Cargamos los datos del archivo csv. El resultado nos da una lista de diccionarios, donde, para cada diccionario, las claves serán los nombres de las columnas, y los valores , el valor de dicha columna.
# Cargamos los datos en una lista de diccionarios import csv csv_rdr = csv.reader(open('potencias_frio.csv'), delimiter=';') kw_cl_list = [] for index, row in enumerate(csv_rdr): if index == 0: encabezado = row else: row[4] = int(row[4]) d = {} for index, value in enumerate(row): key = encabezado[index] d[key] = value kw_cl_list.append(d)
Para este ejercicio he creado la clase myGroupBy:
def groupby_agg(self): """ :param data: dataset de datos formato lista de diccionarios :param groupby_fields: campo sobre el que agregar. Ejemplo: ['ZONA,'PLANTA',TIPO'] :param agg_fields: lista con los campos a agregar. Ejemplo: ['EQUIPO', 'KW_FRIO'] :param aggr_list: lista con las funciones de agregacion. Correspondientes con agg_fields. Ejemplo: ['count', 'sum'] :return: diccionario """ def aggregator(agg_field): """ :param agg_field: campo que se va a agregar :return diccionario con las claves y los valores agrupados """ from collections import defaultdict d_aggr = defaultdict(list) for row in self.data: groupby_field_string = '' for field in self.groupby_fields: if groupby_field_string != '': groupby_field_string += '|' + row[field] else: groupby_field_string += row[field] d_aggr[groupby_field_string].append(row[agg_field]) return d_aggr from collections import defaultdict d_res = defaultdict(list) for index, agg_field in enumerate(self.agg_fields): if aggr_list[index] == 'sum': for k, v in sorted(aggregator(agg_field).items()): if v is None: v = 0.0 d_res[k].append(sum(v)) if aggr_list[index] == 'count': for k, v in sorted(aggregator(agg_field).items()): d_res[k].append(len([item for item in v if item])) return d_res
Al crear una instancia de la clase, tendremos que pasarle estos argumentos:
- data: Los datos que hemos obtenido del csv y que hemos pasado a la lista de diccionarios.
- groupby_fields: Una lista en la que hay que colocar los campos por los que queremos agrupar. En este caso quiero agrupar por zona, planta y tipo, así que la lista sería [‘ZONA,’PLANTA’,TIPO’].
- agg_fields:Una lista con los campo a agregar. En este caso [‘EQUIPO’, ‘KW_FRIO’].
- aggr_list: Una lista con las funciones de agregación. El orden tiene que corresponder con el campo a agregar. En este caso queremos calcular la cuenta de los equipos y la suma de los kW de frío., así que le pasaríamos [‘count’, ‘sum’].
La función aggregator crea un diccionario con las grupaciones de datos. En este casso, las claves tendrán una pinta así: SUR|TARIFA|CONFORT y su valor será una lista con todos los datos que correspondan. Luego, recorriendo el diccionario devuelto por aggregator, podemos sumar o contar los elementos de dicha lista.
Pues vamos allá.
Definimos los argumentos que vamos a necesitar:
data = kw_cl_list groupby_fields = ['ZONA','PLANTA','TIPO'] agg_fields = ['EQUIPO', 'KW_FRIO'] aggr_list = ['count', 'sum']
Creamos una instancia que llamaré frioGroupBy:
frioGroupBy = myGroupBy(data, groupby_fields, agg_fields, aggr_list)
que tiene esta pinta:
<__main__.myGroupBy object at 0x00000152DA01E748>
Ahora llamamos al método groupby_agg de la clase para obtener los datos agrupados. Los colocaremos en una nueva variable que llamaré frioGroupedData.
frioGroupedData = frioGroupBy.groupby_agg()
Comprobamos el resultado:
print("{:<7}{:<9}{:<11}{:<15}{:<20}".format( "ZONA", "PLANTA", "TIPO", "NUM_EQUIPOS", "SUMA KW FRIO")) for key in sorted(frioGroupedData .keys(), key=lambda key: key.split('|')[0]): zona, planta, tipo = key.split('|') print("{:<7}{:<9}{:<11}{:<15}{:<20}".format( zona, planta, tipo, frioGroupedData [key][0], frioGroupedData [key][1]))
ZONA PLANTA TIPO NUM_EQUIPOS SUMA KW FRIO ESTE BRAVA TECNICO 35 1930 ESTE BRAVA CONFORT 139 7881 NORTE GORBEA TECNICO 56 3341 NORTE GORBEA CONFORT 47 2619 SUR TARIFA TECNICO 31 1072 SUR TARIFA CONFORT 21 991
Como véis, el resultado es el mismo.