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

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

df= pd.read_csv("../../data/Pandas/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:

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

El atributo groups nos muestra los grupos hemos creado:

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

[3]:
{'F': [2, 3], 'M': [0, 1, 4, 5]}
[4]:
bySex.describe()
[4]:
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:

[5]:
dfM = df.loc[bySex.groups['M'].values] #Recorda que "loc" accedeix per index de fila
dfM
[5]:
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
[6]:
df[df.Sexo=="M"] # es lo  mismo!
[6]:
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.

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

dfg
[7]:
Altura Cof
Categoria
cap6 5.32 0.530
cap8 1.78 0.970
laboral 3.41 0.585
[8]:
dfg.index
[8]:
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:

[11]:
gr = df.groupby(['Sexo',"Categoria"]).mean(numeric_only=True)
gr


[11]:
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

Si queremos realizar un conteo de los elementos, debemos seleccionar

[12]:
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'])
[14]:
gr.loc[("M",:)] # Como accedemos en rango en un multiindice?
  Cell In[14], line 1
    gr.loc[("M",:)] # Como accedemos en rango en un multiindice?
                ^
SyntaxError: invalid syntax

[15]:
gr.loc["F"] # primer index
[15]:
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 ~/PycharmProjects/TTAD/.venv/lib/python3.12/site-packages/pandas/core/indexes/base.py:3812, in Index.get_loc(self, key)
   3811 try:
-> 3812     return self._engine.get_loc(casted_key)
   3813 except KeyError as err:

File pandas/_libs/index.pyx:167, in pandas._libs.index.IndexEngine.get_loc()

File pandas/_libs/index.pyx:196, in pandas._libs.index.IndexEngine.get_loc()

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

File pandas/_libs/hashtable_class_helper.pxi:7096, 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)
Cell In[16], line 1
----> 1 gr.loc["cap6"] # dependent index

File ~/PycharmProjects/TTAD/.venv/lib/python3.12/site-packages/pandas/core/indexing.py:1192, in _LocationIndexer.__getitem__(self, key)
   1190 maybe_callable = com.apply_if_callable(key, self.obj)
   1191 maybe_callable = self._check_deprecated_callable_usage(key, maybe_callable)
-> 1192 return self._getitem_axis(maybe_callable, axis=axis)

File ~/PycharmProjects/TTAD/.venv/lib/python3.12/site-packages/pandas/core/indexing.py:1432, in _LocIndexer._getitem_axis(self, key, axis)
   1430 # fall thru to straight lookup
   1431 self._validate_key(key, axis)
-> 1432 return self._get_label(key, axis=axis)

File ~/PycharmProjects/TTAD/.venv/lib/python3.12/site-packages/pandas/core/indexing.py:1382, in _LocIndexer._get_label(self, label, axis)
   1380 def _get_label(self, label, axis: AxisInt):
   1381     # GH#5567 this will fail if the label is not present in the axis.
-> 1382     return self.obj.xs(label, axis=axis)

File ~/PycharmProjects/TTAD/.venv/lib/python3.12/site-packages/pandas/core/generic.py:4315, in NDFrame.xs(self, key, axis, level, drop_level)
   4312     index = self.index
   4314 if isinstance(index, MultiIndex):
-> 4315     loc, new_index = index._get_loc_level(key, level=0)
   4316     if not drop_level:
   4317         if lib.is_integer(loc):
   4318             # Slice index must be an integer or None

File ~/PycharmProjects/TTAD/.venv/lib/python3.12/site-packages/pandas/core/indexes/multi.py:3309, in MultiIndex._get_loc_level(self, key, level)
   3307         return indexer, maybe_mi_droplevels(indexer, ilevels)
   3308 else:
-> 3309     indexer = self._get_level_indexer(key, level=level)
   3310     if (
   3311         isinstance(key, str)
   3312         and self.levels[level]._supports_partial_string_indexing
   3313     ):
   3314         # check to see if we did an exact lookup vs sliced
   3315         check = self.levels[level].get_loc(key)

File ~/PycharmProjects/TTAD/.venv/lib/python3.12/site-packages/pandas/core/indexes/multi.py:3410, in MultiIndex._get_level_indexer(self, key, level, indexer)
   3407         return slice(i, j, step)
   3409 else:
-> 3410     idx = self._get_loc_single_level_index(level_index, key)
   3412     if level > 0 or self._lexsort_depth == 0:
   3413         # Desired level is not sorted
   3414         if isinstance(idx, slice):
   3415             # test_get_loc_partial_timestamp_multiindex

