Open In Colab

5 - Agrupacions de dades

Les agrupacions són operacions necessàries per a analitzar dades, ja que permeten extreure informació en funció de dades categòriques del nostre dataframe.

Començarem aquest aprenentatge amb les següents dades

[11]:
import pandas as pd
from tabulate import tabulate # Nova llibreria

df = pd.read_csv("data/data_groups.csv") # Atenció: Dades creades aleatòriament !!

print(tabulate(df.head(), headers='keys'))

print("-"*30)
print(df.columns)
print("-"*30)
print(df.shape)
      Unnamed: 0  Dni        Nom                             CP  Ciutat      Sexe    Tipus certificat cat      Punts
--  ------------  ---------  ----------------------------  ----  ----------  ------  ----------------------  -------
 0             0  H61414629  María Dolores Arjona Jove     7800  Eivissa     M       B                            73
 1             1  S3138381C  Núria Quirós                  7511  Ruberts     F       A                            40
 2             2  J8698188C  Miguel José María Gil Vargas  7340  Alaro       M       A                            45
 3             3  A48821615  Jordi Chaves Bustamante       7609  Bellavista  F       B                            40
 4             4  U0247281I  Jana Rosa Collado Menéndez    7006  Palma       M       B                            86
------------------------------
Index(['Unnamed: 0', 'Dni', 'Nom', 'CP', 'Ciutat', 'Sexe',
       'Tipus certificat cat', 'Punts'],
      dtype='object')
------------------------------
(1000, 8)

A.4.0 Activitats “d’escalfament”

[2]:
#A401 - Quines i quantes ciutats n'hi ha?

[3]:
#A402 - Suprimeix la columna primera: "Unnamed: 0"

[4]:
#A403 - Llista les files 3, 4 i 5 amb les columnes de Nom i Punts

[5]:
#A404 - Crea dues columnes noves amb nom i llinatges de l'actual columna de "Nom".

[12]:
#A405 - Quantes categories de "Tipus de certificat cat" ?

Groups

Per a agrupar només cal usar el mètode de groupby.

En el següent exemple agrupem les dades segons el sexe de la persona mitjançant el mètode groupby que retorna un DataFrame agrupat:

[13]:
bySex = df.groupby('Sexe')
type(bySex)
[13]:
pandas.core.groupby.generic.DataFrameGroupBy
[14]:
print(bySex)
<pandas.core.groupby.generic.DataFrameGroupBy object at 0x11b5f6970>
[15]:
bySex
[15]:
<pandas.core.groupby.generic.DataFrameGroupBy object at 0x11b5f6970>

Una agupació té sentit quan se fà qualque tipus d’agregació de dades. Ara mateix la variable bySex només te informació computacional que separa els elements de cada tipus de grup. Hem de fer qualque operació, com per exemple, describe()

[43]:
# Podem descriure la informació d'un "[DataFrame]Groupby"
bySex.describe()
[43]:
Unnamed: 0 CP Punts
count mean std min 25% 50% 75% max count mean ... 75% max count mean std min 25% 50% 75% max
Sexe
F 483.0 503.248447 290.076225 1.0 245.0 496.0 757.5 999.0 483.0 7519.567288 ... 7701.0 7870.0 483.0 50.084886 28.548551 1.0 24.5 50.0 74.0 99.0
M 517.0 495.998066 287.877191 0.0 253.0 502.0 744.0 998.0 517.0 7533.143133 ... 7701.0 7870.0 517.0 49.321083 28.504273 1.0 24.0 50.0 73.0 99.0

2 rows × 24 columns

Només guarda quins elements pertanyen a cada categoria. L’atribut groups ens mostra quines mostres/files pertanyen a cada grup:

[37]:
bySex.groups # Quin tipus de variable és? Quina informació conté?
[37]:
{'F': [1, 3, 6, 7, 8, 9, 12, 23, 25, 26, 28, 29, 32, 33, 35, 36, 38, 42, 43, 44, 45, 48, 53, 54, 57, 58, 59, 63, 65, 70, 71, 72, 79, 80, 81, 82, 84, 87, 88, 92, 94, 95, 97, 98, 99, 103, 105, 106, 107, 108, 111, 112, 115, 116, 117, 118, 120, 125, 128, 129, 132, 134, 135, 136, 137, 138, 140, 141, 142, 143, 144, 147, 148, 150, 151, 153, 154, 155, 156, 161, 162, 163, 164, 165, 168, 170, 172, 174, 175, 176, 177, 178, 179, 181, 182, 184, 185, 188, 194, 196, ...], 'M': [0, 2, 4, 5, 10, 11, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 24, 27, 30, 31, 34, 37, 39, 40, 41, 46, 47, 49, 50, 51, 52, 55, 56, 60, 61, 62, 64, 66, 67, 68, 69, 73, 74, 75, 76, 77, 78, 83, 85, 86, 89, 90, 91, 93, 96, 100, 101, 102, 104, 109, 110, 113, 114, 119, 121, 122, 123, 124, 126, 127, 130, 131, 133, 139, 145, 146, 149, 152, 157, 158, 159, 160, 166, 167, 169, 171, 173, 180, 183, 186, 187, 189, 190, 191, 192, 193, 195, 197, 199, 201, ...]}

Aquest atribut, (un dictionari) ens permet realitzar operacions de filtrat en funció dels grups:

[40]:
dfM = df.loc[bySex.groups['M'].values] #Recorda que "loc" accedeix per index de fila
dfM
[40]:
Unnamed: 0 Dni Nom CP Ciutat Sexe Tipus certificat cat Punts
0 0 H61414629 María Dolores Arjona Jove 7800 Eivissa M B 73
2 2 J8698188C Miguel José María Gil Vargas 7340 Alaro M A 45
4 4 U0247281I Jana Rosa Collado Menéndez 7006 Palma M B 86
5 5 V3761435A Antonio Murillo Suarez 7701 Mao M B 53
10 10 A64863723 Nil Mínguez Hervás 7350 Binissalem M B 17
... ... ... ... ... ... ... ... ...
991 991 G28994028 Juan Cervera Arribas 7006 Palma M B 83
993 993 J3906733E Josefa María Dolores Barreda Ibañez 7609 Bellavista M B 17
994 994 C26665125 Josefa Ferrando-Navas 7340 Alaro M A 56
996 996 H72967045 Álex Javier Campos Palomar 7701 Mao M B 72
998 998 C61296679 Èric Pinedo 7009 Palma M A 1

517 rows × 8 columns

Obviament: Segons el tipus d’agrupació, l’operació pot realitzar-se com una selecció lògica.

[41]:
df[df.Sexe=="M"]
[41]:
Unnamed: 0 Dni Nom CP Ciutat Sexe Tipus certificat cat Punts
0 0 H61414629 María Dolores Arjona Jove 7800 Eivissa M B 73
2 2 J8698188C Miguel José María Gil Vargas 7340 Alaro M A 45
4 4 U0247281I Jana Rosa Collado Menéndez 7006 Palma M B 86
5 5 V3761435A Antonio Murillo Suarez 7701 Mao M B 53
10 10 A64863723 Nil Mínguez Hervás 7350 Binissalem M B 17
... ... ... ... ... ... ... ... ...
991 991 G28994028 Juan Cervera Arribas 7006 Palma M B 83
993 993 J3906733E Josefa María Dolores Barreda Ibañez 7609 Bellavista M B 17
994 994 C26665125 Josefa Ferrando-Navas 7340 Alaro M A 56
996 996 H72967045 Álex Javier Campos Palomar 7701 Mao M B 72
998 998 C61296679 Èric Pinedo 7009 Palma M A 1

517 rows × 8 columns

[16]:
#Cada dataframegroupby te dos elements: el id-value del grup i el df de elements d'aquest grup
for identificador, group in df.groupby('Sexe'):
    # Criteri de l'agrupacio: M o F
    print("*"*10)
    print(identificador)
    print("*"*10)
    # Dataframe específic
    print(group.head())

    print("-"*60)
**********
F
**********
   Unnamed: 0        Dni                      Nom    CP      Ciutat Sexe  \
1           1  S3138381C             Núria Quirós  7511     Ruberts    F
3           3  A48821615  Jordi Chaves Bustamante  7609  Bellavista    F
6           6  H16936148         Arlet del Rovira  7350  Binissalem    F
7           7  E47121991         Emma Tapia Salas  7340       Alaro    F
8           8  E15932486             Jana Barrios  7870   La Savina    F

  Tipus certificat cat  Punts
1                    A     40
3                    B     40
6                    A     54
7                    C     60
8                    A     35
------------------------------------------------------------
**********
M
**********
    Unnamed: 0        Dni                           Nom    CP      Ciutat  \
