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:
Funciones estadísticas: mean, std, …
Funciones matemáticas: sum, prod, …
Otras funciones: max, min, …
[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.
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
Isaac Lera and Gabriel Moya Universitat de les Illes Balears isaac.lera@uib.edu, gabriel.moya@uib.edu