Température HadCRUT¶

Dans ce document, c'est le package prince qui est utilisé pour réaliser l'ACP, pour démonstration.

In [1]:
import pandas
import numpy
import matplotlib.pyplot as plt
import seaborn
seaborn.set_style("white")

from prince import PCA
In [2]:
temp = pandas.read_table(
    "https://crudata.uea.ac.uk/cru/data/temperature/HadCRUT5.0Analysis_gl.txt", 
    sep = "\s+", 
    names = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", "Annual"])
temp = temp.iloc[::2]
temp = temp.iloc[:-1] # suppression de la dernière ligne (i.e. 2024 n'est pas finie)
temp
Out[2]:
Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec Annual
1850 -0.734 -0.360 -0.627 -0.605 -0.532 -0.352 -0.176 -0.223 -0.394 -0.516 -0.277 -0.322 -0.426
1851 -0.266 -0.467 -0.641 -0.528 -0.222 -0.207 -0.136 -0.148 -0.146 -0.045 -0.108 -0.248 -0.264
1852 -0.346 -0.496 -0.567 -0.575 -0.113 -0.077 0.009 -0.149 -0.006 -0.158 -0.275 0.064 -0.224
1853 -0.204 -0.399 -0.273 -0.401 -0.270 -0.124 -0.072 -0.040 -0.266 -0.373 -0.401 -0.326 -0.262
1854 -0.403 -0.392 -0.247 -0.327 -0.277 -0.302 -0.162 -0.233 -0.216 -0.099 -0.412 -0.454 -0.294
... ... ... ... ... ... ... ... ... ... ... ... ... ...
2020 1.058 1.110 1.075 1.050 0.894 0.811 0.822 0.809 0.867 0.799 1.007 0.692 0.916
2021 0.707 0.565 0.737 0.757 0.698 0.718 0.791 0.809 0.875 0.928 0.864 0.758 0.767
2022 0.782 0.753 0.890 0.767 0.750 0.847 0.778 0.875 0.816 0.951 0.669 0.745 0.802
2023 0.783 0.886 1.129 0.925 0.880 1.059 1.161 1.213 1.348 1.327 1.342 1.264 1.110
2024 1.146 1.276 1.221 1.207 1.061 1.094 1.130 1.216 1.143 1.201 1.209 1.125 1.169

175 rows × 13 columns

Description¶

  • Pas de données manquantes
  • Des moyennes globalement similaires
  • Idem pour la variance
  • Pas de distribution dissymétrique, mais quelques valeurs extrêmes toutefois.
In [3]:
temp.describe().round(2)
Out[3]:
Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec Annual
count 175.00 175.00 175.00 175.00 175.00 175.00 175.00 175.00 175.00 175.00 175.00 175.00 175.00
mean -0.09 -0.09 -0.11 -0.08 -0.08 -0.06 -0.03 -0.02 -0.04 -0.03 -0.07 -0.10 -0.07
std 0.43 0.44 0.46 0.42 0.39 0.37 0.36 0.37 0.37 0.40 0.42 0.43 0.39
min -1.04 -0.85 -0.83 -0.68 -0.70 -0.63 -0.59 -0.61 -0.65 -0.68 -0.68 -0.90 -0.60
25% -0.39 -0.41 -0.42 -0.40 -0.35 -0.30 -0.30 -0.28 -0.31 -0.33 -0.37 -0.41 -0.34
50% -0.18 -0.21 -0.20 -0.18 -0.18 -0.15 -0.12 -0.12 -0.14 -0.10 -0.13 -0.20 -0.17
75% 0.15 0.13 0.12 0.15 0.13 0.11 0.10 0.14 0.13 0.12 0.08 0.11 0.09
max 1.15 1.28 1.22 1.21 1.06 1.09 1.16 1.22 1.35 1.33 1.34 1.26 1.17
In [4]:
plt.figure(figsize = (16, 8))
seaborn.boxplot(data = temp)
plt.show()
No description has been provided for this image

Evolution annuelle sur la période¶

La représentation de l'évolution des anomalies moyennes annuelles permet de voir que la température est en augmentation, particulièrement depuis le milieu des années 1970.

In [5]:
plt.figure(figsize = (16, 8))
plt.axhline(y = 0, linestyle = "dashed", color = "gray", alpha = .5)
plt.axvline(x = 1961, linestyle = "dashed", color = "gray", alpha = .5)
plt.axvline(x = 1990, linestyle = "dashed", color = "gray", alpha = .5)
seaborn.lineplot(x = "Annee", y = "Annual",
                 data = temp.assign(Annee = temp.index).reset_index(drop=True))
plt.show()
No description has been provided for this image

Analyse¶

on enlève la dernière colonne, qui est l'anomalie annuelle.

In [6]:
temp2 = temp[temp.columns[:12]]
temp2
Out[6]:
Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec
1850 -0.734 -0.360 -0.627 -0.605 -0.532 -0.352 -0.176 -0.223 -0.394 -0.516 -0.277 -0.322
1851 -0.266 -0.467 -0.641 -0.528 -0.222 -0.207 -0.136 -0.148 -0.146 -0.045 -0.108 -0.248
1852 -0.346 -0.496 -0.567 -0.575 -0.113 -0.077 0.009 -0.149 -0.006 -0.158 -0.275 0.064
1853 -0.204 -0.399 -0.273 -0.401 -0.270 -0.124 -0.072 -0.040 -0.266 -0.373 -0.401 -0.326
1854 -0.403 -0.392 -0.247 -0.327 -0.277 -0.302 -0.162 -0.233 -0.216 -0.099 -0.412 -0.454
... ... ... ... ... ... ... ... ... ... ... ... ...
2020 1.058 1.110 1.075 1.050 0.894 0.811 0.822 0.809 0.867 0.799 1.007 0.692
2021 0.707 0.565 0.737 0.757 0.698 0.718 0.791 0.809 0.875 0.928 0.864 0.758
2022 0.782 0.753 0.890 0.767 0.750 0.847 0.778 0.875 0.816 0.951 0.669 0.745
2023 0.783 0.886 1.129 0.925 0.880 1.059 1.161 1.213 1.348 1.327 1.342 1.264
2024 1.146 1.276 1.221 1.207 1.061 1.094 1.130 1.216 1.143 1.201 1.209 1.125

175 rows × 12 columns

Et on réalise une ACP sur les données brutes, car les variables ont toutes la même unité, et des variations similaires.

In [7]:
pca = PCA(n_components = 12)
pca.fit(temp2)
Out[7]:
PCA(n_components=12)
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
PCA(n_components=12)

On remarque que 92% de l'information est contenue dans le seul premier facteur.

In [8]:
pca.eigenvalues_summary
Out[8]:
eigenvalue % of variance % of variance (cumulative)
component
0 11.124 92.70% 92.70%
1 0.301 2.51% 95.21%
2 0.126 1.05% 96.26%
3 0.103 0.85% 97.12%
4 0.083 0.69% 97.81%
5 0.065 0.54% 98.35%
6 0.047 0.39% 98.74%
7 0.041 0.35% 99.08%
8 0.035 0.29% 99.38%
9 0.029 0.24% 99.61%
10 0.024 0.20% 99.82%
11 0.022 0.18% 100.00%
In [9]:
pca.scree_plot()
Out[9]:
In [10]:
pca.column_correlations.round(2) 
Out[10]:
component 0 1 2 3 4 5 6 7 8 9 10 11
variable
Jan 0.94 -0.26 -0.15 0.13 0.01 -0.11 -0.08 0.05 -0.00 -0.00 -0.01 -0.01
Feb 0.94 -0.26 -0.11 0.01 0.00 0.16 0.07 -0.04 -0.02 0.00 0.02 -0.00
Mar 0.96 -0.14 0.08 -0.20 -0.07 0.01 -0.09 0.06 -0.02 -0.01 0.05 -0.00
Apr 0.97 -0.13 0.08 -0.08 -0.03 -0.03 0.02 -0.04 0.04 0.02 -0.12 0.03
May 0.97 -0.07 0.08 -0.01 0.05 -0.13 0.08 -0.07 0.03 0.02 0.07 -0.02
Jun 0.98 0.03 0.11 0.04 0.12 0.00 0.05 0.06 -0.06 -0.10 -0.01 0.03
Jul 0.97 0.07 0.12 0.07 0.08 0.05 -0.03 0.04 -0.04 0.12 -0.00 -0.01
Aug 0.98 0.09 0.07 0.08 -0.01 0.07 -0.05 -0.01 0.09 -0.05 -0.01 -0.08
Sep 0.98 0.12 0.01 0.10 -0.08 0.02 -0.04 -0.03 0.05 -0.01 0.04 0.11
Oct 0.97 0.15 -0.03 0.02 -0.12 -0.03 -0.01 -0.08 -0.12 -0.01 -0.02 -0.03
Nov 0.96 0.18 -0.11 -0.03 -0.09 -0.02 0.10 0.11 0.03 0.03 -0.00 -0.01
Dec 0.94 0.21 -0.17 -0.12 0.15 -0.00 -0.05 -0.04 0.02 -0.00 -0.01 0.01

Le cercle des corrélations nous informe que les mois ont globalement tous le même comportement, et donc que les années à droite du graphique seront les années les plus chaudes.

Vous remarquerez qu'il est plus simple à obtenir avec le module prince, par rapport à sklearn.

In [11]:
coord_col = pca.column_correlations

fig, axes = plt.subplots(figsize = (10, 10))
fig.suptitle("Cercle des corrélations")
axes.set_xlim(-1, 1)
axes.set_ylim(-1, 1)
axes.axvline(x = 0, color = 'lightgray', linestyle = '--', linewidth = 1)
axes.axhline(y = 0, color = 'lightgray', linestyle = '--', linewidth = 1)
for j in range(12):
    xj = coord_col.iloc[j,0]
    yj = coord_col.iloc[j,1]
    axes.text(xj, yj, coord_col.index[j], size = 25)
    axes.plot([0,xj], [0,yj], color = "gray", linestyle = 'dashed')
plt.gca().add_artist(plt.Circle((0,0),1,color='blue',fill=False))

plt.show()
No description has been provided for this image
In [12]:
pca.plot(temp2,
         x_component = 0,
         y_component = 1,
         show_column_markers = False,
         show_column_labels = True)
Out[12]:
In [13]:
coord = pca.row_coordinates(temp2)
coord
Out[13]:
component 0 1 2 3 4 5 6 7 8 9 10 11
1850 -3.071309 0.635555 -0.018651 0.009371 0.413547 0.786137 0.277005 0.275376 0.211260 0.246231 0.110715 -0.175821
1851 -1.640291 0.837327 -0.257089 0.681652 0.027104 -0.296712 0.175586 0.023327 -0.190173 0.062788 0.182531 -0.199253
1852 -1.275256 1.124770 -0.045102 0.519288 0.834015 -0.211089 -0.124049 -0.187674 -0.106376 0.099260 0.530889 0.202568
1853 -1.656835 -0.075961 0.401136 0.294911 0.491537 0.061327 -0.393613 0.325093 0.116623 -0.098832 0.172356 -0.257820
1854 -1.938869 0.038687 0.406090 -0.009249 -0.294111 -0.008112 -0.217234 -0.209801 -0.323525 0.125962 0.132320 -0.056110
... ... ... ... ... ... ... ... ... ... ... ... ...
2020 8.429555 -0.726340 -0.004645 0.009049 -0.256703 -0.012315 0.232039 0.268562 0.148746 0.157750 -0.010115 0.140896
2021 7.230910 0.684227 0.178344 0.265253 -0.218412 -0.195664 -0.144415 0.004509 -0.046591 0.065496 -0.051645 0.080758
2022 7.514924 0.156719 0.358792 0.210943 0.014276 0.013955 -0.273364 -0.163086 -0.275042 -0.290296 0.061668 -0.064923
2023 10.213719 1.621144 0.146847 0.195253 -0.293477 0.346207 -0.231632 0.111659 -0.056894 -0.000021 0.202233 0.227505
2024 10.660062 0.095211 0.089687 0.192368 -0.050481 0.263344 0.012659 -0.005632 0.036619 -0.000161 -0.079894 -0.071410

175 rows × 12 columns

In [14]:
contrib = pca.row_contributions_
contrib.round(2)
Out[14]:
component 0 1 2 3 4 5 6 7 8 9 10 11
1850 0.00 0.01 0.00 0.00 0.01 0.05 0.01 0.01 0.01 0.01 0.00 0.01
1851 0.00 0.01 0.00 0.03 0.00 0.01 0.00 0.00 0.01 0.00 0.01 0.01
1852 0.00 0.02 0.00 0.02 0.05 0.00 0.00 0.00 0.00 0.00 0.07 0.01
1853 0.00 0.00 0.01 0.00 0.02 0.00 0.02 0.01 0.00 0.00 0.01 0.02
1854 0.00 0.00 0.01 0.00 0.01 0.00 0.01 0.01 0.02 0.00 0.00 0.00
... ... ... ... ... ... ... ... ... ... ... ... ...
2020 0.04 0.01 0.00 0.00 0.00 0.00 0.01 0.01 0.00 0.00 0.00 0.01
2021 0.03 0.01 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
2022 0.03 0.00 0.01 0.00 0.00 0.00 0.01 0.00 0.01 0.02 0.00 0.00
2023 0.05 0.05 0.00 0.00 0.01 0.01 0.01 0.00 0.00 0.00 0.01 0.01
2024 0.06 0.00 0.00 0.00 0.00 0.01 0.00 0.00 0.00 0.00 0.00 0.00

175 rows × 12 columns

Représentation des années¶

On peut maintenant representer les années sur le premier plan factoriel. On voit bien que les dernières années sont les plus chaudes.

In [15]:
pca.plot(temp2,
         x_component = 0,
         y_component = 1,
         show_row_markers = False,
         show_column_markers = False,
         show_row_labels = True)
Out[15]:

En se concentrant sur les valeurs extrêmes.

In [16]:
plt.figure(figsize = (16, 10))
seaborn.scatterplot(x = 0, y = 1, data = coord, color = "gray", alpha = .25)
for i in range(coord.shape[0]):
    taille = "small"
    if (contrib.iloc[i,0] > .02):
        taille = 20
    if (contrib.iloc[i,1] > .02):
        taille = 20
    plt.text(coord.iloc[i][0], coord.iloc[i][1], coord.index[i], 
             fontsize = taille, ha = "center", va = "center")
No description has been provided for this image
In [17]:
pandas.DataFrame(temp.mean()).transpose().round(3).rename(index = {0: "Moyenne"})
Out[17]:
Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec Annual
Moyenne -0.092 -0.089 -0.109 -0.079 -0.083 -0.056 -0.034 -0.017 -0.035 -0.031 -0.068 -0.102 -0.066

Années avec un début d'année chaud et une fin d'année froide¶

In [18]:
temp.filter(items = [1852, 1877, 1893, 1951], axis = 0)
Out[18]:
Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec Annual
1852 -0.346 -0.496 -0.567 -0.575 -0.113 -0.077 0.009 -0.149 -0.006 -0.158 -0.275 0.064 -0.224
1877 -0.409 -0.195 -0.070 -0.316 -0.403 -0.109 0.017 0.230 0.127 0.125 0.043 -0.028 -0.082
1893 -1.040 -0.846 -0.421 -0.508 -0.570 -0.453 -0.253 -0.295 -0.369 -0.305 -0.402 -0.477 -0.495
1951 -0.373 -0.521 -0.276 -0.120 0.020 0.050 0.027 0.111 0.121 0.116 -0.082 0.066 -0.072

Années avec un début d'année froid et une fin d'année chaude¶

In [19]:
temp.filter(items = [1878, 1882, 1903, 1912, 1992], axis = 0)
Out[19]:
Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec Annual
1878 -0.041 0.261 0.403 0.210 -0.092 0.018 -0.062 -0.045 0.028 -0.148 -0.119 -0.290 0.010
1882 -0.012 -0.098 -0.146 -0.289 -0.290 -0.358 -0.308 -0.224 -0.145 -0.382 -0.385 -0.628 -0.272
1903 -0.423 -0.235 -0.406 -0.590 -0.514 -0.549 -0.511 -0.611 -0.571 -0.674 -0.631 -0.708 -0.535
1912 -0.326 -0.312 -0.557 -0.338 -0.335 -0.290 -0.510 -0.580 -0.646 -0.685 -0.556 -0.577 -0.476
1992 0.357 0.343 0.305 0.156 0.164 0.152 0.003 0.035 -0.096 -0.029 -0.040 0.090 0.120

Années (beaucoup) plus chaudes que la moyenne¶

In [20]:
temp.filter(items = [i for i in range(2015, 2022)], axis = 0)
Out[20]:
Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec Annual
2015 0.732 0.773 0.787 0.684 0.726 0.752 0.710 0.799 0.818 1.014 0.985 1.035 0.818
2016 1.079 1.216 1.176 1.023 0.873 0.769 0.761 0.940 0.837 0.812 0.839 0.772 0.925
2017 0.947 1.062 1.044 0.835 0.775 0.650 0.793 0.802 0.723 0.804 0.795 0.806 0.836
2018 0.707 0.776 0.776 0.811 0.701 0.727 0.724 0.734 0.677 0.858 0.732 0.816 0.753
2019 0.813 0.835 1.077 0.925 0.777 0.808 0.851 0.836 0.813 0.954 0.933 1.026 0.887
2020 1.058 1.110 1.075 1.050 0.894 0.811 0.822 0.809 0.867 0.799 1.007 0.692 0.916
2021 0.707 0.565 0.737 0.757 0.698 0.718 0.791 0.809 0.875 0.928 0.864 0.758 0.767

Heatmap classique de l'évolution pour repérer les années à comportement atypique¶

In [21]:
plt.figure(figsize = (16, 4))
g = seaborn.heatmap(temp2.T, cmap = "coolwarm")
No description has been provided for this image

Et avec une AFC ?¶

Les données doivent être toutes positives, donc on ajoute une valeur fixe à toutes les valeurs.

In [22]:
from prince import CA

ca = CA()
ca.fit(temp2 + 10)
Out[22]:
<prince.ca.CA at 0x1a6b43edd30>

Certaines années ont un comportement très particulier, ce qui gène considérablement l'analyse globale. Vous pouvez zoomer dans le graphique obtenu.

In [23]:
ca.plot(temp2,
        x_component = 0,
        y_component = 1,
        show_row_markers = False,
        show_column_markers = False,
        show_row_labels = True,
        show_column_labels = False
)
Out[23]:
In [24]:
ca.plot(temp2,
        x_component = 0,
        y_component = 1,
        show_row_markers = False,
        show_column_markers = False,
        show_row_labels = False,
        show_column_labels = True
)
Out[24]:

La représentation conjointe ne nous permet pas de mieux commprendre les comportements atypiques.

In [25]:
ca.plot(temp2,
        x_component = 0,
        y_component = 1,
        show_row_markers = False,
        show_column_markers = False,
        show_row_labels = True,
        show_column_labels = True
)
Out[25]:

Quelques remarques par année :

  • 1878 : Mars très chaud
  • 1942 : Janvier très chaud alors que l'année semble froide ou moyenne
  • 1943 : Janvier et Mars froid
  • 1961 : Décembre très froid
  • 1978 : Août très froid
In [26]:
temp.filter(items = [i for i in [1878, 1942, 1943, 1961, 1978]], axis = 0)
Out[26]:
Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec Annual
1878 -0.041 0.261 0.403 0.210 -0.092 0.018 -0.062 -0.045 0.028 -0.148 -0.119 -0.290 0.010
1942 0.250 0.016 -0.045 -0.009 0.122 0.027 -0.062 -0.061 -0.014 -0.093 -0.056 -0.080 -0.001
1943 -0.191 -0.027 -0.164 0.022 0.033 -0.078 0.021 0.031 -0.015 0.230 0.104 0.110 0.006
1961 -0.046 0.097 -0.055 0.066 0.074 0.092 -0.033 -0.008 -0.025 -0.060 -0.027 -0.248 -0.014
1978 0.037 0.075 0.060 0.053 0.014 -0.054 0.015 -0.217 0.037 -0.060 0.094 0.011 0.006
In [27]:
plt.figure(figsize = (4, 16))
g = seaborn.heatmap(temp2, cmap = "coolwarm")
No description has been provided for this image
In [ ]: