Agrupaciones de datos

Las agrupaciones son operaciones necesarias para analizar datos, ya que permiten extraer información en función de datos categóricos de nuestro dataframe.

Cargaremos los datos llamados experiment.csv que podemos encontrar en el siguiente enlace

[6]:
import pandas as pd
import numpy as np

df= pd.read_csv("data/experiment.csv")
print(df)
   Nombre Apellidos  Altura Sexo  Nacimiento   Cof Categoria
0    Will     Smith    1.43    M  10/10/1920  0.19   laboral
1     Jon      Snow    1.98    M   10/1/1970  0.98   laboral
2    Laia   Ramirez    1.87    F  09/10/1987  0.76      cap6
3    Luzy      Raim    1.67    F  23/07/1979  0.56      cap6
4    Fein      Mang    1.78    M  12/03/1937  0.27      cap6
5  Victor     Colom    1.78    M  22/09/1957  0.97      cap8

En el siguiente ejemplo agrupamos los datos según el sexo de la persona mediante el método groupby que devuelve un DataFrame agrupado:

[8]:
bySex = df.groupby('Sexo')
bySex
[8]:
<pandas.core.groupby.generic.DataFrameGroupBy object at 0x164818df0>

El atributo groups nos muestra los grupos hemos creado:

[9]:
# Podemos saber los grupos realizados y que índices del dataframe tienen.
bySex.groups # nos proporciona un diccionario

[9]:
{'F': [2, 3], 'M': [0, 1, 4, 5]}
[10]:
bySex.describe()
[10]:
Altura Cof
count mean std min 25% 50% 75% max count mean std min 25% 50% 75% max
Sexo
F 2.0 1.7700 0.141421 1.67 1.7200 1.77 1.82 1.87 2.0 0.6600 0.141421 0.56 0.61 0.66 0.7100 0.76
M 4.0 1.7425 0.228674 1.43 1.6925 1.78 1.83 1.98 4.0 0.6025 0.431383 0.19 0.25 0.62 0.9725 0.98

Esto nos permite realizar operaciones de filtrado con base a los grupos que hemos creado:

[4]:
dfM = df.loc[bySex.groups['M'].values] #Recorda que "loc" accedeix per index de fila
dfM
[4]:
Nombre Apellidos Altura Sexo Nacimiento Cof Categoria
0 Will Smith 1.43 M 10/10/1920 0.19 laboral
1 Jon Snow 1.98 M 10/1/1970 0.98 laboral
4 Fein Mang 1.78 M 12/03/1937 0.27 cap6
5 Victor Colom 1.78 M 22/09/1957 0.97 cap8
[7]:
df[df.Sexo=="M"] # es lo  mismo!
[7]:
Nombre Apellidos Altura Sexo Nacimiento Cof Categoria
0 Will Smith 1.43 M 10/10/1920 0.19 laboral
1 Jon Snow 1.98 M 10/1/1970 0.98 laboral
4 Fein Mang 1.78 M 12/03/1937 0.27 cap6
5 Victor Colom 1.78 M 22/09/1957 0.97 cap8

Funciones de agregación en grupos.

El método ‘aggregate’ nos permite crear variables de agregación en la tabla obtenida con ‘groupby’. Indicaremos la información que deseamos obtener de cada columna utilizando un diccionario. Especificamos la función que aplicaremos a los datos de cada grupo en cada columna para obtener un único valor.

Para ejemplificar esta sección, agruparemos el dataframe por Categoria laboral. En este caso para la columna Altura consultamos la suma de las alturas del grupo y Cof la media.

La función aggregate nos permite crear variables de agregación sobre la tabla obtenida con groupby. Indicaremos la información que queremos obtener de cada columna con un diccionario. Especificamos la función que vamos a aplicar a los datos de cada grupo en cada columna para obtener un único valor.

[11]:
dfg = df.groupby(["Categoria"]).aggregate({
    "Altura":np.sum,
    "Cof":np.mean})

dfg
[11]:
Altura Cof
Categoria
cap6 5.32 0.530
cap8 1.78 0.970
laboral 3.41 0.585
[12]:
dfg.index
[12]:
Index(['cap6', 'cap8', 'laboral'], dtype='object', name='Categoria')

