Tableaux périodiques en colormap (LaTeX)

Où l’on repeint le tableau périodique pour visualiser l’évolution de quelques grandeurs (électronégativité, affinité électronique, énergie d’ionisation, rayon atomique, nombre d’isotopes stables).
Le document est généré avec le package pgfplots pour LaTeX.

Avec le package pfgplots, on peut tracer des graphiques, soit avec l’expression analytique d’une fonction, soit avec des données tabulées :

\documentclass[border=0.5cm,11pt,convert={ghostscript,outext=.png,density=800x800}]{standalone}%conversion en png avec ghostscript

\usepackage{pgfplots}\pgfplotsset{compat=newest}

\begin{document}

\begin{tikzpicture}
\begin{axis}
\addplot table [x=A,y=B,col sep=tab]{%
	A	B	C
	2	12	Lu
	3	15	Ma
	4	10	Me
	5	8	Je
};
\end{axis}
\end{tikzpicture}

\end{document}

Les données comportent ici 3 colonnes ; on précise quelles colonnes fournissent les données en x et en y.

D’autre part, on peut ajouter des étiquettes aux points :

\documentclass[border=0.5cm,11pt,convert={ghostscript,outext=.png,density=800x800}]{standalone}%conversion en png avec ghostscript

\usepackage{pgfplots}\pgfplotsset{compat=newest}

\begin{document}

\begin{tikzpicture}
\begin{axis}
[
nodes near coords,% indique d'ajouter les métadonnées
point meta={explicit symbolic},% indique que les métadonnées ne sont pas des scalaires
]
\addplot table [x=A,y=B,col sep=tab,meta=C]{%
	A	B	C
	2	12	Lu
	3	15	Ma
	4	10	Me
	5	8	Je
};
\end{axis}
\end{tikzpicture}

\end{document}

“meta” sont les métadonnées à mettre en étiquettes des points. Ici on indique que la colonne C fournit les métadonnées.

On peut modifier la taille et la forme des marqueurs :

\documentclass[border=0.5cm,11pt,convert={ghostscript,outext=.png,density=800x800}]{standalone}%conversion en png avec ghostscript

\usepackage{pgfplots}\pgfplotsset{compat=newest}

\begin{document}

\begin{tikzpicture}
\begin{axis}
[
nodes near coords,
point meta={explicit symbolic},
mark options={mark=square*,mark size=6pt}
]
\addplot table [x=A,y=B,col sep=tab,meta=C]{%
	A	B	C
	2	12	Lu
	3	15	Ma
	4	10	Me
	5	8	Je
};
\end{axis}
\end{tikzpicture}

\end{document}

Enfin, on peut utiliser un colormap, c’est-à-dire une correspondance entre une gamme de valeurs et une gamme de couleurs, pour colorer les marqueurs selon une valeur dans une colonne :

\documentclass[border=0.5cm,11pt,convert={ghostscript,outext=.png,density=800x800}]{standalone}%conversion en png avec ghostscript

\usepackage{pgfplots}\pgfplotsset{compat=newest}

\begin{document}

\begin{tikzpicture}
\begin{axis}
[
only marks,
scatter,
scatter src=\thisrow{C},% couleur des carrés donnée
mark options={mark=square*,mark size=6pt},
colorbar horizontal,
colormap={mycmap}{color(0cm)=(white); color(1cm)=(red)},
]
\addplot table [x=A,y=B,col sep=tab]{%
	A	B	C
	2	12	0.1
	3	15	0.5
	4	10	0.01
	5	8	0.8
};
\end{axis}
\end{tikzpicture}

\end{document}

Ici, la colonne C contient des valeurs à faire correspondre à la gamme de couleurs. Automatiquement, la couleur initiale (blanc) est associée à la valeur la plus faible, et la couleur finale à la valeur maximale.


C’est tout ce qu’il nous fallait pour produire ceci :