0            0  H61414629     María Dolores Arjona Jove  7800     Eivissa
2            2  J8698188C  Miguel José María Gil Vargas  7340       Alaro
4            4  U0247281I    Jana Rosa Collado Menéndez  7006       Palma
5            5  V3761435A        Antonio Murillo Suarez  7701         Mao
10          10  A64863723            Nil Mínguez Hervás  7350  Binissalem

   Sexe Tipus certificat cat  Punts
0     M                    B     73
2     M                    A     45
4     M                    B     86
5     M                    B     53
10    M                    B     17
------------------------------------------------------------

Agregacions

El mètodo aggregate ens permet crear variables d’agregació sobre la taula obtinguda amb groupby. Indicarem la informació que volem obtenir de cada columna amb un diccionari. Especifiquem la funció que aplicarem a les dades de cada grup en cada columna per a obtenir un únic valor.

[17]:
df.groupby(["Tipus certificat cat"]).aggregate(
    {               # Dictionari
    "Punts":sum     # Columna : Operacion de agregació
    }
    ) #
[17]:
Punts
Tipus certificat cat
A 22502
B 17289
C 7399
D 2500

Podem aplicar un gran nombre de funcions d’agregació:

[18]:
df.groupby(["Tipus certificat cat"]).aggregate({"Punts":max})
[18]:
Punts
Tipus certificat cat
A 99
B 99
C 99
D 99

Si volem incloure la mateixa columna múltiples vegades, hem de fer servir el mètode agg

[23]:
import numpy as np
## Una manera
dftipus = df.groupby(["Tipus certificat cat"]).agg(Maximo=('Punts', max), Minim=('Punts', 'min'), Mitj=('Punts', np.mean))
dftipus
[23]:
Maximo Minim Mitj
Tipus certificat cat
A 99 1 50.115813
B 99 1 49.681034
C 99 1 48.045455
D 99 1 51.020408
[25]:
type(dftipus) #Nota: Una vegada feta una agregació damunt una agrupació obtenim un dataframe !!!
[25]:
pandas.core.frame.DataFrame
[26]:
dftipus[dftipus.Mitj<50]
[26]:
Maximo Minim Mitj
Tipus certificat cat
B 99 1 49.681034
C 99 1 48.045455

4.1. Activitats

[27]:
# 4.1.1 Quina estructura té aquest dataframe: shape, index, columns?

[28]:
# 4.1.2 Pots accedir al valor mínim del certificat "C"

[29]:
# 4.1.3 Pots canviar el nom a les columnes a "max punts", "min punts" i "mitjana punts", respectivament.
[31]:
# Una altra manera
dftipus2 = df.groupby(["Tipus certificat cat"]).agg({
        'Punts': ['mean', 'count', np.min, np.std],
   })

dftipus2
[31]:
Punts
mean count amin std
Tipus certificat cat
A 50.115813 449 1 28.518712
B 49.681034 348 1 28.271817
C 48.045455 154 1 27.907724
D 51.020408 49 1 32.520180
[32]:
dftipus2.columns ## ALERTA ! MULTIINDEX
[32]:
MultiIndex([('Punts',  'mean'),
            ('Punts', 'count'),
            ('Punts',  'amin'),
            ('Punts',   'std')],
           )
[39]:
#Si volem accedir a una columna específica

dftipus2["Punts"]["mean"] ### Obliga a fer ús d'un conjunt seguit de "keys" de columnes
[39]:
Tipus certificat cat
A    50.115813
B    49.681034
C    48.045455
D    51.020408
Name: mean, dtype: float64
[40]:
dftipus2[("Punts","mean")] ### O fer us del concepte de tupla
[40]:
Tipus certificat cat
A    50.115813
B    49.681034
C    48.045455
D    51.020408
Name: (Punts, mean), dtype: float64

Agrupacions amb múltiples columnes

En una agrupació amb diversos criteris, es creen totes les combinacions possibles entre els criteris donant lloc a un producte cartesià de les agregacions indicades.