File ~/PycharmProjects/TTAD/.venv/lib/python3.12/site-packages/pandas/core/indexes/multi.py:2999, in MultiIndex._get_loc_single_level_index(self, level_index, key)
   2997     return -1
   2998 else:
-> 2999     return level_index.get_loc(key)

File ~/PycharmProjects/TTAD/.venv/lib/python3.12/site-packages/pandas/core/indexes/base.py:3819, in Index.get_loc(self, key)
   3814     if isinstance(casted_key, slice) or (
   3815         isinstance(casted_key, abc.Iterable)
   3816         and any(isinstance(x, slice) for x in casted_key)
   3817     ):
   3818         raise InvalidIndexError(key)
-> 3819     raise KeyError(key) from err
   3820 except TypeError:
   3821     # If we have a listlike key, _check_indexing_error will raise
   3822     #  InvalidIndexError. Otherwise we fall through and re-raise
   3823     #  the TypeError.
   3824     self._check_indexing_error(key)

KeyError: 'cap6'
[17]:
# Necesitamos invocar un IndexSlice.
gr.loc[pd.IndexSlice[:, 'cap6'],:]
# https://pandas.pydata.org/docs/reference/api/pandas.IndexSlice.html
[17]:
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:

[18]:
gr = df.groupby(['Sexo',"Categoria"]).count().reset_index()
gr
[18]:
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
[19]:
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/Pandas/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)
[24]:
dfc = df.groupby(["Ciutat","CP"])["Tipus certificat cat"].apply(list)
dfc
[24]:
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
[25]:
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]
----------------------------------------
{np.str_('A'): np.int64(57), np.str_('B'): np.int64(42), np.str_('C'): np.int64(16), np.str_('D'): np.int64(5)}

En pandas, además de utilizar las funciones de agregación predefinidas como sum(), mean(), min() o max(), también podemos emplear funciones propias (definidas por el usuario) para realizar agregaciones más flexibles y adaptadas a nuestras necesidades.

Estas funciones personalizadas pueden ser cualquier función de Python que reciba una serie de datos como entrada y devuelva un único valor como resultado. Esto permite aplicar cálculos específicos, métricas avanzadas o transformaciones que no están disponibles de forma nativa en pandas.

[26]:
df2 = df.groupby(["Ciutat","CP"]).agg({"Tipus certificat cat": np.size}) # lambda !

print(df2)
                 Tipus certificat cat
Ciutat     CP
Alaro      7340                   120
Ariany     7529                   113
Bellavista 7609                    98
Binissalem 7350                    93
Eivissa    7800                    91
La Savina  7870                    90
Mao        7701                    63
           7703                    45
Palma      7006                    36
           7009                    23
           7013                    37
Pedruscada 7590                   106
Ruberts    7511                    85

Funciones Lambda

En Python, las funciones lambda permiten definir funciones pequeñas y anónimas de forma compacta. Se utilizan especialmente cuando necesitamos una función sencilla para una operación puntual y no queremos crear una función completa con def.

Una función lambda se escribe en una sola línea y puede recibir uno o varios argumentos, pero solo puede contener una expresión, cuyo resultado se devuelve automáticamente. Gracias a su simplicidad, las lambdas son muy útiles en operaciones como filtrado, transformación o ordenación de datos, así como al trabajar con funciones de orden superior como map(), filter() o sorted().

Su uso hace el código más expresivo y conciso en situaciones donde definir una función tradicional sería innecesariamente largo. Sin embargo, se recomienda utilizarlas únicamente para tareas simples, manteniendo la legibilidad del código como prioridad.

[29]:
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)
[np.int64(102), np.int64(102), np.int64(18), np.int64(27), np.int64(51), np.int64(66), np.int64(146), np.int64(20313051), np.int64(902)]

El siguiente código muestra cómo definir una función personalizada en Python para utilizarla como parte de una agregación en pandas. En este caso, la función miBarem50p recibe una serie y cuenta cuántos valores son mayores que 50. Posteriormente, esta función se aplica dentro de un groupby mediante una función lambda, lo que permite integrar la lógica propia dentro del proceso de agregación junto con otras funciones como np.size. Esto hace posible obtener métricas específicas adaptadas a nuestras necesidades durante el análisis de los datos.

[28]:
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]})

print(df3)
                     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 dejar esto pendiente para isaac futuro
#hola
#HOLA

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

Función cut

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:

[3]:
import numpy as np
import pandas as pd
np.random.seed(0)
df_random = pd.DataFrame({"candidat":np.arange(1,11), "nota":np.random.randint(0,11,size=10)})
df_random
[3]:
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

Actividad

[16]:
# 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)
(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

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