En python : Pandas etc.
Module de première année de BUT informatique, Reims. TP de statistiques descriptives.
L’analyse de données contemporaine traite généralement des données avec un nombre important de variables, et de grandes quantités d’individus.
La gestion de ces variables avec des structures de données basiques
devient vite compliquée. Le module pandas
fait figure de
référence quand il s’agit de traiter ces données tabulaires. Il étend
les structures de données de base de python
en ajoutant un
type Series
(pour représenter une variable) et un
type DataFrame
, qui est une sorte de dict
dont
les valeurs sont des Series
. L’usage des data
frames est indiqué lorsqu’on doit représenter un tableau de données
hétérogènes (c’est à dire dont les variables -en
colonnes- sont de natures différentes). Le module
seaborn
permet quant à lui de produire des graphiques
facilement à partir d’un data frame.
Attention : ce travail est à réaliser avec la prochaine version de jupyter au département https://iut-info.univ-reims.fr/notebook/
pandas
pandas
(doc de
ref.) inclut des outils d’import et d’export de données, d’accès, de
filtrage, et d’analyse de ces données.
La méthode read_csv
permet de lire un fichier
csv
et de créer le data frame correspondant. Le chemin vers
le fichier csv
peut être une URL.
import pandas
# Choisir ce jeu de données par défaut
= pandas.read_csv("https://raw.githubusercontent.com/lgreski/pokemonData/master/Pokemon.csv")
df # Choisir ce jeu de données si vous êtes expert-dresseur et savez ce que vous faites
# df = pandas.read_csv("./pokemon2.csv")
On peut ensuite “découvrir” le data frame avec :
df.head()
ID | Name | Form | Type1 | Type2 | Total | HP | Attack | Defense | Sp. Atk | Sp. Def | Speed | Generation | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 1 | Bulbasaur | Grass | Poison | 318 | 45 | 49 | 49 | 65 | 65 | 45 | 1 | |
1 | 2 | Ivysaur | Grass | Poison | 405 | 60 | 62 | 63 | 80 | 80 | 60 | 1 | |
2 | 3 | Venusaur | Grass | Poison | 525 | 80 | 82 | 83 | 100 | 100 | 80 | 1 | |
3 | 4 | Charmander | Fire | 309 | 39 | 52 | 43 | 60 | 50 | 65 | 1 | ||
4 | 5 | Charmeleon | Fire | 405 | 58 | 64 | 58 | 80 | 65 | 80 | 1 |
Il est important de vérifier que les types inférés conviennent :
df.dtypes
ID int64
Name object
Form object
Type1 object
Type2 object
Total int64
HP int64
Attack int64
Defense int64
Sp. Atk int64
Sp. Def int64
Speed int64
Generation int64
dtype: object
Les noms des variables :
df.columns
Index(['ID', 'Name', 'Form', 'Type1', 'Type2', 'Total', 'HP', 'Attack',
'Defense', 'Sp. Atk', 'Sp. Def', 'Speed', 'Generation'],
dtype='object')
Pour obtenir les dimensions du data frame :
print("Nombre de lignes : ", len(df))
print("Nombre de colonnes : ", len(df.columns))
Nombre de lignes : 1045
Nombre de colonnes : 13
On peut ensuite accéder à des colonnes ou des lignes complètes :
'Name'].head(10) df[
0 Bulbasaur
1 Ivysaur
2 Venusaur
3 Charmander
4 Charmeleon
5 Charizard
6 Squirtle
7 Wartortle
8 Blastoise
9 Caterpie
Name: Name, dtype: object
La transformer en liste :
'Name'].head(10).tolist() df[
['Bulbasaur',
'Ivysaur',
'Venusaur',
'Charmander',
'Charmeleon',
'Charizard',
'Squirtle',
'Wartortle',
'Blastoise',
'Caterpie']
Accès à une ligne :
12] df.loc[
ID 13
Name Weedle
Form
Type1 Bug
Type2 Poison
Total 195
HP 40
Attack 35
Defense 30
Sp. Atk 20
Sp. Def 20
Speed 50
Generation 1
Name: 12, dtype: object
Conversion en tuple :
tuple(df.loc[12])
(13, 'Weedle', ' ', 'Bug', 'Poison', 195, 40, 35, 30, 20, 20, 50, 1)
Ou en dict
:
dict(df. iloc[12])
{'ID': 13,
'Name': 'Weedle',
'Form': ' ',
'Type1': 'Bug',
'Type2': 'Poison',
'Total': 195,
'HP': 40,
'Attack': 35,
'Defense': 30,
'Sp. Atk': 20,
'Sp. Def': 20,
'Speed': 50,
'Generation': 1}
On peut aussi accéder directement à une valeur, grâce aux indices :
12, 0] df.iloc[
13
Ou avec les noms (clés) :
12, 'ID'] df.loc[
13
9, 13], ['Name', 'Attack']] df.loc[[
Name | Attack | |
---|---|---|
9 | Caterpie | 30 |
13 | Kakuna | 25 |
10:20,['Name', 'Attack']] df.loc[
Name | Attack | |
---|---|---|
10 | Metapod | 20 |
11 | Butterfree | 45 |
12 | Weedle | 35 |
13 | Kakuna | 25 |
14 | Beedrill | 90 |
15 | Pidgey | 45 |
16 | Pidgeotto | 60 |
17 | Pidgeot | 80 |
18 | Rattata | 56 |
19 | Raticate | 81 |
20 | Spearow | 60 |
On même effectuer des requêtes pour sélectionner des sous-parties du tableau :
'Attack']>160]['Name'].tolist() df[df[
['Deoxys',
'Rampardos',
'Kyurem',
'Mewtwo',
'Heracross',
'Tyranitar',
'Banette',
'Groudon',
'Rayquaza',
'Garchomp',
'Gallade',
'Kartana',
'Necrozma',
'Zacian',
'Calyrex']
Explications :
df['Attack']>160
retourne une Series
de
booléens indiquant, pour chaque élément de la colonne
Attack
, s’il est ou non supérieur à 160.'Attack']>160 df[
0 False
1 False
2 False
3 False
4 False
...
1040 False
1041 False
1042 False
1043 True
1044 False
Name: Attack, Length: 1045, dtype: bool
df[df['Attack']>160]
sélection la partie du tableau
correspondante'Attack']>160] df[df[
ID | Name | Form | Type1 | Type2 | Total | HP | Attack | Defense | Sp. Atk | Sp. Def | Speed | Generation | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
389 | 386 | Deoxys | Attack Forme | Psychic | 600 | 50 | 180 | 20 | 180 | 20 | 150 | 3 | |
414 | 409 | Rampardos | Rock | 495 | 97 | 165 | 60 | 65 | 50 | 58 | 4 | ||
666 | 646 | Kyurem | Black Kyurem | Dragon | Ice | 700 | 125 | 170 | 100 | 120 | 90 | 95 | 5 |
686 | 150 | Mewtwo | Mega Mewtwo X | Psychic | Fighting | 780 | 106 | 190 | 100 | 154 | 100 | 130 | 6 |
691 | 214 | Heracross | Mega Heracross | Bug | Fighting | 600 | 80 | 185 | 115 | 40 | 105 | 75 | 6 |
693 | 248 | Tyranitar | Mega Tyranitar | Rock | Dark | 700 | 100 | 164 | 150 | 95 | 120 | 71 | 6 |
706 | 354 | Banette | Mega Banette | Ghost | 555 | 64 | 165 | 75 | 93 | 83 | 75 | 6 | |
714 | 383 | Groudon | Primal Groudon | Ground | Fire | 770 | 100 | 180 | 160 | 150 | 90 | 90 | 6 |
715 | 384 | Rayquaza | Mega Rayquaza | Dragon | Flying | 780 | 105 | 180 | 100 | 180 | 100 | 115 | 6 |
717 | 445 | Garchomp | Mega Garchomp | Dragon | Ground | 700 | 108 | 170 | 115 | 120 | 95 | 92 | 6 |
720 | 475 | Gallade | Mega Gallade | Psychic | Fighting | 618 | 68 | 165 | 95 | 65 | 115 | 110 | 6 |
911 | 798 | Kartana | Grass | Steel | 570 | 59 | 181 | 131 | 59 | 31 | 109 | 7 | |
916 | 800 | Necrozma | Ultra Necrozma | Psychic | Dragon | 754 | 97 | 167 | 97 | 167 | 97 | 129 | 7 |
1028 | 888 | Zacian | Crowned Sword | Fairy | Steel | 720 | 92 | 170 | 115 | 80 | 115 | 148 | 8 |
1043 | 898 | Calyrex | Ice Rider | Psychic | Ice | 680 | 100 | 165 | 150 | 85 | 130 | 50 | 8 |
df[df['Attack']>160]['Name']
restriction de ce
sous-tableau à la colonne Name
'Attack']>160]['Name'] df[df[
389 Deoxys
414 Rampardos
666 Kyurem
686 Mewtwo
691 Heracross
693 Tyranitar
706 Banette
714 Groudon
715 Rayquaza
717 Garchomp
720 Gallade
911 Kartana
916 Necrozma
1028 Zacian
1043 Calyrex
Name: Name, dtype: object
On peut combiner (mais attention,
pandas
a ses propres opérateurs logiques,
et les parenthèses sont indispensables) :
= df[(df['Attack']>130) & (df['Defense']>140)]['Name']
selection
print("Les pokemons ayant plus 130 en attaque et plus de 140 en défense sont : ",
' '.join(selection.tolist()))
Les pokemons ayant plus 130 en attaque et plus de 140 en défense sont : Tyranitar Aggron Metagross Groudon Stakataka Melmetal Calyrex
On peut ajouter des variables à un data frame existant :
'diff_ad'] = [0] * len(df) df[
et aussi appliquer des fonctions à chaque élément d’une
Series
grâce à la méthode apply()
:
'diff_ad'] = (df['Attack']-df['Defense']).apply(abs) df[
Ici, la fonction abs
est appliquée à la
Series
obtenue par différence des Séries
Attack
et Defense
.
'diff_ad'].head(10) df[
0 0
1 1
2 1
3 9
4 6
5 6
6 17
7 17
8 17
9 5
Name: diff_ad, dtype: int64
df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 1045 entries, 0 to 1044
Data columns (total 14 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 ID 1045 non-null int64
1 Name 1045 non-null object
2 Form 1045 non-null object
3 Type1 1045 non-null object
4 Type2 1045 non-null object
5 Total 1045 non-null int64
6 HP 1045 non-null int64
7 Attack 1045 non-null int64
8 Defense 1045 non-null int64
9 Sp. Atk 1045 non-null int64
10 Sp. Def 1045 non-null int64
11 Speed 1045 non-null int64
12 Generation 1045 non-null int64
13 diff_ad 1045 non-null int64
dtypes: int64(10), object(4)
memory usage: 114.4+ KB
Exercice « MAIS POURQUOI ON A FAIT TOUT ÇA ? »
Les modules pandas
et numpy
incluent la
plupart des fonctions que nous avons impléentées lors des séquences
précédentes…
À l’aide la documentation et d’un peu de flair, retrouver les
fonctions/méthodes correspondant à celles des TP précédents. S’il en
manque, on pourra recourir au module statistics
.
La description rudimentaire des variables d’un data frame peut facilement s’automatiser. Il existe d’ailleurs des modules dédiés. Ces modules, utiles en première intention ne remplaceront hélas pas une étude approfondie « manuelle ».
Import du module pandas-profiling
:
import pandas_profiling
= pandas_profiling.ProfileReport(df)
report ='./rapport.html') report.to_file(output_file
Ouvrir rapport.html
avec votre
navigateur préféré…
Exercice PODIUMS
Écrire une fonction podiums()
qui détermine, pour chaque
variable parmi Total
, HP
, Attack
,
Defense
, Sp. Atk
, Sp. Def
et
Speed
, les trois meilleurs pokemons.
Exercice ÉVALUATION D’UN DECK
Écrire une fonction eval_dec(df, selec)
qui évalue un
sous-ensemble de pokemons (défini dans la liste selec
) du
data frame df
, en calculant :
et en déterminant le quantile de ce deck moyen, pour chacune de ces caractéristiques
Les bibliothèques suivantes permettent de produire des graphiques avancés, pour certains dynamiques et intéractifs.
Seaborn
est -nous l’avons vu- une surcouche à
matplotlib
, pour améliorer l’apparence visuelle des
graphiquesPlotly
est une bibliothèque de production graphique
‘web’Altair
est un module python
qui permet
d’exploiter la bibliothèque vega-lite
Dask
est un module python
qui constitue la
solution la plus simple pour créer des dashboards ‘web’Streamlit
permet de créer des interfaces web à la
R-Shiny
Grafana
est le célèbre outils de
monitoring/visualisation avec lequel on échange en json
;
des modules python
d’interfaçage avec grafana
existent