Chaque case du tableau périodique est un marqueur carré, coloré avec un colormap. Chaque symbole chimique est une étiquette qui a été ajoutée par-dessus le marqueur.


Le code LaTeX lit les données figurant dans un fichier csv (un par grandeur analysée). Ce fichier .csv contient :

  • une colonne avec le numéro de colonne de l’élément, dans le tableau périodique à 18 colonnes
  • une colonne avec son numéro de ligne dans le tableau périodique
  • une colonne avec son symbole
  • enfin une colonne avec la valeur de la grandeur analysée (électronégativité,…)

De tels fichiers ont été générés par un petit script Python qui exploite le module Mendeleev. Ci-dessous un extrait du fichier csv pour l’affinité électronique des éléments.

col	lig	symbole	valeur
1	1	H	0.754195
18	1	He	NaN
1	2	Li	0.618049
2	2	Be	NaN
13	2	B	0.279723
14	2	C	1.262119
15	2	N	NaN
16	2	O	1.4611135
17	2	F	3.4011897000000006

Le code Python qui extrait les données et les exporte en fichier csv. Il nécessite d’installer les modules Mendeleev, Numpy (gestion de listes de valeurs) et Pandas (gestion de données en tableau et export csv).

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Crée le Déc 17  2022

@auteur: David ALBERTO
(www.astrolabe-science.fr)
"""
"""
Ce code récupère les symboles, lignes et colonnes du tableau périodique
pour les éléments chimiques demandés, ainsi que les valeurs d'une
série de paramètres. Ces données sont exportées dans un fichier csv.
"""
import numpy as np#pour remplacer les valeurs absentes par 'NaN' (Not a Number)
from mendeleev import element
import pandas as pd

Zmax=118#on peut limiter aux premiers éléments chimiques
liste_Z = np.arange(1,Zmax+1)
NomFichier = 'donnees_elements_chimiques.csv'

"""
La ligne est la donnee de la période de l'élément.
La colonne est la donnee du paramètre "group_id" de l'élément.
Pour les terres rares, le module ne fournit pas de donnee pour "group_id" ; 
je me base donc sur la donnee de Z pour établir un numéro de colonne.
"""
y_offset_TR = 3# décalage de lignes vers le bas pour les terres rares


lig = []
col = []
symbole = []
electroneg = []
affinite = []
energie_ionisation = []
rayon_atomique = []
abondance_croute = []
abondance_mer = []
masse_atomique = []
bloc = []
T_ebu = []
T_fus = []
densite = []
polarisabilite = []
config_elec = []
config_elec_fin = []
enthalpie_vap = []
enthalpie_fus = []
enthalpie_form = []
english = []
conduct_therm = []
rayon_vdw = []
francais = ['Hydrogène','Hélium','Lithium','Béryllium','Bore','Carbone','Azote','Oxygène',
      'Fluor','Néon','Sodium','Magnésium','Aluminium','Silicium','Phosphore','Soufre',
      'Chlore','Argon','Potassium','Calcium','Scandium','Titane','Vanadium','Chrome',
      'Manganèse','Fer','Cobalt','Nickel','Cuivre','Zinc','Gallium','Germanium',
      'Arsenic','Sélénium','Brome','Krypton','Rubidium','Strontium','Yttrium',
      'Zirconium','Niobium','Molybdène','Technétium','Ruthénium','Rhodium',
      'Palladium','Argent','Cadmium','Indium',
'Étain','Antimoine','Tellure','Iode','Xénon','Césium','Baryum','Lanthane','Cérium',
'Praséodyme','Néodyme','Prométhium','Samarium','Europium','Gadolinium','Terbium',
'Dysprosium','Holmium','Erbium','Thulium','Ytterbium','Lutetium','Hafnium','Tantale',
'Tungstène','Rhénium','Osmium','Iridium','Platine','Or','Mercure','Thallium','Plomb',
'Bismuth','Polonium','Astate','Radon','Francium','Radium','Actinium','Thorium','Protactinium',
'Uranium','Neptunium','Plutonium','Americium','Curium','Berkelium','Californium','Einsteinium',
'Fermium','Mendelevium','Nobelium','Lawrencium','Rutherfordium','Dubnium','Seaborgium','Bohrium',
'Hassium','Meitnerium','Darmstadtium','Roentgenium','Copernicium','Nihonium','Flerovium',
'Moscovium','Livermorium','Tennessine','Oganesson',
]

abondance_univers = pd.read_csv("abondanceUnivers.csv",
                        sep='\t',
                        usecols=[4],
                        names=['abondance'],
                        skiprows=1
                        )

abondance_univers = abondance_univers['abondance']


for Z in liste_Z:

    Z = int(Z)
    if Z>=58 and Z<=71:#lanthanides
        colonne = 2+Z-57
        ligne = element(Z).period+y_offset_TR
    elif Z>=90 and Z<=103:#actinides
        colonne = 2+Z-89
        ligne = element(Z).period+y_offset_TR
    else:
        colonne = element(Z).group_id
        ligne = element(Z).period
    col.append(colonne)
    lig.append(ligne)
    symbole.append(element(Z).symbol)
    
    electroneg.append(element(Z).electronegativity('pauling'))

    Aff = element(Z).electron_affinity
    if type(Aff) == float:
        if Aff >= 0:
            affinite.append(Aff)
        else:
            affinite.append(np.nan)
    else:
        affinite.append(np.nan)

    if Z <=108:
        energie_ionisation.append(element(Z).ionenergies[1])
    else:
        energie_ionisation.append(np.nan)
    abondance_croute.append(element(Z).abundance_crust)
    abondance_mer.append(element(Z).abundance_sea)
    masse_atomique.append(element(Z).atomic_weight)
    blocZ = element(Z).block
    bloc.append(blocZ)
    conf= element(Z).econf
    config_elec.append(conf)
    liste_conf=conf.split()
    if blocZ == 'f':
        bloc_fin = liste_conf[-3]
    elif blocZ != 'd' or Z==46:
        bloc_fin = liste_conf[-1]
    else:
        bloc_fin = liste_conf[-2]
    config_elec_fin.append(bloc_fin)
    T_ebu.append(element(Z).boiling_point)
    T_fus.append(element(Z).melting_point)
    densite.append(element(Z).density)
    polarisabilite.append(element(Z).dipole_polarizability)
    enthalpie_vap.append(element(Z).evaporation_heat)
    enthalpie_fus.append(element(Z).fusion_heat)
    enthalpie_form.append(element(Z).heat_of_formation)
    english.append(element(Z).name)
    conduct_therm.append(element(Z).thermal_conductivity)
    rayon_vdw.append(element(Z).vdw_radius)
    rayon_atomique.append(element(Z).atomic_radius)
    print(Z)# en guise de compteur, pour vérification
# ----------------------------------------------



#  création d'un tableau (dataframe du module pandas)
df=pd.DataFrame({'Z': liste_Z,
                 'col':col,
                  'lig':lig,
                  'symbole':symbole,
                  'bloc' : bloc,
                  'francais': francais,
                  'english' : english,
                   'masse_atomique' : masse_atomique,
                   'config_elec' : config_elec,
                   'config_elec_fin' : config_elec_fin,
                   'electronegativite' : electroneg,
                   'rayon_atomique' : rayon_atomique,
                   'rayon_vdw' : rayon_vdw,
                   'energie_ionisation' : energie_ionisation,
                   'densite' : densite,
                   'abondance_croute' : abondance_croute,
                   'abondance_mer' : abondance_mer,
                    'abondance_univers' : abondance_univers,
                   'T_fus' : T_fus,
                   'T_ebu' : T_ebu,
                   'polarisabilite' : polarisabilite,
                   'enthalpie_fus' : enthalpie_fus,
                   'enthalpie_vap' : enthalpie_vap,
                   'enthalpie_form' : enthalpie_form,
                   'conduct_therm' : conduct_therm,
                   'affinite_elec' : affinite
                  }
                )



df.to_csv(NomFichier,
          sep='\t',# séparateur de colonne : tabulation
          index=False,
          na_rep='NaN'# remplace les données vides par 'NaN' (lisible par LaTeX pgfplots)
          )

Voici le code LaTeX pour l’évolution de l’affinité électronique dans le tableau périodique :

\documentclass[border=0.5cm,11pt,convert={ghostscript,outext=.png,density=800x800}]{standalone}%conversion en png, en ligne de commande, avec ghostscript (voir doc standalone)

%COMPILé AVEC XeLaTeX
% se reporter au manuel PGFPLOTS section 3.4 et 5.3 (colormaps)

\usepackage[T1]{fontenc}
\usepackage[x11names,dvipsnames,svgnames]{xcolor}
\colorlet{vert}{OliveGreen!70!black}
\colorlet{rouge}{red!80!black}
\colorlet{monorange}{yellow!50!red}
\usepackage{lmodern}
\usepackage{tikz}
\usepackage{pgfplots}\pgfplotsset{compat=newest}
\pgfplotsset{/pgf/number format/.cd,1000 sep={~}}
\usetikzlibrary{plotmarks}

\begin{document}

\footnotesize
\sffamily

\begin{tikzpicture}
\begin{axis}[
y dir=reverse,% inversion axe y (périodes)
xmin=1,xmax=18,
ymin=1,ymax=10,
%colorbar,% verticale à gauche, par défaut
colorbar horizontal,
colormap={mycmap}{color(0cm)=(white); color(1cm)=(DarkOrange)},
width=360pt,% 20 fois 18 colonnes, pour les proportions
height=200pt,% 20 fois 10 lignes, pour les proportions
scale only axis,
title=\Large{Affinité électronique (eV)},
hide axis,
]
%
% affichage des carrés colorés :
\addplot [%
scatter,
only marks,
%mark=*,% marqueur rond plein
mark=square*,% marqueur carré plein
scatter src=\thisrow{valeur},% couleur des carrés donnée par la colonne 'valeur'
scatter/use mapped color=
        {draw opacity=0,fill=mapped color},% retire le contour du marqueur
mark options={mark size=10pt,% adapté aux dimensions
},
]
table []{donnees_affinite_elec.csv};
%
%affichage des symboles :
\addplot [nodes near coords,
nodes near coords align={center},
only marks,% supprime la ligne reliant les points
no marks,%supprime les points
point meta={explicit symbolic},% métadonnée : non numérique
]
table [meta=symbole]{donnees_affinite_elec.csv};
\end{axis}
\end{tikzpicture}
\end{document}

La classe de document est standalone, mais cela fonctionnerait aussi bien dans une autre classe de document. J’espère que les commentaires du code parlent d’eux-même.

Les dimensions du graphique (largeur, hauteur) sont à adapter à la taille de la police ainsi qu’à celle du marqueur. Si on préfère des marqueurs ronds :

Si on préfère que les marqueurs se touchent, on augmente leur taille.

Tous les colormaps créés ici sont séquentiels : une couleur s’intensifie à partir du blanc. À noter : les données sans valeurs numériques (‘NaN’ dans le fichier .csv) donnent lieu à des marqueurs incolores. Extrait de la documentation de pgfplots :


Et voici les fichiers à télécharger :

Codes Python :

Les fichiers .csv de données :

Et pour finir les codes LaTeX :


Amélioration :

Voici un dataset (jeu de données) des éléments chimiques, dans un fichier .csv. Il contient, pour les 118 éléments connus actuellement, les données par colonne :

  • numéro atomique
  • numéro de colonne dans le tableau périodique (format semi-long, avec les lanthanides et actinides séparés)
  • numéro de ligne
  • bloc (‘s’, ‘p’, ‘d’, ‘f’)
  • nom français
  • nom anglais
  • masse atomique
  • configuration électronique
  • configuration électronique (sous-couche en cours de remplissage)
  • électronégativité (Pauling)
  • affinité électronique (eV)
  • rayon atomique
  • rayon de Van der Waals
  • densité
  • abondance dans la croûte terrestre
  • abondance dans la mer
  • abondance dans l’Univers
  • température de fusion
  • température d’ébullition
  • polarisabilité
  • enthalpie de fusion
  • enthalpie de vaporisation
  • enthalpie de formation
  • conductivité thermique

Pour les unités des données numériques qui en ont, je vous renvoie à la documentation de la bibliothèque mendeleev pour Python.

Lorsqu’une donnée est indisponible, ‘NaN’ est inséré. Certains logiciels peuvent gérer cette information ; sinon il est possible de chercher/remplacer ‘NaN’ par autre chose à l’aide d’un logiciel éditeur de texte (comme Gedit sur Linux , ou Bloc-Notes sur Windows). Les colonnes sont séparées par une Tabulation.

Avec un tel jeu de données, il est possible de générer tous les graphiques précédents en sélectionnant la colonne de la grandeur qui nous intéresse.

Après avoir exécuté une commande “\addplot” pour les marqueurs colorés, puis une deuxième pour les symboles, on peut faire une troisième commande pour afficher la valeur dans la case de l’élément :

En fonction de la taille de la police choisie pour les symboles et celle pour les données numériques, il faut adapter la taille du marqueur, les dimensions du graphique, et le décalage vertical entre le symbole (surélevé par rapport au marqueur), et la donnée numérique (abaissée). Ces différents réglages par tâtonnements permettent aussi d’ajuster l’intervalle entre les cases. Pour faciliter un peu les réglages, j’ai créé une longueur LaTeX (\newlength) pour le marqueur, en début de code.

\documentclass[border=0.5cm,11pt,convert={ghostscript,outext=.png,density=800x800}]{standalone}%conversion en png, en ligne de commande, avec ghostscript

%Auteur : David Alberto (www.astrolabe-science.fr)
%COMPILé AVEC XeLaTeX, PDFLaTeX

% se reporter au manuel PGFPLOTS section 3.4 et 5.3 (colormaps)

\usepackage{fontspec}% avec XeLaTeX (utilisation d'une police installée)
\usepackage[T1]{fontenc}
\usepackage[x11names,dvipsnames,svgnames]{xcolor}
\colorlet{vert}{OliveGreen!70!black}
\colorlet{rouge}{red!80!black}
\colorlet{monorange}{yellow!50!red}
%\usepackage{ebgaramond}% éventuelle police dispo avec un package
\usepackage{lmodern}
\usepackage{tikz}
\usepackage{pgfplots}\pgfplotsset{compat=newest}
\pgfplotsset{/pgf/number format/.cd,1000 sep={~}}
\usetikzlibrary{plotmarks}
\usepackage{siunitx}
\usepackage{comment}

\begin{comment}
Ce script permet de disposer les éléments chimiques dans un tableau périodique, en accédant aux données du fichier annexe 'donnees_elements_chimiques.csv'.
Le fichier donne les données suivantes en colonnes :
'col': numéro de colonne dans le tableau périodique
'lig': numéro de ligne dans le tableau périodique
'symbole'
bloc'
francais': nom en francais,
english' : nom en anglais
masse_atomique'
config_elec' : configuration électronique
'electronegativite' : electronégativité (Pauling),
'rayon_atomique' : en pm
'rayon_vdw' : en pm
'densite'
'abondance_croute' : abondance dans la croûte terrestre (mg/kg)
'abondance_mer' : abondance dans la mer (mg/L)
'abondance_univers' : abondance dans l'univers (%)
'T_fus' : Température de fusion (°C)
'T_ebu' : Température d'ébullition (°C)
'polarisabilite'
'enthalpie_fus' : (kJ/mol)
'enthalpie_vap' : (kJ/mol)
'enthalpie_form' : (kJ/mol)
'conduct_therm' : conductivité thermique (W/(mK))

Les données viennent de la bibliothèque Python "mendeleev".
Voir la documentation https://mendeleev.readthedocs.io/en/stable/data.html
sauf Abondance univers (sciencenotes.org)
\end{comment}

% choix éventuel de police, selon ce qui est installé sur le poste :
%\setmainfont{Linux Libertine O}% avec XeLaTeX
\setmainfont{Laksaman}% avec XeLaTeX

\colorlet{macouleur}{SlateBlue4}% couleur du colormap
\newlength{\taillemarqueur}
\setlength{\taillemarqueur}{15pt}% taille du marqueur coloré

\begin{document}

%\sffamily

\begin{tikzpicture}
\begin{axis}[
y dir=reverse,% inversion axe y (périodes)
xmin=0,xmax=18,
ymin=1,ymax=10,
%colorbar,% verticale à gauche, par défaut
colorbar horizontal,
colormap={mycmap}{color(0cm)=(white); color(1cm)=(macouleur)},
width=600pt,% environ \taillemarqueur fois 18 colonnes, pour les proportions, plus espace
height=300pt,% environ \taillemarqueur fois 10 lignes, pour les proportions, plus espace
scale only axis,
%title=\Large{Température de fusion (\si{\degreeCelsius})},
title=\Huge{Rayon atomique (pm)},
hide axis,
colorbar style={xshift=\taillemarqueur},
]
%
% affichage des carrés colorés :
\addplot [%
scatter,
only marks,
%mark=*,% marqueur rond plein
mark=square*,% marqueur carré plein
scatter src=\thisrow{rayon_atomique},% couleur des carrés donnée par la colonne de la grandeur choisie.
scatter/use mapped color={draw opacity=0,fill=mapped color},%supprime le contour du marqueur
mark options={mark size=\taillemarqueur,% adapté aux dimensions
},
]
table [col sep=tab,x=col,y=lig]{donnees_elements_chimiques.csv};
%
%affichage des symboles :
\addplot [nodes near coords,
nodes near coords align={center},
nodes near coords style={yshift=3pt,font=\large},% réglage affichage
only marks,% supprime la ligne reliant les points
no marks,%supprime les points
point meta={explicit symbolic},% métadonnée : non numérique
]
table [x=col,y=lig, meta=symbole, col sep=tab,]{donnees_elements_chimiques.csv};
%
%affichage des valeurs :
\addplot [nodes near coords,
nodes near coords align={below},
nodes near coords style={yshift=-4pt,font=\scriptsize},% réglage affichage
only marks,% supprime la ligne reliant les points
no marks,%supprime les points
point meta={\thisrow{rayon_atomique}},% métadonnée : grandeur voulue
]
table [x=col,y=lig, col sep=tab]{donnees_elements_chimiques.csv};
\end{axis}
\end{tikzpicture}
\end{document}

On peut également indiquer une couleur de marqueur optionnelle selon le contenu de la colonne ‘bloc’ :

Ceci nécessite des réglages particuliers ; j’en ai donc fait un script distinct des autres :

Le graphique suivant demande plus de travail d’ajustement des dimensions et tailles de police, si on veut que la configuration électronique des éléments les plus lourds tienne dans la case :

C’est pourquoi j’ai mis dans le dataset une colonne ‘config_elec_fin’ qui donne la fin de la configuration, plus précisément la sous-couche en cours de remplissage (en tenant compte des irrégularités du bloc d, mais sans afficher le bloc f, dont les irrégularités ne sont pas corrigées) :

Voici un tableau périodique “classique”, sans colormap, avec 5 commandes \addplot :

Soyez le premier à commenter

Laisser un commentaire