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)¶
Podem assignar-los posant
pd.NA
,None
onp.nan
a una o més cel·lesEls mètodes i funcions de Pandas saben operar encara que hi hagi valors nuls
Vegeu també https://pandas.pydata.org/docs/user_guide/missing_data.html
>>> 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¶
Cal fer servir el mètode
groupby()
, que retorna un unDataFrameGroupBy
(un iterable deDataFrame
). 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 unaSeries
, que serà unDataFrame
.
>>> 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