[67]:
dftc = df.groupby(["Tipus certificat cat","Ciutat"]).mean()
dftc
[67]:
Unnamed: 0 CP Punts
Tipus certificat cat Ciutat
A Alaro 447.929825 7340.000000 50.789474
Ariany 547.203704 7529.000000 52.537037
Bellavista 499.148936 7609.000000 46.787234
Binissalem 535.375000 7350.000000 44.325000
Eivissa 504.931818 7800.000000 52.227273
La Savina 468.194444 7870.000000 47.944444
Mao 528.918919 7701.864865 47.027027
Palma 471.918367 7009.469388 51.081633
Pedruscada 475.347826 7590.000000 56.847826
Ruberts 503.487179 7511.000000 49.128205
B Alaro 485.333333 7340.000000 52.452381
Ariany 496.404762 7529.000000 49.166667
Bellavista 540.851852 7609.000000 44.185185
Binissalem 560.909091 7350.000000 52.787879
Eivissa 525.484848 7800.000000 47.393939
La Savina 524.470588 7870.000000 44.441176
Mao 497.040816 7701.775510 50.673469
Palma 493.062500 7008.625000 55.375000
Pedruscada 486.606061 7590.000000 50.484848
Ruberts 391.956522 7511.000000 47.391304
C Alaro 451.625000 7340.000000 39.687500
Ariany 606.583333 7529.000000 41.833333
Bellavista 503.111111 7609.000000 37.500000
Binissalem 478.058824 7350.000000 67.823529
Eivissa 525.777778 7800.000000 41.444444
La Savina 508.357143 7870.000000 52.500000
Mao 433.428571 7701.857143 52.428571
Palma 371.333333 7011.666667 33.083333
Pedruscada 554.000000 7590.000000 53.166667
Ruberts 541.823529 7511.000000 51.235294
D Alaro 203.200000 7340.000000 66.800000
Ariany 446.600000 7529.000000 47.200000
Bellavista 728.833333 7609.000000 56.333333
Binissalem 470.000000 7350.000000 67.666667
Eivissa 581.800000 7800.000000 35.800000
La Savina 455.333333 7870.000000 43.000000
Mao 467.000000 7703.000000 20.000000
Palma 613.666667 7008.000000 30.333333
Pedruscada 496.777778 7590.000000 66.777778
Ruberts 636.833333 7511.000000 40.000000

Observacions - És necessari la mitja de “CP”?

4.2 Activitat

[41]:
# 4.2.1 Fes que només surti la columna de "Punts" a l'agrupació anterior.

[42]:
# 4.2.2 Pots seleccionar només les mostres de Mao.
[43]:
# 4.2.3 Com podries calcular la mida de cada grup?

MultIndex

A vegades un índex no és suficient per expressar la meta-informació que identifica una o algunes columnes. Per exemple, una coordenada està formada per la latitud i longitud.

Un multindex és una jerarquia d’índexs.

Una agrupació amb diferents criteris origina un multiindex

[45]:
dftc = df.groupby(["Tipus certificat cat","Ciutat"]).mean()
dftc.index
[45]:
MultiIndex([('A',      'Alaro'),
            ('A',     'Ariany'),
            ('A', 'Bellavista'),
            ('A', 'Binissalem'),
            ('A',    'Eivissa'),
            ('A',  'La Savina'),
            ('A',        'Mao'),
            ('A',      'Palma'),
            ('A', 'Pedruscada'),
            ('A',    'Ruberts'),
            ('B',      'Alaro'),
            ('B',     'Ariany'),
            ('B', 'Bellavista'),
            ('B', 'Binissalem'),
            ('B',    'Eivissa'),
            ('B',  'La Savina'),
            ('B',        'Mao'),
            ('B',      'Palma'),
            ('B', 'Pedruscada'),
            ('B',    'Ruberts'),
            ('C',      'Alaro'),
            ('C',     'Ariany'),
            ('C', 'Bellavista'),
            ('C', 'Binissalem'),
            ('C',    'Eivissa'),
            ('C',  'La Savina'),
            ('C',        'Mao'),
            ('C',      'Palma'),
            ('C', 'Pedruscada'),
            ('C',    'Ruberts'),
            ('D',      'Alaro'),
            ('D',     'Ariany'),
            ('D', 'Bellavista'),
            ('D', 'Binissalem'),
            ('D',    'Eivissa'),
            ('D',  'La Savina'),
            ('D',        'Mao'),
            ('D',      'Palma'),
            ('D', 'Pedruscada'),
            ('D',    'Ruberts')],
           names=['Tipus certificat cat', 'Ciutat'])