Podemos aplicar un gran número de funciones de agregación:

[9]:
type(dfg) #Alerta! Una agregación genera un dataframe y por lo tanto podemos seguir aplicando lo que ya sabemos
[9]:
pandas.core.frame.DataFrame
[10]:
dfg[dfg.Altura>3]
[10]:
Altura Cof
Categoria
cap6 5.32 0.530
laboral 3.41 0.585

Agrupaciones de múltiples columnas

También se pueden realizar agrupaciones de múltiples columnas. Se crean todas las combinaciones de las diversas columnas que existen en el DataFrame. Veamos un ejemplo:

[5]:
gr = df.groupby(['Sexo',"Categoria"]).mean()
print(gr)
gr.index

                Altura    Cof
Sexo Categoria
F    cap6        1.770  0.660
M    cap6        1.780  0.270
     cap8        1.780  0.970
     laboral     1.705  0.585
/var/folders/6j/7gfvt_29797dypw8t1wttblw0000gn/T/ipykernel_71814/3859985590.py:1: FutureWarning: The default value of numeric_only in DataFrameGroupBy.mean is deprecated. In a future version, numeric_only will default to False. Either specify numeric_only or select only columns which should be valid for the function.
  gr = df.groupby(['Sexo',"Categoria"]).mean()
[5]:
MultiIndex([('F',    'cap6'),
            ('M',    'cap6'),
            ('M',    'cap8'),
            ('M', 'laboral')],
           names=['Sexo', 'Categoria'])

Si queremos realizar un conteo de los elementos, debemos seleccionar

[39]:
gr = df.groupby(['Sexo',"Categoria"])["Sexo"].count()
print(gr)
Sexo  Categoria
F     cap6         2
M     cap6         1
      cap8         1
      laboral      2
Name: Sexo, dtype: int64

Multiindice

A veces, un índice no es suficiente para expresar la meta-información que identifica una o varias columnas. Por ejemplo, una coordenada está compuesta por la latitud y la longitud.

Un ‘multiíndice’ es una jerarquía de índices.

Agrupar según diferentes criterios resulta en un multiíndice.

[13]:
gr = df.groupby(['Sexo',"Categoria"]).count()
gr.index
[13]:
MultiIndex([('F',    'cap6'),
            ('M',    'cap6'),
            ('M',    'cap8'),
            ('M', 'laboral')],
           names=['Sexo', 'Categoria'])
[18]:
gr.loc[("M",:)]
  Input In [18]
    gr.loc[("M",:)]
                ^
SyntaxError: invalid syntax

[17]:
gr.loc["F"] # primer index
[17]:
Nombre Apellidos Altura Nacimiento Cof
Categoria
cap6 2 2 2 2 2
[16]:
gr.loc["cap6"] # dependent index
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
File ~/.pyenv/versions/3.9.7/lib/python3.9/site-packages/pandas/core/indexes/base.py:3802, in Index.get_loc(self, key, method, tolerance)
   3801 try:
-> 3802     return self._engine.get_loc(casted_key)
   3803 except KeyError as err:

File ~/.pyenv/versions/3.9.7/lib/python3.9/site-packages/pandas/_libs/index.pyx:138, in pandas._libs.index.IndexEngine.get_loc()

File ~/.pyenv/versions/3.9.7/lib/python3.9/site-packages/pandas/_libs/index.pyx:165, in pandas._libs.index.IndexEngine.get_loc()

File pandas/_libs/hashtable_class_helper.pxi:5745, in pandas._libs.hashtable.PyObjectHashTable.get_item()

File pandas/_libs/hashtable_class_helper.pxi:5753, in pandas._libs.hashtable.PyObjectHashTable.get_item()

KeyError: 'cap6'

The above exception was the direct cause of the following exception:

KeyError                                  Traceback (most recent call last)
/Users/isaac/Projects/TxADM_notebooks/notebooks/Part2/00_Pandas/02_Agrupación_de_datos.ipynb Cell 24 line <cell line: 1>()
----> <a href='vscode-notebook-cell:/Users/isaac/Projects/TxADM_notebooks/notebooks/Part2/00_Pandas/02_Agrupaci%C3%B3n_de_datos.ipynb#X61sZmlsZQ%3D%3D?line=0'>1</a> gr.loc["cap6"]

File ~/.pyenv/versions/3.9.7/lib/python3.9/site-packages/pandas/core/indexing.py:1073, in _LocationIndexer.__getitem__(self, key)
   1070 axis = self.axis or 0
   1072 maybe_callable = com.apply_if_callable(key, self.obj)
-> 1073 return self._getitem_axis(maybe_callable, axis=axis)

File ~/.pyenv/versions/3.9.7/lib/python3.9/site-packages/pandas/core/indexing.py:1312, in _LocIndexer._getitem_axis(self, key, axis)
   1310 # fall thru to straight lookup
   1311 self._validate_key(key, axis)
-> 1312 return self._get_label(key, axis=axis)

File ~/.pyenv/versions/3.9.7/lib/python3.9/site-packages/pandas/core/indexing.py:1260, in _LocIndexer._get_label(self, label, axis)
   1258 def _get_label(self, label, axis: int):
   1259     # GH#5567 this will fail if the label is not present in the axis.
-> 1260     return self.obj.xs(label, axis=axis)

File ~/.pyenv/versions/3.9.7/lib/python3.9/site-packages/pandas/core/generic.py:4049, in NDFrame.xs(self, key, axis, level, drop_level)
   4046 self._consolidate_inplace()
   4048 if isinstance(index, MultiIndex):
-> 4049     loc, new_index = index._get_loc_level(key, level=0)
   4050     if not drop_level:
   4051         if lib.is_integer(loc):

File ~/.pyenv/versions/3.9.7/lib/python3.9/site-packages/pandas/core/indexes/multi.py:3160, in MultiIndex._get_loc_level(self, key, level)
   3158         return indexer, maybe_mi_droplevels(indexer, ilevels)
   3159 else:
-> 3160     indexer = self._get_level_indexer(key, level=level)
   3161     if (
   3162         isinstance(key, str)
   3163         and self.levels[level]._supports_partial_string_indexing
   3164     ):
   3165         # check to see if we did an exact lookup vs sliced
   3166         check = self.levels[level].get_loc(key)

File ~/.pyenv/versions/3.9.7/lib/python3.9/site-packages/pandas/core/indexes/multi.py:3263, in MultiIndex._get_level_indexer(self, key, level, indexer)
   3259         return slice(i, j, step)
   3261 else:
-> 3263     idx = self._get_loc_single_level_index(level_index, key)
   3265     if level > 0 or self._lexsort_depth == 0:
   3266         # Desired level is not sorted
   3267         if isinstance(idx, slice):
   3268             # test_get_loc_partial_timestamp_multiindex

File ~/.pyenv/versions/3.9.7/lib/python3.9/site-packages/pandas/core/indexes/multi.py:2849, in MultiIndex._get_loc_single_level_index(self, level_index, key)
   2847     return -1
   2848 else:
-> 2849     return level_index.get_loc(key)

File ~/.pyenv/versions/3.9.7/lib/python3.9/site-packages/pandas/core/indexes/base.py:3804, in Index.get_loc(self, key, method, tolerance)
   3802     return self._engine.get_loc(casted_key)
   3803 except KeyError as err:
-> 3804     raise KeyError(key) from err
   3805 except TypeError:
   3806     # If we have a listlike key, _check_indexing_error will raise
   3807     #  InvalidIndexError. Otherwise we fall through and re-raise
   3808     #  the TypeError.
   3809     self._check_indexing_error(key)

KeyError: 'cap6'
[18]:
# Necesitamos invocar un IndexSlice.
gr.loc[pd.IndexSlice[:, 'cap6'],:]
# https://pandas.pydata.org/docs/reference/api/pandas.IndexSlice.html
[18]:
Nombre Apellidos Altura Nacimiento Cof
Sexo Categoria
F cap6 2 2 2 2 2
M cap6 1 1 1 1 1

