17. Pandas#
En plus de ce qui est inclus dans Anaconda, ce cours nécessitera les bibliothèques suivantes :
!pip install --upgrade wbgapi
!pip install --upgrade yfinance
17.1. Vue d’ensemble#
Pandas est un ensemble d’outils d’analyse de données rapides et efficaces pour Python.
Sa popularité a fortement augmenté ces dernières années, en parallèle de l’essor de domaines tels que la science des données et l’apprentissage automatique.
Voici une comparaison de popularité dans le temps face à Matlab et STATA, offerte par Stack Overflow Trends
Tout comme NumPy fournit le type de données tableau de base ainsi que les opérations fondamentales sur les tableaux, pandas
définit des structures fondamentales pour travailler avec les données et
les dote de méthodes qui facilitent des opérations telles que
la lecture de données
l’ajustement des indices
le travail avec les dates et les séries temporelles
le tri, le regroupement, la réorganisation et le traitement général des données [1]
la gestion des valeurs manquantes, etc., etc.
Des fonctionnalités statistiques plus sophistiquées sont laissées à d’autres paquets, tels que statsmodels et scikit-learn, qui sont construits par-dessus pandas.
Ce cours fournira une introduction de base à pandas.
Tout au long du cours, nous supposerons que les importations suivantes ont été effectuées
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import requests
Deux types de données importants définis par pandas sont Series et DataFrame.
Vous pouvez considérer une Series comme une « colonne » de données, telle qu’une collection d’observations sur une seule variable.
Un DataFrame est un objet à deux dimensions permettant de stocker des colonnes de données liées entre elles.
17.2. Series#
Commençons par les Series.
Nous commençons par créer une série de quatre observations aléatoires
s = pd.Series(np.random.randn(4), name='daily returns')
s
0 1.339102
1 1.228159
2 -0.311071
3 -1.324389
Name: daily returns, dtype: float64
Ici, vous pouvez imaginer les indices 0, 1, 2, 3 comme indexant quatre sociétés cotées, et les valeurs étant les rendements quotidiens de leurs actions.
Les Series de pandas sont construites par-dessus les tableaux NumPy et prennent en charge de nombreuses opérations similaires
s * 100
0 133.910218
1 122.815850
2 -31.107090
3 -132.438928
Name: daily returns, dtype: float64
np.abs(s)
0 1.339102
1 1.228159
2 0.311071
3 1.324389
Name: daily returns, dtype: float64
Mais les Series offrent plus que les tableaux NumPy.
Non seulement elles disposent de méthodes supplémentaires (à orientation statistique)
s.describe()
count 4.000000
mean 0.232950
std 1.282612
min -1.324389
25% -0.564400
50% 0.458544
75% 1.255894
max 1.339102
Name: daily returns, dtype: float64
mais leurs indices sont plus flexibles
s.index = ['AMZN', 'AAPL', 'MSFT', 'GOOG']
s
AMZN 1.339102
AAPL 1.228159
MSFT -0.311071
GOOG -1.324389
Name: daily returns, dtype: float64
Vues de cette manière, les Series sont comme des dictionnaires Python rapides et efficaces (avec la restriction que tous les éléments du dictionnaire ont le même type — dans ce cas, des flottants).
En fait, vous pouvez utiliser une grande partie de la même syntaxe que les dictionnaires Python
s['AMZN']
np.float64(1.339102177625793)
s['AMZN'] = 0
s
AMZN 0.000000
AAPL 1.228159
MSFT -0.311071
GOOG -1.324389
Name: daily returns, dtype: float64
'AAPL' in s
True
17.3. DataFrames#
Alors qu’une Series est une seule colonne de données, un DataFrame comporte plusieurs colonnes, une pour chaque variable.
En substance, un DataFrame dans pandas est analogue à une feuille de calcul Excel (hautement optimisée).
Ainsi, c’est un outil puissant pour représenter et analyser des données naturellement organisées en lignes et en colonnes, souvent avec des indices descriptifs pour les lignes et les colonnes individuelles.
Regardons un exemple qui lit des données à partir du fichier CSV pandas/data/test_pwt.csv, tiré des Penn World Tables.
Le jeu de données contient les indicateurs suivants
| Nom de la variable | Description |
| :- : | :- : |
| POP | Population (en milliers) |
| XRAT | Taux de change par rapport au dollar américain |
| tcgdp | PIB total converti en PPA (en millions de dollars internationaux) |
| cc | Part de la consommation dans le PIB par habitant converti en PPA (%) |
| cg | Part de la consommation publique dans le PIB par habitant converti en PPA (%) |
Nous allons le lire depuis une URL en utilisant la fonction read_csv de pandas.
df = pd.read_csv('https://raw.githubusercontent.com/QuantEcon/lecture-python-programming/main/lectures/_static/lecture_specific/pandas/data/test_pwt.csv')
type(df)
pandas.DataFrame
Voici le contenu de test_pwt.csv
df
| country | country isocode | year | POP | XRAT | tcgdp | cc | cg | |
|---|---|---|---|---|---|---|---|---|
| 0 | Argentina | ARG | 2000 | 37335.653 | 0.999500 | 2.950722e+05 | 75.716805 | 5.578804 |
| 1 | Australia | AUS | 2000 | 19053.186 | 1.724830 | 5.418047e+05 | 67.759026 | 6.720098 |
| 2 | India | IND | 2000 | 1006300.297 | 44.941600 | 1.728144e+06 | 64.575551 | 14.072206 |
| 3 | Israel | ISR | 2000 | 6114.570 | 4.077330 | 1.292539e+05 | 64.436451 | 10.266688 |
| 4 | Malawi | MWI | 2000 | 11801.505 | 59.543808 | 5.026222e+03 | 74.707624 | 11.658954 |
| 5 | South Africa | ZAF | 2000 | 45064.098 | 6.939830 | 2.272424e+05 | 72.718710 | 5.726546 |
| 6 | United States | USA | 2000 | 282171.957 | 1.000000 | 9.898700e+06 | 72.347054 | 6.032454 |
| 7 | Uruguay | URY | 2000 | 3219.793 | 12.099592 | 2.525596e+04 | 78.978740 | 5.108068 |
17.3.1. Sélectionner les données par position#
En pratique, une chose que nous faisons tout le temps est de rechercher, sélectionner et travailler avec un sous-ensemble des données qui nous intéressent.
Nous pouvons sélectionner des lignes particulières en utilisant la notation standard de découpage de tableau Python
df[2:5]
| country | country isocode | year | POP | XRAT | tcgdp | cc | cg | |
|---|---|---|---|---|---|---|---|---|
| 2 | India | IND | 2000 | 1006300.297 | 44.941600 | 1.728144e+06 | 64.575551 | 14.072206 |
| 3 | Israel | ISR | 2000 | 6114.570 | 4.077330 | 1.292539e+05 | 64.436451 | 10.266688 |
| 4 | Malawi | MWI | 2000 | 11801.505 | 59.543808 | 5.026222e+03 | 74.707624 | 11.658954 |
Pour sélectionner des colonnes, nous pouvons passer une liste contenant les noms des colonnes désirées représentés sous forme de chaînes de caractères
df[['country', 'tcgdp']]
| country | tcgdp | |
|---|---|---|
| 0 | Argentina | 2.950722e+05 |
| 1 | Australia | 5.418047e+05 |
| 2 | India | 1.728144e+06 |
| 3 | Israel | 1.292539e+05 |
| 4 | Malawi | 5.026222e+03 |
| 5 | South Africa | 2.272424e+05 |
| 6 | United States | 9.898700e+06 |
| 7 | Uruguay | 2.525596e+04 |
Pour sélectionner à la fois des lignes et des colonnes en utilisant des entiers, l’attribut iloc doit être utilisé avec le format .iloc[lignes, colonnes].
df.iloc[2:5, 0:4]
| country | country isocode | year | POP | |
|---|---|---|---|---|
| 2 | India | IND | 2000 | 1006300.297 |
| 3 | Israel | ISR | 2000 | 6114.570 |
| 4 | Malawi | MWI | 2000 | 11801.505 |
Pour sélectionner des lignes et des colonnes en utilisant un mélange d’entiers et d’étiquettes, l’attribut loc peut être utilisé de manière similaire
df.loc[df.index[2:5], ['country', 'tcgdp']]
| country | tcgdp | |
|---|---|---|
| 2 | India | 1.728144e+06 |
| 3 | Israel | 1.292539e+05 |
| 4 | Malawi | 5.026222e+03 |
17.3.2. Sélectionner les données par conditions#
Au lieu d’indexer les lignes et les colonnes en utilisant des entiers et des noms, nous pouvons également obtenir un sous-dataframe qui nous intéresse et qui satisfait certaines conditions (potentiellement complexes).
Cette section illustre différentes manières de procéder.
La manière la plus directe est d’utiliser l’opérateur [].
df[df.POP >= 20000]
| country | country isocode | year | POP | XRAT | tcgdp | cc | cg | |
|---|---|---|---|---|---|---|---|---|
| 0 | Argentina | ARG | 2000 | 37335.653 | 0.99950 | 2.950722e+05 | 75.716805 | 5.578804 |
| 2 | India | IND | 2000 | 1006300.297 | 44.94160 | 1.728144e+06 | 64.575551 | 14.072206 |
| 5 | South Africa | ZAF | 2000 | 45064.098 | 6.93983 | 2.272424e+05 | 72.718710 | 5.726546 |
| 6 | United States | USA | 2000 | 282171.957 | 1.00000 | 9.898700e+06 | 72.347054 | 6.032454 |
Pour comprendre ce qui se passe ici, remarquez que df.POP >= 20000 renvoie une série de valeurs booléennes.
df.POP >= 20000
0 True
1 False
2 True
3 False
4 False
5 True
6 True
7 False
Name: POP, dtype: bool
Dans ce cas, df[___] prend une série de valeurs booléennes et ne renvoie que les lignes ayant les valeurs True.
Prenons un autre exemple,
df[(df.country.isin(['Argentina', 'India', 'South Africa'])) & (df.POP > 40000)]
| country | country isocode | year | POP | XRAT | tcgdp | cc | cg | |
|---|---|---|---|---|---|---|---|---|
| 2 | India | IND | 2000 | 1006300.297 | 44.94160 | 1.728144e+06 | 64.575551 | 14.072206 |
| 5 | South Africa | ZAF | 2000 | 45064.098 | 6.93983 | 2.272424e+05 | 72.718710 | 5.726546 |
Cependant, il existe une autre manière de faire la même chose, qui peut être légèrement plus rapide pour les grands dataframes, avec une syntaxe plus naturelle.
# ce qui précède est équivalent à
df.query("POP >= 20000")
| country | country isocode | year | POP | XRAT | tcgdp | cc | cg | |
|---|---|---|---|---|---|---|---|---|
| 0 | Argentina | ARG | 2000 | 37335.653 | 0.99950 | 2.950722e+05 | 75.716805 | 5.578804 |
| 2 | India | IND | 2000 | 1006300.297 | 44.94160 | 1.728144e+06 | 64.575551 | 14.072206 |
| 5 | South Africa | ZAF | 2000 | 45064.098 | 6.93983 | 2.272424e+05 | 72.718710 | 5.726546 |
| 6 | United States | USA | 2000 | 282171.957 | 1.00000 | 9.898700e+06 | 72.347054 | 6.032454 |
df.query("country in ['Argentina', 'India', 'South Africa'] and POP > 40000")
| country | country isocode | year | POP | XRAT | tcgdp | cc | cg | |
|---|---|---|---|---|---|---|---|---|
| 2 | India | IND | 2000 | 1006300.297 | 44.94160 | 1.728144e+06 | 64.575551 | 14.072206 |
| 5 | South Africa | ZAF | 2000 | 45064.098 | 6.93983 | 2.272424e+05 | 72.718710 | 5.726546 |
Nous pouvons également autoriser des opérations arithmétiques entre différentes colonnes.
df[(df.cc + df.cg >= 80) & (df.POP <= 20000)]
| country | country isocode | year | POP | XRAT | tcgdp | cc | cg | |
|---|---|---|---|---|---|---|---|---|
| 4 | Malawi | MWI | 2000 | 11801.505 | 59.543808 | 5026.221784 | 74.707624 | 11.658954 |
| 7 | Uruguay | URY | 2000 | 3219.793 | 12.099592 | 25255.961693 | 78.978740 | 5.108068 |
# ce qui précède est équivalent à
df.query("cc + cg >= 80 & POP <= 20000")
| country | country isocode | year | POP | XRAT | tcgdp | cc | cg | |
|---|---|---|---|---|---|---|---|---|
| 4 | Malawi | MWI | 2000 | 11801.505 | 59.543808 | 5026.221784 | 74.707624 | 11.658954 |
| 7 | Uruguay | URY | 2000 | 3219.793 | 12.099592 | 25255.961693 | 78.978740 | 5.108068 |
Par exemple, nous pouvons utiliser le conditionnement pour sélectionner le pays ayant la plus grande part de consommation des ménages dans le PIB cc.
df.loc[df.cc == max(df.cc)]
| country | country isocode | year | POP | XRAT | tcgdp | cc | cg | |
|---|---|---|---|---|---|---|---|---|
| 7 | Uruguay | URY | 2000 | 3219.793 | 12.099592 | 25255.961693 | 78.97874 | 5.108068 |
Lorsque nous ne voulons regarder que certaines colonnes d’un sous-dataframe sélectionné, nous pouvons utiliser les conditions ci-dessus avec la commande .loc[__ , __].
Le premier argument prend la condition, tandis que le second argument prend une liste de colonnes que nous voulons renvoyer.
df.loc[(df.cc + df.cg >= 80) & (df.POP <= 20000), ['country', 'year', 'POP']]
| country | year | POP | |
|---|---|---|---|
| 4 | Malawi | 2000 | 11801.505 |
| 7 | Uruguay | 2000 | 3219.793 |
Application : Sous-ensemble d’un Dataframe
Les jeux de données du monde réel peuvent être énormes.
Il est parfois souhaitable de travailler avec un sous-ensemble de données pour améliorer l’efficacité computationnelle et réduire la redondance.
Imaginons que nous ne nous intéressions qu’à la population (POP) et au PIB total (tcgdp).
Une manière de réduire le dataframe df à ces seules variables est d’écraser le dataframe en utilisant la méthode de sélection décrite ci-dessus
df_subset = df[['country', 'POP', 'tcgdp']]
df_subset
| country | POP | tcgdp | |
|---|---|---|---|
| 0 | Argentina | 37335.653 | 2.950722e+05 |
| 1 | Australia | 19053.186 | 5.418047e+05 |
| 2 | India | 1006300.297 | 1.728144e+06 |
| 3 | Israel | 6114.570 | 1.292539e+05 |
| 4 | Malawi | 11801.505 | 5.026222e+03 |
| 5 | South Africa | 45064.098 | 2.272424e+05 |
| 6 | United States | 282171.957 | 9.898700e+06 |
| 7 | Uruguay | 3219.793 | 2.525596e+04 |
Nous pouvons ensuite enregistrer le jeu de données plus petit pour une analyse ultérieure.
df_subset.to_csv('pwt_subset.csv', index=False)
17.3.3. Méthode Apply#
Une autre méthode Pandas largement utilisée est df.apply().
Elle applique une fonction à chaque ligne/colonne et renvoie une série.
Cette fonction peut être une fonction intégrée comme la fonction max, une fonction lambda, ou une fonction définie par l’utilisateur.
Voici un exemple utilisant la fonction max
df[['year', 'POP', 'XRAT', 'tcgdp', 'cc', 'cg']].apply(max)
year 2.000000e+03
POP 1.006300e+06
XRAT 5.954381e+01
tcgdp 9.898700e+06
cc 7.897874e+01
cg 1.407221e+01
dtype: float64
Cette ligne de code applique la fonction max à toutes les colonnes sélectionnées.
La fonction lambda est souvent utilisée avec la méthode df.apply()
Un exemple trivial consiste à renvoyer elle-même chaque ligne du dataframe
df.apply(lambda row: row, axis=1)
| country | country isocode | year | POP | XRAT | tcgdp | cc | cg | |
|---|---|---|---|---|---|---|---|---|
| 0 | Argentina | ARG | 2000 | 37335.653 | 0.999500 | 2.950722e+05 | 75.716805 | 5.578804 |
| 1 | Australia | AUS | 2000 | 19053.186 | 1.724830 | 5.418047e+05 | 67.759026 | 6.720098 |
| 2 | India | IND | 2000 | 1006300.297 | 44.941600 | 1.728144e+06 | 64.575551 | 14.072206 |
| 3 | Israel | ISR | 2000 | 6114.570 | 4.077330 | 1.292539e+05 | 64.436451 | 10.266688 |
| 4 | Malawi | MWI | 2000 | 11801.505 | 59.543808 | 5.026222e+03 | 74.707624 | 11.658954 |
| 5 | South Africa | ZAF | 2000 | 45064.098 | 6.939830 | 2.272424e+05 | 72.718710 | 5.726546 |
| 6 | United States | USA | 2000 | 282171.957 | 1.000000 | 9.898700e+06 | 72.347054 | 6.032454 |
| 7 | Uruguay | URY | 2000 | 3219.793 | 12.099592 | 2.525596e+04 | 78.978740 | 5.108068 |
Note
Pour la méthode .apply()
axis = 0 – applique la fonction à chaque colonne (variables)
axis = 1 – applique la fonction à chaque ligne (observations)
axis = 0 est le paramètre par défaut
Nous pouvons l’utiliser conjointement avec .loc[] pour effectuer des sélections plus avancées.
complexCondition = df.apply(
lambda row: row.POP > 40000 if row.country in ['Argentina', 'India', 'South Africa'] else row.POP < 20000,
axis=1), ['country', 'year', 'POP', 'XRAT', 'tcgdp']
df.apply() renvoie ici une série de valeurs booléennes pour les lignes qui satisfont la condition spécifiée dans l’instruction if-else.
De plus, elle définit également un sous-ensemble de variables d’intérêt.
complexCondition
(0 False
1 True
2 True
3 True
4 True
5 True
6 False
7 True
dtype: bool,
['country', 'year', 'POP', 'XRAT', 'tcgdp'])
Lorsque nous appliquons cette condition au dataframe, le résultat sera
df.loc[complexCondition]
| country | year | POP | XRAT | tcgdp | |
|---|---|---|---|---|---|
| 1 | Australia | 2000 | 19053.186 | 1.724830 | 5.418047e+05 |
| 2 | India | 2000 | 1006300.297 | 44.941600 | 1.728144e+06 |
| 3 | Israel | 2000 | 6114.570 | 4.077330 | 1.292539e+05 |
| 4 | Malawi | 2000 | 11801.505 | 59.543808 | 5.026222e+03 |
| 5 | South Africa | 2000 | 45064.098 | 6.939830 | 2.272424e+05 |
| 7 | Uruguay | 2000 | 3219.793 | 12.099592 | 2.525596e+04 |
17.3.4. Effectuer des modifications dans les DataFrames#
La capacité d’effectuer des modifications dans les dataframes est importante pour générer un jeu de données propre en vue d’analyses futures.
1. Nous pouvons utiliser df.where() de manière pratique pour « conserver » les lignes que nous avons sélectionnées et remplacer les autres lignes par n’importe quelles autres valeurs
df.where(df.POP >= 20000, False)
| country | country isocode | year | POP | XRAT | tcgdp | cc | cg | |
|---|---|---|---|---|---|---|---|---|
| 0 | Argentina | ARG | 2000 | 37335.653 | 0.9995 | 295072.21869 | 75.716805 | 5.578804 |
| 1 | False | False | False | False | False | False | False | False |
| 2 | India | IND | 2000 | 1006300.297 | 44.9416 | 1728144.3748 | 64.575551 | 14.072206 |
| 3 | False | False | False | False | False | False | False | False |
| 4 | False | False | False | False | False | False | False | False |
| 5 | South Africa | ZAF | 2000 | 45064.098 | 6.93983 | 227242.36949 | 72.71871 | 5.726546 |
| 6 | United States | USA | 2000 | 282171.957 | 1.0 | 9898700.0 | 72.347054 | 6.032454 |
| 7 | False | False | False | False | False | False | False | False |
2. Nous pouvons simplement utiliser .loc[] pour spécifier la colonne que nous voulons modifier, et attribuer des valeurs
df.loc[df.cg == max(df.cg), 'cg'] = np.nan
df
| country | country isocode | year | POP | XRAT | tcgdp | cc | cg | |
|---|---|---|---|---|---|---|---|---|
| 0 | Argentina | ARG | 2000 | 37335.653 | 0.999500 | 2.950722e+05 | 75.716805 | 5.578804 |
| 1 | Australia | AUS | 2000 | 19053.186 | 1.724830 | 5.418047e+05 | 67.759026 | 6.720098 |
| 2 | India | IND | 2000 | 1006300.297 | 44.941600 | 1.728144e+06 | 64.575551 | NaN |
| 3 | Israel | ISR | 2000 | 6114.570 | 4.077330 | 1.292539e+05 | 64.436451 | 10.266688 |
| 4 | Malawi | MWI | 2000 | 11801.505 | 59.543808 | 5.026222e+03 | 74.707624 | 11.658954 |
| 5 | South Africa | ZAF | 2000 | 45064.098 | 6.939830 | 2.272424e+05 | 72.718710 | 5.726546 |
| 6 | United States | USA | 2000 | 282171.957 | 1.000000 | 9.898700e+06 | 72.347054 | 6.032454 |
| 7 | Uruguay | URY | 2000 | 3219.793 | 12.099592 | 2.525596e+04 | 78.978740 | 5.108068 |
3. Nous pouvons utiliser la méthode .apply() pour modifier des lignes/colonnes dans leur ensemble
def update_row(row):
# modifie POP
row.POP = np.nan if row.POP<= 10000 else row.POP
# modifie XRAT
row.XRAT = row.XRAT / 10
return row
df.apply(update_row, axis=1)
| country | country isocode | year | POP | XRAT | tcgdp | cc | cg | |
|---|---|---|---|---|---|---|---|---|
| 0 | Argentina | ARG | 2000 | 37335.653 | 0.099950 | 2.950722e+05 | 75.716805 | 5.578804 |
| 1 | Australia | AUS | 2000 | 19053.186 | 0.172483 | 5.418047e+05 | 67.759026 | 6.720098 |
| 2 | India | IND | 2000 | 1006300.297 | 4.494160 | 1.728144e+06 | 64.575551 | NaN |
| 3 | Israel | ISR | 2000 | NaN | 0.407733 | 1.292539e+05 | 64.436451 | 10.266688 |
| 4 | Malawi | MWI | 2000 | 11801.505 | 5.954381 | 5.026222e+03 | 74.707624 | 11.658954 |
| 5 | South Africa | ZAF | 2000 | 45064.098 | 0.693983 | 2.272424e+05 | 72.718710 | 5.726546 |
| 6 | United States | USA | 2000 | 282171.957 | 0.100000 | 9.898700e+06 | 72.347054 | 6.032454 |
| 7 | Uruguay | URY | 2000 | NaN | 1.209959 | 2.525596e+04 | 78.978740 | 5.108068 |
4. Nous pouvons utiliser la méthode .map() pour modifier toutes les entrées individuelles du dataframe d’un seul coup.
# Arrondit tous les nombres décimaux à 2 décimales
df.map(lambda x : round(x,2) if type(x)!=str else x)
| country | country isocode | year | POP | XRAT | tcgdp | cc | cg | |
|---|---|---|---|---|---|---|---|---|
| 0 | Argentina | ARG | 2000 | 37335.65 | 1.00 | 295072.22 | 75.72 | 5.58 |
| 1 | Australia | AUS | 2000 | 19053.19 | 1.72 | 541804.65 | 67.76 | 6.72 |
| 2 | India | IND | 2000 | 1006300.30 | 44.94 | 1728144.37 | 64.58 | NaN |
| 3 | Israel | ISR | 2000 | 6114.57 | 4.08 | 129253.89 | 64.44 | 10.27 |
| 4 | Malawi | MWI | 2000 | 11801.50 | 59.54 | 5026.22 | 74.71 | 11.66 |
| 5 | South Africa | ZAF | 2000 | 45064.10 | 6.94 | 227242.37 | 72.72 | 5.73 |
| 6 | United States | USA | 2000 | 282171.96 | 1.00 | 9898700.00 | 72.35 | 6.03 |
| 7 | Uruguay | URY | 2000 | 3219.79 | 12.10 | 25255.96 | 78.98 | 5.11 |
Application : Imputation des valeurs manquantes
Le remplacement des valeurs manquantes est une étape importante du traitement des données.
Insérons aléatoirement quelques valeurs NaN
for idx in list(zip([0, 3, 5, 6], [3, 4, 6, 2])):
df.iloc[idx] = np.nan
df
| country | country isocode | year | POP | XRAT | tcgdp | cc | cg | |
|---|---|---|---|---|---|---|---|---|
| 0 | Argentina | ARG | 2000.0 | NaN | 0.999500 | 2.950722e+05 | 75.716805 | 5.578804 |
| 1 | Australia | AUS | 2000.0 | 19053.186 | 1.724830 | 5.418047e+05 | 67.759026 | 6.720098 |
| 2 | India | IND | 2000.0 | 1006300.297 | 44.941600 | 1.728144e+06 | 64.575551 | NaN |
| 3 | Israel | ISR | 2000.0 | 6114.570 | NaN | 1.292539e+05 | 64.436451 | 10.266688 |
| 4 | Malawi | MWI | 2000.0 | 11801.505 | 59.543808 | 5.026222e+03 | 74.707624 | 11.658954 |
| 5 | South Africa | ZAF | 2000.0 | 45064.098 | 6.939830 | 2.272424e+05 | NaN | 5.726546 |
| 6 | United States | USA | NaN | 282171.957 | 1.000000 | 9.898700e+06 | 72.347054 | 6.032454 |
| 7 | Uruguay | URY | 2000.0 | 3219.793 | 12.099592 | 2.525596e+04 | 78.978740 | 5.108068 |
La fonction zip() crée ici des paires de valeurs à partir des deux listes (c’est-à-dire [0,3], [3,4] …)
Nous pouvons utiliser à nouveau la méthode .map() pour remplacer toutes les valeurs manquantes par 0
# remplace toutes les valeurs NaN par 0
def replace_nan(x):
if type(x)!=str:
return 0 if np.isnan(x) else x
else:
return x
df.map(replace_nan)
| country | country isocode | year | POP | XRAT | tcgdp | cc | cg | |
|---|---|---|---|---|---|---|---|---|
| 0 | Argentina | ARG | 2000.0 | 0.000 | 0.999500 | 2.950722e+05 | 75.716805 | 5.578804 |
| 1 | Australia | AUS | 2000.0 | 19053.186 | 1.724830 | 5.418047e+05 | 67.759026 | 6.720098 |
| 2 | India | IND | 2000.0 | 1006300.297 | 44.941600 | 1.728144e+06 | 64.575551 | 0.000000 |
| 3 | Israel | ISR | 2000.0 | 6114.570 | 0.000000 | 1.292539e+05 | 64.436451 | 10.266688 |
| 4 | Malawi | MWI | 2000.0 | 11801.505 | 59.543808 | 5.026222e+03 | 74.707624 | 11.658954 |
| 5 | South Africa | ZAF | 2000.0 | 45064.098 | 6.939830 | 2.272424e+05 | 0.000000 | 5.726546 |
| 6 | United States | USA | 0.0 | 282171.957 | 1.000000 | 9.898700e+06 | 72.347054 | 6.032454 |
| 7 | Uruguay | URY | 2000.0 | 3219.793 | 12.099592 | 2.525596e+04 | 78.978740 | 5.108068 |
Pandas nous fournit également des méthodes pratiques pour remplacer les valeurs manquantes.
Par exemple, l’imputation simple utilisant les moyennes des variables peut être facilement réalisée dans pandas
df = df.fillna(df.iloc[:,2:8].mean())
df
| country | country isocode | year | POP | XRAT | tcgdp | cc | cg | |
|---|---|---|---|---|---|---|---|---|
| 0 | Argentina | ARG | 2000.0 | 1.962465e+05 | 0.999500 | 2.950722e+05 | 75.716805 | 5.578804 |
| 1 | Australia | AUS | 2000.0 | 1.905319e+04 | 1.724830 | 5.418047e+05 | 67.759026 | 6.720098 |
| 2 | India | IND | 2000.0 | 1.006300e+06 | 44.941600 | 1.728144e+06 | 64.575551 | 7.298802 |
| 3 | Israel | ISR | 2000.0 | 6.114570e+03 | 18.178451 | 1.292539e+05 | 64.436451 | 10.266688 |
| 4 | Malawi | MWI | 2000.0 | 1.180150e+04 | 59.543808 | 5.026222e+03 | 74.707624 | 11.658954 |
| 5 | South Africa | ZAF | 2000.0 | 4.506410e+04 | 6.939830 | 2.272424e+05 | 71.217322 | 5.726546 |
| 6 | United States | USA | 2000.0 | 2.821720e+05 | 1.000000 | 9.898700e+06 | 72.347054 | 6.032454 |
| 7 | Uruguay | URY | 2000.0 | 3.219793e+03 | 12.099592 | 2.525596e+04 | 78.978740 | 5.108068 |
L’imputation des valeurs manquantes est un vaste domaine de la science des données impliquant diverses techniques d’apprentissage automatique.
Il existe également des outils plus avancés en Python pour imputer les valeurs manquantes.
17.3.5. Standardisation et visualisation#
Imaginons que nous ne nous intéressions qu’à la population (POP) et au PIB total (tcgdp).
Une manière de réduire le dataframe df à ces seules variables est d’écraser le dataframe en utilisant la méthode de sélection décrite ci-dessus
df = df[['country', 'POP', 'tcgdp']]
df
| country | POP | tcgdp | |
|---|---|---|---|
| 0 | Argentina | 1.962465e+05 | 2.950722e+05 |
| 1 | Australia | 1.905319e+04 | 5.418047e+05 |
| 2 | India | 1.006300e+06 | 1.728144e+06 |
| 3 | Israel | 6.114570e+03 | 1.292539e+05 |
| 4 | Malawi | 1.180150e+04 | 5.026222e+03 |
| 5 | South Africa | 4.506410e+04 | 2.272424e+05 |
| 6 | United States | 2.821720e+05 | 9.898700e+06 |
| 7 | Uruguay | 3.219793e+03 | 2.525596e+04 |
Ici, l’indice 0, 1,..., 7 est redondant car nous pouvons utiliser les noms des pays comme indice.
Pour ce faire, nous définissons l’indice comme étant la variable country du dataframe
df = df.set_index('country')
df
| POP | tcgdp | |
|---|---|---|
| country | ||
| Argentina | 1.962465e+05 | 2.950722e+05 |
| Australia | 1.905319e+04 | 5.418047e+05 |
| India | 1.006300e+06 | 1.728144e+06 |
| Israel | 6.114570e+03 | 1.292539e+05 |
| Malawi | 1.180150e+04 | 5.026222e+03 |
| South Africa | 4.506410e+04 | 2.272424e+05 |
| United States | 2.821720e+05 | 9.898700e+06 |
| Uruguay | 3.219793e+03 | 2.525596e+04 |
Donnons aux colonnes des noms légèrement meilleurs
df.columns = 'population', 'total GDP'
df
| population | total GDP | |
|---|---|---|
| country | ||
| Argentina | 1.962465e+05 | 2.950722e+05 |
| Australia | 1.905319e+04 | 5.418047e+05 |
| India | 1.006300e+06 | 1.728144e+06 |
| Israel | 6.114570e+03 | 1.292539e+05 |
| Malawi | 1.180150e+04 | 5.026222e+03 |
| South Africa | 4.506410e+04 | 2.272424e+05 |
| United States | 2.821720e+05 | 9.898700e+06 |
| Uruguay | 3.219793e+03 | 2.525596e+04 |
La variable population est en milliers, revenons aux unités simples
df['population'] = df['population'] * 1e3
df
| population | total GDP | |
|---|---|---|
| country | ||
| Argentina | 1.962465e+08 | 2.950722e+05 |
| Australia | 1.905319e+07 | 5.418047e+05 |
| India | 1.006300e+09 | 1.728144e+06 |
| Israel | 6.114570e+06 | 1.292539e+05 |
| Malawi | 1.180150e+07 | 5.026222e+03 |
| South Africa | 4.506410e+07 | 2.272424e+05 |
| United States | 2.821720e+08 | 9.898700e+06 |
| Uruguay | 3.219793e+06 | 2.525596e+04 |
Ensuite, nous allons ajouter une colonne montrant le PIB réel par habitant, en multipliant par 1 000 000 au passage car le PIB total est en millions
df['GDP percap'] = df['total GDP'] * 1e6 / df['population']
df
| population | total GDP | GDP percap | |
|---|---|---|---|
| country | |||
| Argentina | 1.962465e+08 | 2.950722e+05 | 1503.579625 |
| Australia | 1.905319e+07 | 5.418047e+05 | 28436.433261 |
| India | 1.006300e+09 | 1.728144e+06 | 1717.324719 |
| Israel | 6.114570e+06 | 1.292539e+05 | 21138.672749 |
| Malawi | 1.180150e+07 | 5.026222e+03 | 425.896679 |
| South Africa | 4.506410e+07 | 2.272424e+05 | 5042.647686 |
| United States | 2.821720e+08 | 9.898700e+06 | 35080.381854 |
| Uruguay | 3.219793e+06 | 2.525596e+04 | 7843.970620 |
L’une des choses agréables avec les objets DataFrame et Series de pandas est qu’ils disposent de méthodes de tracé et de visualisation qui fonctionnent via Matplotlib.
Par exemple, nous pouvons facilement générer un diagramme en barres du PIB par habitant
ax = df['GDP percap'].plot(kind='bar')
ax.set_xlabel('country', fontsize=12)
ax.set_ylabel('GDP per capita', fontsize=12)
plt.show()
Pour le moment, le dataframe est ordonné par ordre alphabétique des pays — modifions-le pour l’ordonner selon le PIB par habitant
df = df.sort_values(by='GDP percap', ascending=False)
df
| population | total GDP | GDP percap | |
|---|---|---|---|
| country | |||
| United States | 2.821720e+08 | 9.898700e+06 | 35080.381854 |
| Australia | 1.905319e+07 | 5.418047e+05 | 28436.433261 |
| Israel | 6.114570e+06 | 1.292539e+05 | 21138.672749 |
| Uruguay | 3.219793e+06 | 2.525596e+04 | 7843.970620 |
| South Africa | 4.506410e+07 | 2.272424e+05 | 5042.647686 |
| India | 1.006300e+09 | 1.728144e+06 | 1717.324719 |
| Argentina | 1.962465e+08 | 2.950722e+05 | 1503.579625 |
| Malawi | 1.180150e+07 | 5.026222e+03 | 425.896679 |
Un tracé comme précédemment donne maintenant
ax = df['GDP percap'].plot(kind='bar')
ax.set_xlabel('country', fontsize=12)
ax.set_ylabel('GDP per capita', fontsize=12)
plt.show()
17.4. Sources de données en ligne#
Python facilite l’interrogation de bases de données en ligne de manière programmatique.
Une base de données importante pour les économistes est FRED — une vaste collection de données de séries temporelles maintenue par la Fed de St. Louis.
Par exemple, supposons que nous soyons intéressés par le taux de chômage.
(Pour télécharger les données au format csv, cliquez sur Download en haut à droite et sélectionnez l’option CSV (data)).
Alternativement, nous pouvons accéder au fichier CSV depuis un programme Python.
Cela peut être fait avec diverses méthodes.
Nous commençons par une méthode de relativement bas niveau, puis revenons à pandas.
17.4.1. Accéder aux données avec requests#
Une option consiste à utiliser requests, une bibliothèque Python standard pour demander des données via Internet.
Pour commencer, essayez le code suivant sur votre ordinateur
r = requests.get('https://fred.stlouisfed.org/graph/fredgraph.csv?bgcolor=%23e1e9f0&chart_type=line&drp=0&fo=open%20sans&graph_bgcolor=%23ffffff&height=450&mode=fred&recession_bars=on&txtcolor=%23444444&ts=12&tts=12&width=1318&nt=0&thu=0&trc=0&show_legend=yes&show_axis_titles=yes&show_tooltip=yes&id=UNRATE&scale=left&cosd=1948-01-01&coed=2024-06-01&line_color=%234572a7&link_values=false&line_style=solid&mark_type=none&mw=3&lw=2&ost=-99999&oet=99999&mma=0&fml=a&fq=Monthly&fam=avg&fgst=lin&fgsnd=2020-02-01&line_index=1&transformation=lin&vintage_date=2024-07-29&revision_date=2024-07-29&nd=1948-01-01')
S’il n’y a pas de message d’erreur, c’est que l’appel a réussi.
Si vous obtenez une erreur, alors il y a deux causes probables
Vous n’êtes pas connecté à Internet — espérons que ce n’est pas le cas.
Votre machine accède à Internet via un serveur proxy, et Python n’en est pas conscient.
Dans le second cas, vous pouvez soit
passer à une autre machine
résoudre votre problème de proxy en lisant la documentation
En supposant que tout fonctionne, vous pouvez maintenant utiliser l’objet source renvoyé par l’appel requests.get('https://research.stlouisfed.org/fred2/series/UNRATE/downloaddata/UNRATE.csv')
url = 'https://fred.stlouisfed.org/graph/fredgraph.csv?bgcolor=%23e1e9f0&chart_type=line&drp=0&fo=open%20sans&graph_bgcolor=%23ffffff&height=450&mode=fred&recession_bars=on&txtcolor=%23444444&ts=12&tts=12&width=1318&nt=0&thu=0&trc=0&show_legend=yes&show_axis_titles=yes&show_tooltip=yes&id=UNRATE&scale=left&cosd=1948-01-01&coed=2024-06-01&line_color=%234572a7&link_values=false&line_style=solid&mark_type=none&mw=3&lw=2&ost=-99999&oet=99999&mma=0&fml=a&fq=Monthly&fam=avg&fgst=lin&fgsnd=2020-02-01&line_index=1&transformation=lin&vintage_date=2024-07-29&revision_date=2024-07-29&nd=1948-01-01'
source = requests.get(url).content.decode().split("\n")
source[0]
'observation_date,UNRATE'
source[1]
'1948-01-01,3.4'
source[2]
'1948-02-01,3.8'
Nous pourrions maintenant écrire du code supplémentaire pour analyser ce texte et le stocker sous forme de tableau.
Mais c’est inutile — la fonction read_csv de pandas peut gérer cette tâche pour nous.
Nous utilisons parse_dates=True afin que pandas reconnaisse notre colonne de dates, permettant un filtrage simple par date
data = pd.read_csv(url, index_col=0, parse_dates=True)
Les données ont été lues dans un DataFrame pandas appelé data que nous pouvons maintenant manipuler de la manière habituelle
type(data)
pandas.DataFrame
data.head() # Une méthode utile pour jeter un coup d'œil rapide à un dataframe
| UNRATE | |
|---|---|
| observation_date | |
| 1948-01-01 | 3.4 |
| 1948-02-01 | 3.8 |
| 1948-03-01 | 4.0 |
| 1948-04-01 | 3.9 |
| 1948-05-01 | 3.5 |
pd.set_option('display.precision', 1)
data.describe() # Votre sortie peut différer légèrement
| UNRATE | |
|---|---|
| count | 918.0 |
| mean | 5.7 |
| std | 1.7 |
| min | 2.5 |
| 25% | 4.4 |
| 50% | 5.5 |
| 75% | 6.7 |
| max | 14.8 |
Nous pouvons également tracer le taux de chômage de 2006 à 2012 comme suit
ax = data['2006':'2012'].plot(title='US Unemployment Rate', legend=False)
ax.set_xlabel('year', fontsize=12)
ax.set_ylabel('%', fontsize=12)
plt.show()
Notez que pandas offre de nombreuses autres alternatives de types de fichiers.
Pandas dispose d’une grande variété de méthodes de haut niveau que nous pouvons utiliser pour lire des fichiers excel, json, parquet ou nous connecter directement à un serveur de base de données.
17.4.2. Utiliser wbgapi et yfinance pour accéder aux données#
La bibliothèque Python wbgapi peut être utilisée pour récupérer des données à partir des nombreuses bases de données publiées par la Banque mondiale.
Note
Vous pouvez trouver des informations utiles sur le paquet wbgapi dans cet article de blog de la Banque mondiale, en plus de ce tutoriel
Nous utiliserons également yfinance pour récupérer des données de Yahoo finance dans les exercices.
Pour l’instant, parcourons un exemple de téléchargement et de tracé de données — cette fois-ci de la Banque mondiale.
La Banque mondiale collecte et organise des données sur un large éventail d’indicateurs.
Par exemple, voici quelques données sur la dette publique en ratio du PIB.
L’exemple de code suivant récupère les données pour vous et trace les séries temporelles pour les États-Unis et l’Australie
import wbgapi as wb
wb.series.info('GC.DOD.TOTL.GD.ZS')
| id | value |
|---|---|
| GC.DOD.TOTL.GD.ZS | Central government debt, total (% of GDP) |
| 1 elements |
govt_debt = wb.data.DataFrame('GC.DOD.TOTL.GD.ZS', economy=['USA','AUS'], time=range(2005,2016))
govt_debt = govt_debt.T # déplace les années des colonnes vers les lignes pour le tracé
govt_debt.plot(xlabel='year', ylabel='Government debt (% of GDP)');
17.5. Exercices#
Exercice 17.1
Avec ces importations :
import datetime as dt
import yfinance as yf
Écrivez un programme pour calculer la variation en pourcentage du prix au cours de l’année 2021 pour les actions suivantes :
ticker_list = {'INTC': 'Intel',
'MSFT': 'Microsoft',
'IBM': 'IBM',
'BHP': 'BHP',
'TM': 'Toyota',
'AAPL': 'Apple',
'AMZN': 'Amazon',
'C': 'Citigroup',
'QCOM': 'Qualcomm',
'KO': 'Coca-Cola',
'GOOG': 'Google'}
Voici la première partie du programme
def read_data(ticker_list,
start=dt.datetime(2021, 1, 1),
end=dt.datetime(2021, 12, 31)):
"""
This function reads in closing price data from Yahoo
for each tick in the ticker_list.
"""
ticker = pd.DataFrame()
for tick in ticker_list:
stock = yf.Ticker(tick)
prices = stock.history(start=start, end=end)
# Change the index to date-only
prices.index = pd.to_datetime(prices.index.date)
closing_prices = prices['Close']
ticker[tick] = closing_prices
return ticker
ticker = read_data(ticker_list)
Complétez le programme pour tracer le résultat sous forme de diagramme en barres comme celui-ci :
Solution
Il existe plusieurs façons d’aborder ce problème en utilisant Pandas pour calculer la variation en pourcentage.
Premièrement, vous pouvez extraire les données et effectuer le calcul comme suit :
p1 = ticker.iloc[0] #Get the first set of prices as a Series
p2 = ticker.iloc[-1] #Get the last set of prices as a Series
price_change = (p2 - p1) / p1 * 100
price_change
INTC 6.9
MSFT 57.2
IBM 18.7
BHP -2.2
TM 23.4
AAPL 38.6
AMZN 5.8
C 3.6
QCOM 25.3
KO 14.9
GOOG 69.0
dtype: float64
Alternativement, vous pouvez utiliser une méthode intégrée pct_change et la configurer pour effectuer le calcul correct en utilisant l’argument periods.
change = ticker.pct_change(periods=len(ticker)-1, axis='rows')*100
price_change = change.iloc[-1]
price_change
INTC 6.9
MSFT 57.2
IBM 18.7
BHP -2.2
TM 23.4
AAPL 38.6
AMZN 5.8
C 3.6
QCOM 25.3
KO 14.9
GOOG 69.0
Name: 2021-12-30 00:00:00, dtype: float64
Puis pour tracer le graphique
price_change.sort_values(inplace=True)
price_change.rename(index=ticker_list, inplace=True)
fig, ax = plt.subplots(figsize=(10,8))
ax.set_xlabel('stock', fontsize=12)
ax.set_ylabel('percentage change in price', fontsize=12)
price_change.plot(kind='bar', ax=ax)
plt.show()
Exercice 17.2
En utilisant la méthode read_data introduite dans Exercice 17.1, écrivez un programme pour obtenir la variation en pourcentage d’une année sur l’autre pour les indices suivants :
indices_list = {'^GSPC': 'S&P 500',
'^IXIC': 'NASDAQ',
'^DJI': 'Dow Jones',
'^N225': 'Nikkei'}
Complétez le programme pour afficher des statistiques descriptives et tracer le résultat sous forme de graphique de série temporelle comme celui-ci :
Solution
En suivant le travail que vous avez effectué dans Exercice 17.1, vous pouvez interroger les données en utilisant read_data en mettant à jour les dates de début et de fin en conséquence.
indices_data = read_data(
indices_list,
start=dt.datetime(1971, 1, 1), #Common Start Date
end=dt.datetime(2021, 12, 31)
)
Ensuite, extrayez le premier et le dernier ensemble de prix par année sous forme de DataFrames et calculez les rendements annuels comme suit :
yearly_returns = pd.DataFrame()
for index, name in indices_list.items():
p1 = indices_data.groupby(indices_data.index.year)[index].first() # Get the first set of returns as a DataFrame
p2 = indices_data.groupby(indices_data.index.year)[index].last() # Get the last set of returns as a DataFrame
returns = (p2 - p1) / p1
yearly_returns[name] = returns
yearly_returns
| S&P 500 | NASDAQ | Dow Jones | Nikkei | |
|---|---|---|---|---|
| 1971 | 1.2e-01 | 1.4e-01 | NaN | 3.6e-01 |
| 1972 | 1.6e-01 | 1.8e-01 | NaN | 9.2e-01 |
| 1973 | -1.8e-01 | -3.2e-01 | NaN | -1.8e-01 |
| 1974 | -3.0e-01 | -3.5e-01 | NaN | -9.9e-02 |
| 1975 | 2.8e-01 | 2.8e-01 | NaN | 1.7e-01 |
| 1976 | 1.8e-01 | 2.5e-01 | NaN | 1.3e-01 |
| 1977 | -1.1e-01 | 7.5e-02 | NaN | -2.7e-02 |
| 1978 | 2.4e-02 | 1.3e-01 | NaN | 2.3e-01 |
| 1979 | 1.2e-01 | 2.8e-01 | NaN | 8.7e-02 |
| 1980 | 2.8e-01 | 3.7e-01 | NaN | 7.7e-02 |
| 1981 | -1.0e-01 | -3.8e-02 | NaN | 7.4e-02 |
| 1982 | 1.5e-01 | 1.9e-01 | NaN | 3.9e-02 |
| 1983 | 1.9e-01 | 2.1e-01 | NaN | 2.3e-01 |
| 1984 | 2.0e-02 | -1.1e-01 | NaN | 1.6e-01 |
| 1985 | 2.8e-01 | 3.2e-01 | NaN | 1.3e-01 |
| 1986 | 1.6e-01 | 7.3e-02 | NaN | 4.4e-01 |
| 1987 | 2.6e-03 | -6.4e-02 | NaN | 1.5e-01 |
| 1988 | 8.5e-02 | 1.3e-01 | NaN | 4.2e-01 |
| 1989 | 2.8e-01 | 2.0e-01 | NaN | 2.9e-01 |
| 1990 | -8.2e-02 | -1.9e-01 | NaN | -3.8e-01 |
| 1991 | 2.8e-01 | 5.8e-01 | NaN | -4.5e-02 |
| 1992 | 4.4e-02 | 1.5e-01 | 4.1e-02 | -2.9e-01 |
| 1993 | 7.1e-02 | 1.6e-01 | 1.3e-01 | 2.5e-02 |
| 1994 | -1.3e-02 | -2.4e-02 | 2.1e-02 | 1.4e-01 |
| 1995 | 3.4e-01 | 4.1e-01 | 3.3e-01 | 9.4e-03 |
| 1996 | 1.9e-01 | 2.2e-01 | 2.5e-01 | -6.1e-02 |
| 1997 | 3.2e-01 | 2.3e-01 | 2.3e-01 | -2.2e-01 |
| 1998 | 2.6e-01 | 3.9e-01 | 1.5e-01 | -7.5e-02 |
| 1999 | 2.0e-01 | 8.4e-01 | 2.5e-01 | 4.1e-01 |
| 2000 | -9.3e-02 | -4.0e-01 | -5.0e-02 | -2.7e-01 |
| 2001 | -1.1e-01 | -1.5e-01 | -5.9e-02 | -2.3e-01 |
| 2002 | -2.4e-01 | -3.3e-01 | -1.7e-01 | -2.1e-01 |
| 2003 | 2.2e-01 | 4.5e-01 | 2.1e-01 | 2.3e-01 |
| 2004 | 9.3e-02 | 8.4e-02 | 3.6e-02 | 6.1e-02 |
| 2005 | 3.8e-02 | 2.5e-02 | -1.1e-03 | 4.0e-01 |
| 2006 | 1.2e-01 | 7.6e-02 | 1.5e-01 | 5.3e-02 |
| 2007 | 3.7e-02 | 9.5e-02 | 6.3e-02 | -1.2e-01 |
| 2008 | -3.8e-01 | -4.0e-01 | -3.3e-01 | -4.0e-01 |
| 2009 | 2.0e-01 | 3.9e-01 | 1.5e-01 | 1.7e-01 |
| 2010 | 1.1e-01 | 1.5e-01 | 9.4e-02 | -4.0e-02 |
| 2011 | -1.1e-02 | -3.2e-02 | 4.7e-02 | -1.9e-01 |
| 2012 | 1.2e-01 | 1.4e-01 | 5.7e-02 | 2.1e-01 |
| 2013 | 2.6e-01 | 3.4e-01 | 2.4e-01 | 5.2e-01 |
| 2014 | 1.2e-01 | 1.4e-01 | 8.4e-02 | 9.7e-02 |
| 2015 | -6.9e-03 | 5.9e-02 | -2.3e-02 | 9.3e-02 |
| 2016 | 1.1e-01 | 9.8e-02 | 1.5e-01 | 3.6e-02 |
| 2017 | 1.8e-01 | 2.7e-01 | 2.4e-01 | 1.6e-01 |
| 2018 | -7.0e-02 | -5.3e-02 | -6.0e-02 | -1.5e-01 |
| 2019 | 2.9e-01 | 3.5e-01 | 2.2e-01 | 2.1e-01 |
| 2020 | 1.5e-01 | 4.2e-01 | 6.0e-02 | 1.8e-01 |
| 2021 | 2.9e-01 | 2.4e-01 | 2.0e-01 | 5.6e-02 |
Ensuite, vous pouvez obtenir des statistiques descriptives en utilisant la méthode describe.
yearly_returns.describe()
| S&P 500 | NASDAQ | Dow Jones | Nikkei | |
|---|---|---|---|---|
| count | 5.1e+01 | 5.1e+01 | 3.0e+01 | 5.1e+01 |
| mean | 9.2e-02 | 1.3e-01 | 9.1e-02 | 7.9e-02 |
| std | 1.6e-01 | 2.5e-01 | 1.4e-01 | 2.4e-01 |
| min | -3.8e-01 | -4.0e-01 | -3.3e-01 | -4.0e-01 |
| 25% | -2.2e-03 | 1.6e-04 | 2.5e-02 | -6.8e-02 |
| 50% | 1.2e-01 | 1.4e-01 | 8.9e-02 | 7.7e-02 |
| 75% | 2.0e-01 | 2.8e-01 | 2.1e-01 | 2.0e-01 |
| max | 3.4e-01 | 8.4e-01 | 3.3e-01 | 9.2e-01 |
Puis, pour tracer le graphique
fig, axes = plt.subplots(2, 2, figsize=(10, 8))
for iter_, ax in enumerate(axes.flatten()): # Flatten 2-D array to 1-D array
index_name = yearly_returns.columns[iter_] # Get index name per iteration
ax.plot(yearly_returns[index_name]) # Plot pct change of yearly returns per index
ax.set_ylabel("percent change", fontsize = 12)
ax.set_title(index_name)
plt.tight_layout()