Search

Sin Pandas y a lo loco!

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.

Puedes descargarte el código de mi 

Deja una respuesta

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.