En estas situaciones donde queremos acceder a esos datos, la forma más sencilla es simplemente eliminar el indice:

[19]:
gr = df.groupby(['Sexo',"Categoria"]).count().reset_index()
gr
[19]:
Sexo Categoria Nombre Apellidos Altura Nacimiento Cof
0 F cap6 2 2 2 2 2
1 M cap6 1 1 1 1 1
2 M cap8 1 1 1 1 1
3 M laboral 2 2 2 2 2
[21]:
gr.index = gr["Categoria"]
print(gr)
          Sexo Categoria  Nombre  Apellidos  Altura  Nacimiento  Cof
Categoria
cap6         F      cap6       2          2       2           2    2
cap6         M      cap6       1          1       1           1    1
cap8         M      cap8       1          1       1           1    1
laboral      M   laboral       2          2       2           2    2

Agregaciones avanzadas

Sobre una agregación, podemos realizar operaciones más allá de las aritméticas.

Por ejemplo, si deseamos crear un histograma de la distribución de tipos de certificados por ciudad y código postal, necesitamos crear una lista para cada grupo.

[23]:
df = pd.read_csv("data/data_groups.csv")
print(df.head())
print(df.shape)

   Unnamed: 0        Dni                           Nom    CP      Ciutat Sexe  \
0           0  H61414629     María Dolores Arjona Jove  7800     Eivissa    M
1           1  S3138381C                  Núria Quirós  7511     Ruberts    F
2           2  J8698188C  Miguel José María Gil Vargas  7340       Alaro    M
3           3  A48821615       Jordi Chaves Bustamante  7609  Bellavista    F
4           4  U0247281I    Jana Rosa Collado Menéndez  7006       Palma    M

  Tipus certificat cat  Punts
