Operacions avançades en pandas

Lectura d’un fitxer en format CSV

Pandas és capaç de llegir diversos formats: CSV, excel, sql, json …

  • CSV = Comma Separated Values, és a dir, un fitxer de text separat per comes (o un altre caràcter)

  • Per examinar el contingut exacte d’un fitxer CSV es pot editar amb qualsevol editor de text planer (l’editor d’idle, emacs, …)

  • En llegir un fitxer, es pot indicar el nom o bé una URL

  • pandas.read_csv() admet molts paràmetres opcionals

>>> import pandas as pd

>>> url = "https://perso.telecom-paristech.fr/eagan/class/igr204/data/cereal.csv"
>>> d3 = pd.read_csv(url, sep=';', usecols=['name', 'type', 'rating'])
>>> d3.head()  # examinar les primeres files; també existeix tail()
                        name         type     rating
0                     String  Categorical      Float
1                  100% Bran            C  68.402973
2          100% Natural Bran            C  33.983679
3                   All-Bran            C  59.425505
4  All-Bran with Extra Fiber            C  93.704912
>>> df = pd.read_csv(url, sep=';', 
...         usecols=['name', 'calories', 'sugars', 'rating'], # ens quedem amb només algunes columnes
...        skiprows=[1]) # saltem la línia, que conté els tipus
>>> df = df.iloc[3:10]  # ens quedem només amb algunes files

Treballar amb valors nuls (missing data)


>>> import numpy as np
>>> df.loc[7, 'sugars'] = None  
>>> df.loc[20, :] = np.nan
>>> df
                         name  calories  sugars     rating
3   All-Bran with Extra Fiber      50.0     0.0  93.704912
4              Almond Delight     110.0     8.0  34.384843
5     Apple Cinnamon Cheerios     110.0    10.0  29.509541
6                 Apple Jacks     110.0    14.0  33.174094
7                     Basic 4     130.0     NaN  37.038562
8                   Bran Chex      90.0     6.0  49.120253
9                 Bran Flakes      90.0     5.0  53.313813
20                        NaN       NaN     NaN        NaN
>>> df.info()  # info() indica valors nuls:
<class 'pandas.core.frame.DataFrame'>
Int64Index: 8 entries, 3 to 20
Data columns (total 4 columns):
 #   Column    Non-Null Count  Dtype  
---  ------    --------------  -----  
 0   name      7 non-null      object 
 1   calories  7 non-null      float64
 2   sugars    6 non-null      float64
 3   rating    7 non-null      float64
dtypes: float64(3), object(1)
memory usage: 620.0+ bytes
>>> df.isna()  # també existeix el mètode notna()
     name  calories  sugars  rating
3   False     False   False   False
4   False     False   False   False
5   False     False   False   False
6   False     False   False   False
7   False     False    True   False
8   False     False   False   False
9   False     False   False   False
20   True      True    True    True
>>> df.dropna()  # elimina les files on hi ha valors nuls
                        name  calories  sugars     rating
3  All-Bran with Extra Fiber      50.0     0.0  93.704912
4             Almond Delight     110.0     8.0  34.384843
5    Apple Cinnamon Cheerios     110.0    10.0  29.509541
6                Apple Jacks     110.0    14.0  33.174094
8                  Bran Chex      90.0     6.0  49.120253
9                Bran Flakes      90.0     5.0  53.313813
>>> df['calories'].sum()  # El mètodes de resum salten valors nuls...
690.0
>>> df.mean(skipna=False, axis=1) # .. però podem indicar skpina=False
3     47.901637
4     50.794948
5     49.836514
6     52.391365
7           NaN
8     48.373418
9     49.437938
20          NaN
dtype: float64
>>> df['sugars'] = df['sugars'].fillna(7) # substituir valor nuls

Operacions vectorials

Pandas suporta les operacions vectorials, és a dir, operar amb tota una columna (o fila) element a element (multiplicar un valor a tota una columna, sumar dues columnes o files, etc).


>>> df['calories'] * 2
3     100.0
4     220.0
5     220.0
6     220.0
7     260.0
8     180.0
9     180.0
20      NaN
Name: calories, dtype: float64
>>> df['extra'] = df['sugars'] / df['calories']

Altres mètodes: idxmax, replace, apply, …


>>> df['sugars'].idxmax()   # retorna **l'índex**, no el número de fila
6
>>> pmax = df['calories'].idxmax()
>>> df.loc[pmax]['name']  # nom dels cereals que tenen màximes calories
'Basic 4'
>>> df.sort_values('name') # ordenem pel nom
                         name  calories  sugars     rating     extra