[46]:
dftc.loc["A"] # primer index
[46]:
Unnamed: 0 CP Punts
Ciutat
Alaro 447.929825 7340.000000 50.789474
Ariany 547.203704 7529.000000 52.537037
Bellavista 499.148936 7609.000000 46.787234
Binissalem 535.375000 7350.000000 44.325000
Eivissa 504.931818 7800.000000 52.227273
La Savina 468.194444 7870.000000 47.944444
Mao 528.918919 7701.864865 47.027027
Palma 471.918367 7009.469388 51.081633
Pedruscada 475.347826 7590.000000 56.847826
Ruberts 503.487179 7511.000000 49.128205
[47]:
dftc.loc["Mao"] # dependent index
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
File ~/.pyenv/versions/3.9.7/envs/my397/lib/python3.9/site-packages/pandas/core/indexes/base.py:3621, in Index.get_loc(self, key, method, tolerance)
   3620 try:
-> 3621     return self._engine.get_loc(casted_key)
   3622 except KeyError as err:

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

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

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

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

KeyError: 'Mao'

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

KeyError                                  Traceback (most recent call last)
/Users/isaac/Projects/TallerPythonFEE/lessons/4/4_PandasGroups.ipynb Cell 51 in <cell line: 1>()
----> <a href='vscode-notebook-cell:/Users/isaac/Projects/TallerPythonFEE/lessons/4/4_PandasGroups.ipynb#X50sZmlsZQ%3D%3D?line=0'>1</a> dftc.loc["Mao"]

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

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

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

File ~/.pyenv/versions/3.9.7/envs/my397/lib/python3.9/site-packages/pandas/core/generic.py:3857, in NDFrame.xs(self, key, axis, level, drop_level)
   3854 self._consolidate_inplace()
   3856 if isinstance(index, MultiIndex):
-> 3857     loc, new_index = index._get_loc_level(key, level=0)
   3858     if not drop_level:
   3859         if lib.is_integer(loc):

File ~/.pyenv/versions/3.9.7/envs/my397/lib/python3.9/site-packages/pandas/core/indexes/multi.py:3113, in MultiIndex._get_loc_level(self, key, level)
   3111         return indexer, maybe_mi_droplevels(indexer, ilevels)
   3112 else:
-> 3113     indexer = self._get_level_indexer(key, level=level)
   3114     if (
   3115         isinstance(key, str)
   3116         and self.levels[level]._supports_partial_string_indexing
   3117     ):
   3118         # check to see if we did an exact lookup vs sliced
   3119         check = self.levels[level].get_loc(key)

File ~/.pyenv/versions/3.9.7/envs/my397/lib/python3.9/site-packages/pandas/core/indexes/multi.py:3222, in MultiIndex._get_level_indexer(self, key, level, indexer)
   3218         return slice(i, j, step)
   3220 else:
-> 3222     idx = self._get_loc_single_level_index(level_index, key)
   3224     if level > 0 or self._lexsort_depth == 0:
   3225         # Desired level is not sorted
   3226         if isinstance(idx, slice):
   3227             # test_get_loc_partial_timestamp_multiindex

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

File ~/.pyenv/versions/3.9.7/envs/my397/lib/python3.9/site-packages/pandas/core/indexes/base.py:3623, in Index.get_loc(self, key, method, tolerance)
   3621     return self._engine.get_loc(casted_key)
   3622 except KeyError as err:
-> 3623     raise KeyError(key) from err
   3624 except TypeError:
   3625     # If we have a listlike key, _check_indexing_error will raise
   3626     #  InvalidIndexError. Otherwise we fall through and re-raise
   3627     #  the TypeError.
   3628     self._check_indexing_error(key)

KeyError: 'Mao'
[53]:
dftc.loc[("A","Mao")]
[53]:
Unnamed: 0     528.918919
CP            7701.864865
Punts           47.027027
Name: (A, Mao), dtype: float64
[55]:
#Podem seleccionar totes les Categories de Mao
dftc.loc[[:,"Mao"]]
  Input In [55]
    dftc.loc([:,"Mao"])
              ^
SyntaxError: invalid syntax

[56]:
# Una manera més elegant és:
dftc.loc[pd.IndexSlice[:, 'Mao'],:] # Quins parametres n'hi ha? LOC i IndexSlice
# https://pandas.pydata.org/docs/reference/api/pandas.IndexSlice.html
[56]:
Unnamed: 0 CP Punts
Tipus certificat cat Ciutat
A Mao 528.918919 7701.864865 47.027027
B Mao 497.040816 7701.775510 50.673469
C Mao 433.428571 7701.857143 52.428571
D Mao 467.000000 7703.000000 20.000000
[83]:
dfBCMao = dftc.loc[pd.IndexSlice[["B","C"], 'Mao'],"Punts"]
dfBCMao
[83]:
Tipus certificat cat  Ciutat
B                     Mao       50.673469
C                     Mao       52.428571
Name: Punts, dtype: float64