0                    B     73
1                    A     40
2                    A     45
3                    B     40
4                    B     86
(1000, 8)
[22]:
dfc = df.groupby(["Ciutat","CP"])["Tipus certificat cat"].apply(list)
dfc
[22]:
Ciutat      CP
Alaro       7340    [A, C, B, A, A, B, A, D, A, B, A, B, B, A, B, ...
Ariany      7529    [B, A, B, A, A, B, A, B, A, B, A, A, C, A, B, ...
Bellavista  7609    [B, C, A, A, A, C, A, A, B, A, A, A, B, A, C, ...
Binissalem  7350    [A, B, B, C, C, A, A, A, C, A, C, A, B, B, D, ...
Eivissa     7800    [B, A, A, B, B, B, A, A, A, A, B, C, A, A, B, ...
La Savina   7870    [A, A, A, B, A, A, B, B, A, A, B, A, B, B, A, ...
Mao         7701    [B, C, B, C, B, C, A, B, B, B, A, C, C, A, C, ...
            7703    [B, A, B, C, A, A, B, A, C, B, A, B, C, A, B, ...
Palma       7006    [B, A, B, B, A, A, A, B, B, A, A, B, B, A, B, ...
            7009    [A, D, A, B, C, A, A, C, B, A, A, A, C, A, A, ...
            7013    [A, A, B, C, C, C, C, A, A, A, B, A, C, C, A, ...
Pedruscada  7590    [A, B, A, A, A, A, B, B, C, B, B, B, C, A, A, ...
Ruberts     7511    [A, A, A, B, C, A, B, A, A, B, B, A, C, B, B, ...
Name: Tipus certificat cat, dtype: object
[23]:
tipusAlaro = dfc.loc[pd.IndexSlice["Alaro",7340]]

values, counts = np.unique(tipusAlaro, return_counts=True)
print(values) # Tipus
print(counts) # quantitat
print("-"*40)
distribucioAlaro = dict(zip(values,counts)) ## Que fa el ZIP?!
print(distribucioAlaro)
['A' 'B' 'C' 'D']
[57 42 16  5]
----------------------------------------
{'A': 57, 'B': 42, 'C': 16, 'D': 5}
[24]:
def hola(valor):
    print(valor*3)
[25]:
hola(9)
27
[24]:
## També podem invocar funcions especifiques! en lloc de una sum, mean, max, etc.
df2 = df.groupby(["Ciutat","CP"]).agg(
        {"Tipus certificat cat": [lambda x: list(x), np.size]}) # lambda !

print(df2)
                                              Tipus certificat cat
                                                        <lambda_0> size
Ciutat     CP
Alaro      7340  [A, C, B, A, A, B, A, D, A, B, A, B, B, A, B, ...  120
Ariany     7529  [B, A, B, A, A, B, A, B, A, B, A, A, C, A, B, ...  113
Bellavista 7609  [B, C, A, A, A, C, A, A, B, A, A, A, B, A, C, ...   98
Binissalem 7350  [A, B, B, C, C, A, A, A, C, A, C, A, B, B, D, ...   93
Eivissa    7800  [B, A, A, B, B, B, A, A, A, A, B, C, A, A, B, ...   91
La Savina  7870  [A, A, A, B, A, A, B, B, A, A, B, A, B, B, A, ...   90
Mao        7701  [B, C, B, C, B, C, A, B, B, B, A, C, C, A, C, ...   63
           7703  [B, A, B, C, A, A, B, A, C, B, A, B, C, A, B, ...   45
Palma      7006  [B, A, B, B, A, A, A, B, B, A, A, B, B, A, B, ...   36
           7009  [A, D, A, B, C, A, A, C, B, A, A, A, C, A, A, ...   23
           7013  [A, A, B, C, C, C, C, A, A, A, B, A, C, C, A, ...   37
Pedruscada 7590  [A, B, A, A, A, A, B, B, C, B, B, B, C, A, A, ...  106
Ruberts    7511  [A, A, A, B, C, A, B, A, A, B, B, A, C, B, B, ...   85
[41]:
# Qué és una funció lambda?

a = np.array([10,10,4,5,7,8,12,4507,30])
b = list(map(lambda i:i**2+2*i,a))

def f1(i):
    return i**2+2

b = list(map(f1,a))

print(b)
[102, 102, 18, 27, 51, 66, 146, 20313051, 902]
[36]:
# Em aquest exemple apliquem una funció que té un criterí més específic:

def miBarem50p(serie):
  up50list = []
  for value in serie.values:
    if value>50:
      up50list.append(value)
  return len(up50list)


df3 = df.groupby(["Ciutat","CP"]).agg(
        {"Punts": [lambda x: miBarem50p(x), np.size]}) # lambda: què és X?

print(df3) #Quina interpretació dels resultats feu?
                     Punts
                <lambda_0> size
Ciutat     CP
Alaro      7340         61  120
Ariany     7529         56  113
Bellavista 7609         43   98
Binissalem 7350         50   93
Eivissa    7800         44   91
La Savina  7870         41   90
Mao        7701         34   63
           7703         18   45
Palma      7006         19   36
           7009         10   23
           7013         19   37
Pedruscada 7590         65  106
Ruberts    7511         36   85
[ ]:

Ejercicios

1) Usando el fichero WHO.csv, ¿Cuál es el volumen total de CO2 emitido por cada continente?

[ ]:

2) ¿Cuál es el número de paises por continente?

[ ]:

3) Del conjunto “who.csv” selecciona 30 paises al azar y sobre ellos calcula la media de “Net primary school enrolment ratio female (%)” agrupados por: Continente

Nota: la selección de 30 paises aleatoria ha de ser reproducible

[ ]:

3b) Repite la anterior actividad pero ahora con todos los paises. ¿Sale la misma media?

[ ]:

4) Calcula la cantidad de ayuda recibida por cada municipio en función del númeto total de habitantes. (v2)

[1]:
# V2. Con necesidad de agrupar
import pandas as pd
import random

random.seed(0)

nombres = [f'Municipio{i}' for i in range(1, 11)]

data_municipios = {
    'Nombre': nombres,
    'Código Postal': [random.randint(10000, 99999) for _ in range(10)],
    'Población': [random.randint(1000, 50000) for _ in range(10)]  # Añadimos un atributo aleatorio, en este caso "Población"
}

df_municipios = pd.DataFrame(data_municipios)


data_ayudas = {
    'Nombre': [random.choice(nombres) for _ in range(20)],
    'Ayuda Económica (en euros)': [random.randint(1000, 10000) for _ in range(20)],
    'Número de Beneficiarios': [random.randint(10, 100) for _ in range(20)]
}

df_ayudas = pd.DataFrame(data_ayudas)

print("Dataframe de Municipios:")
print(df_municipios)

print("\nDataframe de Ayudas:")
print(df_ayudas)
Dataframe de Municipios:
        Nombre  Código Postal  Población
0   Municipio1          60494      39232
1   Municipio2          65125      15315
2   Municipio3          15306      34075
3   Municipio4          43936      10127
4   Municipio5          77013      19470
5   Municipio6          73691      10158
6   Municipio7          63075       7214
7   Municipio8          49755      41525
8   Municipio9          72468      17417
9  Municipio10          56930      35902

Dataframe de Ayudas:
         Nombre  Ayuda Económica (en euros)  Número de Beneficiarios
0   Municipio10                        9989                       72
1    Municipio3                        1230                       23
2    Municipio5                        2528                       48
3    Municipio2                        7534                       80
4    Municipio2                        1018                       47
5    Municipio6                        9086                      100
6    Municipio8                        6458                       25
7    Municipio9                        4996                       80
8    Municipio2                        6328                       52
9    Municipio6                        2031                       79
10   Municipio7                        4130                       36
11   Municipio6                        4632                       87
12  Municipio10                        4909                       80
13   Municipio4                        3334                       85
14   Municipio9                        9896                       46
15   Municipio8                        8339                       66
16   Municipio8                        2494                       21
17   Municipio9                        2318                       86
18   Municipio5                        6243                       59
19   Municipio1                        9322                       50

5) Agrupa los datos por el nombre de de la escuela. - ¿Qué escuela tiene más infantes? - ¿Qué escuela tiene los infantes más altos?

[ ]:
data = {
    'school': ['s001', 's002', 's003', 's001', 's002', 's004'],
    'class': ['V', 'V', 'VI', 'VI', 'V', 'VI'],
    'name': ['Alberto Franco', 'Gino Mcneill', 'Ryan Parkes', 'Eesha Hinton', 'Gino Mcneill', 'David Parkes'],
    'date_Of_Birth': ['15/05/2002', '17/05/2002', '16/02/1999', '25/09/1998', '11/05/2002', '15/09/1997'],
    'age': [12, 12, 13, 13, 14, 12],
    'height': [173, 192, 186, 167, 151, 159],
    'weight': [35, 32, 33, 30, 31, 32],
    'address': ['street1', 'street2', 'street3', 'street1', 'street2', 'street4']
}

df = pd.DataFrame(data, index=['S1', 'S2', 'S3', 'S4','S5','S6'])
df.head()
[ ]:
#TODO

6) Dado el siguiente listado de ventas: - ¿Qué comprador ha gastado más? - ¿Qué vendedor ha hecho más ventas?

[6]:
import pandas as pd
import numpy as np

# Crear datos aleatorios
np.random.seed(1)
n = 15
productos = ['Producto ' + str(i) for i in range(1, n+1)]
precios = np.random.randint(10, 100, n)
compradores = np.random.choice(['Juan', 'Pedro', 'Maria', 'Ana'], n)
vendedores = np.random.choice(['Carlos', 'Laura', 'Miguel', 'Elena'], n)

# Crear DataFrame
df_ventas = pd.DataFrame({
    'Producto': productos,
    'Precio': precios,
    'Comprador': compradores,
    'Vendedor': vendedores
})

print(df_ventas)
       Producto  Precio Comprador Vendedor
0    Producto 1      47      Juan    Laura
1    Producto 2      22     Maria    Laura
2    Producto 3      82     Pedro    Elena
3    Producto 4      19     Maria   Miguel
4    Producto 5      85      Juan   Carlos
5    Producto 6      15       Ana   Miguel
6    Producto 7      89      Juan    Laura
7    Producto 8      74     Maria    Laura
8    Producto 9      26      Juan    Laura
9   Producto 10      11     Pedro    Elena
10  Producto 11      86     Maria    Elena
11  Producto 12      81     Maria    Laura
12  Producto 13      16      Juan   Miguel
13  Producto 14      35       Ana    Laura
14  Producto 15      60       Ana    Laura
[7]:
#TODO

7) Fichero “data/data_groups_cursos.csv”; contiene cursos realizados por personas. Cada muestra corresponde a un curso. Se pide un listado del número de cursos realizado por cada persona y una valoración de sus puntos según el tipo de curso realizado: - ‘A’: 3 puntos - ‘B’: 2 puntos - ‘C’: 1 punto - ‘D’: 0.5 punto - “E”: 0.5 punto - “F”: 1 punto

[8]:
# TODO
[42]:
a = np.array([1,2,3,4,5,6,10])
print(a.max())
print(a.argmax())

10
6

Categorización de datos

TODO Valores ya categorizados?

https://pandas.pydata.org/docs/user_guide/categorical.html

La función cut de Pandas es una herramienta útil para segmentar y discretizar datos en intervalos o categorías. Esta función es especialmente útil cuando desea convertir una variable numérica continua en una variable categórica al dividirla en intervalos o categorías específicas.

Documentación

Crearemos un nuevo DataFrame para realizar pruebas:

[14]:

[0.   0.25 0.5  0.75 1.  ]
----------
0       (3.5, 5.0]
1    (-0.001, 3.0]
2    (-0.001, 3.0]
3    (-0.001, 3.0]
4       (5.0, 9.0]
5       (5.0, 9.0]
6    (-0.001, 3.0]
7       (3.5, 5.0]
8    (-0.001, 3.0]
9       (3.5, 5.0]
Name: nota, dtype: category
Categories (4, interval[float64, right]): [(-0.001, 3.0] < (3.0, 3.5] < (3.5, 5.0] < (5.0, 9.0]]
----------
[14]:
(-0.001, 3.0]    5
(3.5, 5.0]       3
(5.0, 9.0]       2
(3.0, 3.5]       0
Name: nota, dtype: int64
[43]:
import numpy as np
np.random.seed(0)
df_random = pd.DataFrame({"candidat":np.arange(1,11), "nota":np.random.randint(0,11,size=10)})
df_random
[43]:
candidat nota
0 1 5
1 2 0
2 3 3
3 4 3
4 5 7
5 6 9
6 7 3
7 8 5
8 9 2
9 10 4

A continuación, creamos una columna nueva que contiene la nota pero en escala categórica:

[10]:
df_random["notaCategorica"] = pd.cut(df_random.nota, 3, labels=["dolent", "mig", "bona"])
df_random
[10]:
candidat nota notaCategorica
0 1 5 mig
1 2 0 dolent
2 3 3 dolent
3 4 3 dolent
4 5 7 bona
5 6 9 bona
6 7 3 dolent
7 8 5 mig
8 9 2 dolent
9 10 4 mig
[12]:
df_random[df_random.notaCategorica==df_random.notaCategorica.max()]
[12]:
candidat nota notaCategorica
4 5 7 bona
5 6 9 bona
[45]:
# mejora
bins = [-1, 4, 7, 8.5, 10]
labels = ['Suspens', 'Be', 'Notable', 'Excel·lent']
df_random["notaCategorica"] = pd.cut(df_random.nota, bins, labels=labels) # include_lowest=True
df_random
[45]:
candidat nota notaCategorica
0 1 5 Be
1 2 0 Suspens
2 3 3 Suspens
3 4 3 Suspens
4 5 7 Be
5 6 9 Excel·lent
6 7 3 Suspens
7 8 5 Be
8 9 2 Suspens
9 10 4 Suspens
[48]:
# Podemos definir el intervalo que deseemos (como un Histograma)
import numpy as np
marks = np.arange(0,1.1,0.25)
print(marks)
print
print("-"*10)
factors = pd.qcut(df_random.nota, marks)
print(factors)
print("-"*10)
print(pd.value_counts(factors))
print(df_random.nota)
[0.   0.25 0.5  0.75 1.  ]
----------
0       (3.5, 5.0]
1    (-0.001, 3.0]
2    (-0.001, 3.0]
3    (-0.001, 3.0]
4       (5.0, 9.0]
5       (5.0, 9.0]
6    (-0.001, 3.0]
7       (3.5, 5.0]
8    (-0.001, 3.0]
9       (3.5, 5.0]
Name: nota, dtype: category
Categories (4, interval[float64, right]): [(-0.001, 3.0] < (3.0, 3.5] < (3.5, 5.0] < (5.0, 9.0]]
----------
(-0.001, 3.0]    5
(3.5, 5.0]       3
(5.0, 9.0]       2
(3.0, 3.5]       0
Name: nota, dtype: int64
0    5
1    0
2    3
3    3
4    7
5    9
6    3
7    5
8    2
9    4
Name: nota, dtype: int64
[51]:
# Del fichero:

import pandas as pd
df = pd.read_csv("data/rdu-weather-history.csv",sep=";")
print(df.head())
print(df.columns)
print(df.shape)
print(df.snowfall.describe())

values, repeticiones = np.unique(df.snowfall,return_counts=True)
print(values)
print(repeticiones)
         date  temperaturemin  temperaturemax  precipitation  snowfall  \
0  2015-04-08            62.1            84.0           0.00       0.0
1  2015-04-20            63.0            78.1           0.28       0.0
2  2015-04-26            45.0            54.0           0.02       0.0
3  2015-04-28            39.0            69.1           0.00       0.0
4  2015-05-03            46.9            79.0           0.00       0.0

   snowdepth  avgwindspeed  fastest2minwinddir  fastest2minwindspeed  \
0        0.0          5.82                40.0                 29.97
1        0.0         11.86               180.0                 21.92
2        0.0          5.82                50.0                 12.97
3        0.0          2.68                40.0                 12.08
4        0.0          2.68               200.0                 12.08

   fastest5secwinddir  ...  drizzle snow freezingrain smokehaze thunder  \
0                30.0  ...       No   No           No       Yes      No
1               170.0  ...       No   No           No        No     Yes
2                40.0  ...       No   No           No        No      No
3                40.0  ...       No   No           No        No      No
4               210.0  ...       No   No           No        No      No

  highwind hail blowingsnow dust freezingfog
0       No   No          No   No          No
1       No   No          No   No          No
2       No   No          No   No          No
3       No   No          No   No          No
4       No   No          No   No          No

[5 rows x 28 columns]
Index(['date', 'temperaturemin', 'temperaturemax', 'precipitation', 'snowfall',
       'snowdepth', 'avgwindspeed', 'fastest2minwinddir',
       'fastest2minwindspeed', 'fastest5secwinddir', 'fastest5secwindspeed',
       'fog', 'fogheavy', 'mist', 'rain', 'fogground', 'ice', 'glaze',
       'drizzle', 'snow', 'freezingrain', 'smokehaze', 'thunder', 'highwind',
       'hail', 'blowingsnow', 'dust', 'freezingfog'],
      dtype='object')
(4557, 28)
count    4555.000000
mean        0.013723
std         0.214786
min         0.000000
25%         0.000000
50%         0.000000
75%         0.000000
max         7.010000
Name: snowfall, dtype: float64
[0.   0.12 0.2  0.31 0.39 0.51 0.59 0.71 0.79 0.91 0.98 1.18 1.42 1.89
 3.19 3.31 3.5  3.58 5.91 6.69 7.01  nan]
[4506    3    7    4    6    3    2    3    1    2    3    2    3    2
    2    1    1    1    1    1    1    2]

Queremos discretizar la variable snowfall para tener 4 categorias de nevada.

En primer lugar, usa la función qcut para hacer esta categorización. Haz una prueba y analiza por qué está función quizás no sea la mejor opción. Ahora, crea 4 categorías utilizando la función cut:

[18]:
#TODO

License: CC BY 4.0 Isaac Lera and Gabriel Moya Universitat de les Illes Balears isaac.lera@uib.edu, gabriel.moya@uib.edu