3   All-Bran with Extra Fiber      50.0     0.0  93.704912  0.000000
4              Almond Delight     110.0     8.0  34.384843  0.072727
5     Apple Cinnamon Cheerios     110.0    10.0  29.509541  0.090909
6                 Apple Jacks     110.0    14.0  33.174094  0.127273
7                     Basic 4     130.0     7.0  37.038562  0.053846
8                   Bran Chex      90.0     6.0  49.120253  0.066667
9                 Bran Flakes      90.0     5.0  53.313813  0.055556
20                        NaN       NaN     7.0        NaN       NaN
>>> df.sort_values(['calories', 'sugars']) # ordenem per un criteri múltiple
                         name  calories  sugars     rating     extra
3   All-Bran with Extra Fiber      50.0     0.0  93.704912  0.000000
9                 Bran Flakes      90.0     5.0  53.313813  0.055556
8                   Bran Chex      90.0     6.0  49.120253  0.066667
4              Almond Delight     110.0     8.0  34.384843  0.072727
5     Apple Cinnamon Cheerios     110.0    10.0  29.509541  0.090909
6                 Apple Jacks     110.0    14.0  33.174094  0.127273
7                     Basic 4     130.0     7.0  37.038562  0.053846
20                        NaN       NaN     7.0        NaN       NaN
>>> df.replace(110,100)  # substituir un valor; retorna un nou DataFrame
                         name  calories  sugars     rating     extra
3   All-Bran with Extra Fiber      50.0     0.0  93.704912  0.000000
4              Almond Delight     100.0     8.0  34.384843  0.072727
5     Apple Cinnamon Cheerios     100.0    10.0  29.509541  0.090909
6                 Apple Jacks     100.0    14.0  33.174094  0.127273
7                     Basic 4     130.0     7.0  37.038562  0.053846
8                   Bran Chex      90.0     6.0  49.120253  0.066667
9                 Bran Flakes      90.0     5.0  53.313813  0.055556
20                        NaN       NaN     7.0        NaN       NaN
>>> def f(x):
...    name, cals, sug, rat, extra = x
...    return str(name)[:7] + ' ' + str(sug+cals)
>>> df.apply(f, axis=1)  # aplica una funció a cada fila (o columna)
3      All-Bra 50.0
4     Almond  118.0
5     Apple C 120.0
6     Apple J 124.0
7     Basic 4 137.0
8      Bran Ch 96.0
9      Bran Fl 95.0
20          nan nan
dtype: object
>>> df.applymap(lambda x: len(str(x)))  # aplica una funció a cada cel·la
    name  calories  sugars  rating  extra
3     25         4       3       9      3
4     14         5       3       9     19
5     23         5       4       9     19
6     11         5       4       9     19
7      7         5       3       9     19
8      9         4       3       9     19
9     11         4       3       9     19
20     3         3       3       3      3
>>> df['calories'].unique()   # obté els valors que conté una Series
array([ 50., 110., 130.,  90.,  nan])

Treballar amb dades agrupades

_images/groupby.svg
  • Cal fer servir el mètode groupby(), que retorna un un DataFrameGroupBy (un iterable de DataFrame). No es pot examinar directament.

  • Les funcions de resum i selecció es poden aplicar a tot un grup. S’aplica la funció a cada DataFrame del grup. El resultat és una Series indexada segons els valors de cada grup, excepte que si la funció aplicada retorna una Series, que serà un DataFrame.


>>> df.loc[20,'calories'] = 50.0
>>> g = df.groupby('calories')  # agrupem segons el valor de les calories
>>> g.groups   # retorna un diccionari de (valor del grup, llista d'indexs del DataFrame)
{50.0: [3, 20], 90.0: [8, 9], 110.0: [4, 5, 6], 130.0: [7]}
>>> g.get_group(110)   # retorna un DataFrame
                      name  calories  sugars     rating     extra
4           Almond Delight     110.0     8.0  34.384843  0.072727
5  Apple Cinnamon Cheerios     110.0    10.0  29.509541  0.090909
6              Apple Jacks     110.0    14.0  33.174094  0.127273
>>> g.max()
          sugars     rating     extra
calories                             
50.0         7.0  93.704912  0.000000
90.0         6.0  53.313813  0.066667
110.0       14.0  34.384843  0.127273
130.0        7.0  37.038562  0.053846
>>> g['sugars'].mean()  # mitjana de sucres de cada grup de calories
calories
50.0      3.500000
90.0      5.500000
110.0    10.666667
130.0     7.000000
Name: sugars, dtype: float64
>>> g.size()  # nombre d'elements a cada grup
calories
50.0     2
90.0     2
110.0    3
130.0    1
dtype: int64
>>> g2 = df.groupby((df['rating'] // 20)*20)  # agrupem segons un criteri
>>> g2.groups
{20.0: [4, 5, 6, 7], 40.0: [8, 9], 80.0: [3]}
>>> g2['sugars'].apply(lambda x: (x.idxmin(), x.min()))
rating
20.0    (7, 7.0)
40.0    (9, 5.0)
80.0    (3, 0.0)
Name: sugars, dtype: object