Una altra opció és aplanar o esborrar l’índex

[89]:
dftc = df.groupby(["Tipus certificat cat","Ciutat"]).mean().reset_index()
print(dftc)
   Tipus certificat cat      Ciutat  Unnamed: 0           CP      Punts
0                     A       Alaro  447.929825  7340.000000  50.789474
1                     A      Ariany  547.203704  7529.000000  52.537037
2                     A  Bellavista  499.148936  7609.000000  46.787234
3                     A  Binissalem  535.375000  7350.000000  44.325000
4                     A     Eivissa  504.931818  7800.000000  52.227273
5                     A   La Savina  468.194444  7870.000000  47.944444
6                     A         Mao  528.918919  7701.864865  47.027027
7                     A       Palma  471.918367  7009.469388  51.081633
8                     A  Pedruscada  475.347826  7590.000000  56.847826
9                     A     Ruberts  503.487179  7511.000000  49.128205
10                    B       Alaro  485.333333  7340.000000  52.452381
11                    B      Ariany  496.404762  7529.000000  49.166667
12                    B  Bellavista  540.851852  7609.000000  44.185185
13                    B  Binissalem  560.909091  7350.000000  52.787879
14                    B     Eivissa  525.484848  7800.000000  47.393939
15                    B   La Savina  524.470588  7870.000000  44.441176
16                    B         Mao  497.040816  7701.775510  50.673469
17                    B       Palma  493.062500  7008.625000  55.375000
18                    B  Pedruscada  486.606061  7590.000000  50.484848
19                    B     Ruberts  391.956522  7511.000000  47.391304
20                    C       Alaro  451.625000  7340.000000  39.687500
21                    C      Ariany  606.583333  7529.000000  41.833333
22                    C  Bellavista  503.111111  7609.000000  37.500000
23                    C  Binissalem  478.058824  7350.000000  67.823529
24                    C     Eivissa  525.777778  7800.000000  41.444444
25                    C   La Savina  508.357143  7870.000000  52.500000
26                    C         Mao  433.428571  7701.857143  52.428571
27                    C       Palma  371.333333  7011.666667  33.083333
28                    C  Pedruscada  554.000000  7590.000000  53.166667
29                    C     Ruberts  541.823529  7511.000000  51.235294
30                    D       Alaro  203.200000  7340.000000  66.800000
31                    D      Ariany  446.600000  7529.000000  47.200000
32                    D  Bellavista  728.833333  7609.000000  56.333333
33                    D  Binissalem  470.000000  7350.000000  67.666667
34                    D     Eivissa  581.800000  7800.000000  35.800000
35                    D   La Savina  455.333333  7870.000000  43.000000
36                    D         Mao  467.000000  7703.000000  20.000000
37                    D       Palma  613.666667  7008.000000  30.333333
38                    D  Pedruscada  496.777778  7590.000000  66.777778
39                    D     Ruberts  636.833333  7511.000000  40.000000
[93]:
print(dftc[dftc["Ciutat"]=="Mao"])
   Tipus certificat cat Ciutat  Unnamed: 0           CP      Punts
6                     A    Mao  528.918919  7701.864865  47.027027
16                    B    Mao  497.040816  7701.775510  50.673469
26                    C    Mao  433.428571  7701.857143  52.428571
36                    D    Mao  467.000000  7703.000000  20.000000
[96]:
print(dftc[dftc["Ciutat"]=="Mao"].Punts) # Quina informació trobeu?

6     47.027027
16    50.673469
26    52.428571
36    20.000000
Name: Punts, dtype: float64

Activitat. 4.3

[59]:
# Com podem agafar només les categories B i C de la ciutat de Mao?

Agregacions avançades

Damunt d’una agregació podem fer operacions diferents a les aritmètiques.

Per exemple, si volem crear un histograma de la distribució de tipus de certificat per Ciutat i CP, necessitem crear una llista per a cada grup.

[60]:
# Obtindre un llistat de certificats per agrupació de ciutat i codi postal
df = pd.read_csv("data/data_groups.csv")
df.head()
[60]:
Unnamed: 0 Dni Nom CP Ciutat Sexe Tipus certificat cat Punts
0 0 H61414629 María Dolores Arjona Jove 7800 Eivissa M B 73
1 1 S3138381C Núria Quirós 7511 Ruberts F A 40
2 2 J8698188C Miguel José María Gil Vargas 7340 Alaro M A 45
3 3 A48821615 Jordi Chaves Bustamante 7609 Bellavista F B 40
4 4 U0247281I Jana Rosa Collado Menéndez 7006 Palma M B 86
[61]:
dfc = df.groupby(["Ciutat","CP"])["Tipus certificat cat"].apply(list)
dfc
[61]:
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
[62]:
# Podem fer més operatives amb els valors obtinguts
# Calcula la distribucció de tipus de certificats
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}
[63]:
## 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
[64]:
# Qué és una funció lambda?
[195]:
# 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

Les agrupacions són un mecanisme de selecció agregada de la informació. I el resultat, són tipus compatibles: dataframes o sèries, per tant, entre altres coses, podem fer una visualització:

[196]:
df3.columns = ['Candidats per damunt dels 50 punts',"Candidats totals"]
df3.reset_index(inplace=True)
df3.drop(columns="CP",inplace=True)
df3.index = df3.Ciutat
[194]:
df3.plot(kind="bar") #Alerta amb la interpretació: punts per CP i Ciutat
[194]:
<AxesSubplot:xlabel='Ciutat'>
../../_images/lessons_4_4_PandasGroups_70_1.png

Activitat

  1. Agrupa els segënts dades per el nom de la escola.

  • Quina escola té més nins?

  • Quina escola té els nins més alts?

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

[65]:
school class name date_Of_Birth age height weight address
S1 s001 V Alberto Franco 15/05/2002 12 173 35 street1
S2 s002 V Gino Mcneill 17/05/2002 12 192 32 street2
S3 s003 VI Ryan Parkes 16/02/1999 13 186 33 street3
S4 s001 VI Eesha Hinton 25/09/1998 13 167 30 street1
S5 s002 V Gino Mcneill 11/05/2002 14 151 31 street2
[ ]:
#TODO
  1. Donat el següent llistat de vendes:

  • Quin comprador ha gastat més?

  • Quin vendedor ha fet més vendes?

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

# Crear datos aleatorios
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      44     Pedro   Carlos
1    Producto 2      37     Maria    Elena
2    Producto 3      16     Maria   Carlos
3    Producto 4      35       Ana    Elena
4    Producto 5      83     Maria   Miguel
5    Producto 6      64      Juan    Laura
6    Producto 7      17       Ana    Laura
7    Producto 8      88     Pedro   Carlos
8    Producto 9      74      Juan   Miguel
9   Producto 10      41       Ana   Miguel
10  Producto 11      58      Juan   Carlos
11  Producto 12      29       Ana    Elena
12  Producto 13      66     Maria   Miguel
13  Producto 14      32       Ana   Miguel
14  Producto 15      54       Ana    Laura
[ ]:
#TODO
  1. Usant el fitxer 1/data/WHO.csv, Quin és el volum total de CO2 emès per cada continent? Qui continent emet més CO2?

[198]:
#TODO
  1. Usant el fitxer 1/data/WHO.csv, Quants països hi ha per continent? Quin continent té més països?

[197]:
#TODO
  1. Usant el fitxer 1/data/presupuesto_gastos_2023.csv, Calcula la despesa total per “SAILAREN DESKRIBAPENA_EU/DESCRIPCION DEPARTAMENT_CAS” ? Ordena el resultats per despesa (descendent)

[ ]:
#TODO
  1. Fitxer “data/data_groups_cursos.csv”, El fitxer conté cursos realitzats per persones. Cada mostra correspon a un curs. Es demana obtenir un llistat de número de cursos realitzat per cada persona, una valoració de punts segons el curs de la següent manera:

  • ‘A’: 3 punts

  • ‘B’: 2 punts

  • ‘C’: 1 punt

  • ‘D’: 0.5 punt

  • “E”: 0.5 punt

  • “F”: 1 punt

[199]:
#TODO

D.2 (amb el anterior cas D), tonar a calcula la valoració de punts considerant la combinació de cursos (independentment del seu ordre): - A, B i C : +10 punts - A i F: +3 punts - E i F: -10 punts - C i F: +5 punts

[201]:
#TODO

Activitat 4 del homework

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