Un tableau périodique simple – Python

Avec données extraites d’un fichier .csv, et affichage optionnel des familles et des blocs.

Remarque : voir aussi cet autre article, pour une autre version d’un tableau périodique, sans jeu de données mais avec le module mendeleev.


Après avoir cherché longtemps des tableaux périodiques à la fois dépouillés et en français pour mes élèves de Seconde, j’en suis arrivé à le faire moi-même, avec un petit code Python.

La méthode est la suivante :

  • entrer dans un fichier .csv la liste des éléments chimiques par ligne, avec en colonnes les données utiles : numéros de ligne et de colonne dans la classification (pour servir de coordonnées), nom, numéro atomique.
  • écrire un code Python qui lit les données du fichier .csv (module Pandas)
  • parcourir la liste des éléments en affichant dans un graphe (module Matplotlib) ces données utiles.

La première étape serait la plus fastidieuse ; heureusement on peut compter sur des personnes généreuses qui mettent à disposition des jeux de données, comme sur le site www.datastro.eu/ (onglet “Chimie”)

On peut télécharger un fichier assez complet, avec des données physiques et chimiques par éléments (voir leur mise en forme dans cet autre article). Ici, on n’aura besoin que du nom de l’élément et de son numéro atomique. J’ai ajouté moi-même deux colonnes donnant les numéros de ligne et de colonne dans le tableau périodique, ce qui est assez rapide. J’ai aussi traduit les noms des éléments en français.

Dans ce même code, j’ai inséré une option pour tracer les limites des 4 principales familles d’éléments. Ce sont des tracés de rectangles colorés, avec ajout manuel de légendes. Sur matplotlib, les rectangles sont des “patches” pour lesquels il faut importer une fonctionnalité spécifique en début de code. Afin que les rectangles soient légèrement transparents, j’ai réglé l’opacité à 0.5 (le paramètre “alpha”), ce qui m’a contraint à importer “mcolors”.

Le début du code :


import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
from matplotlib import colors as mcolors#pour la fonction to_rgba() : rectangles avec alpha différent de 1
import pandas as pd

#%%%%%%%%%%%%%%%%%%%%%%%%%%%
a=6#espace horizontal
b=6#espace vertical

blocs=False#afficher les limites des blocs
familles=False#afficher les limites des familles

Les paramètres “a” et “b” sont les espaces entre deux éléments consécutifs. Les booléens “blocs” et “familles” permettent de choisir les options en début de code.


#extraction des données :
table=pd.read_csv("periodic-table.csv", 
delimiter=";", 
skiprows=2, #ne lit pas les deux premières lignes
#nrows=18,#permet de limiter à quelques périodes. Mais adapter les dimensions
usecols=[0, 1, 2, 3, 4], #sélectionne les colonnes utiles
names=['numéro atomique', "symbole", 'nom', 'ligne', 'colonne'], #renommer les colonnes
)
Z=table["numéro atomique"]
nom=table["nom"]
symb=table["symbole"]
ligne=table["ligne"]
colonne=table["colonne"]

Quand on extrait des données d’un fichier .csv, je trouve préférable de modifier au minimum le fichier d’origine. Ici, par exemple, je personnalise les noms des colonnes dans le code, ce qui ne modifie pas le contenu du fichier .csv.

Ensuite, je définis un graphique avec ses paramètres :


#paramètres du graphique
fig, ax=plt.subplots(figsize=(12,7), 
tight_layout=True, #marges réduites
)
#ax.set_aspect('equal')#pas indispensable
plt.title("Le tableau périodique des éléments chimiques", fontsize=18)
plt.xticks([])
plt.yticks([])
#plt.axis('off')#retire le cadre du graphe
plt.xlim(0, 19*a)#à adapter aux dimensions choisies pour la figure
plt.ylim(-9*b, 0)# à adapter au nombre de périodes représentées

#tracé des cases :
for i in range(len(table)):
    if int(Z[i])<57 or int(Z[i])>71:#supprime les lanthanides
        if int(Z[i])<89 or int(Z[i])>103:#supprime les actinides
            plt.text(a*colonne[i], -b/2-b*ligne[i], symb[i], fontsize=13, va='bottom', ha='center')#symbole
            plt.text(a*colonne[i]-0.15*a, -b/2-b*(ligne[i]+0.1),int(Z[i]), fontsize=7, ha='right')#N° atomique
            plt.text(a*colonne[i], -b*(ligne[i]+0.15),nom[i], fontsize=6, ha='center')#nom

#n° des colonnes :-------------------------------
for i in range(1, 19):
    plt.text(a*(i-0.2), -3, "%s"%(i), fontsize=8)
#------------------------------------------------------------------------------------

Le tracé des limites des familles :

if familles==True:
    coul18=mcolors.to_rgba('orange', 0.5)#le dernier paramètre est alpha (opacité de 0 à 1)
    #ici : tracer un rectangle qui encadre les gaz nobles
    rectangle=mpatches.Rectangle((a*17.5, -b*8), a, b*7, fc=coul18, ec='k')
    ax.add_artist(rectangle)
    plt.text(a*4, -b*1.5, "gaz nobles", fontweight='bold', bbox=dict(facecolor=coul18))
    
    coul17=mcolors.to_rgba('gold', 0.5)
    #ici : tracer un rectangle qui encadre les halogènes
    rectangle=mpatches.Rectangle((a*16.5, -b*8), a, b*6, color=coul17, ec='k')
    ax.add_artist(rectangle)
    plt.text(a*4, -b*2.0, "halogènes", fontweight='bold', bbox=dict(facecolor=coul17))
    
    coul1=mcolors.to_rgba('royalblue', 0.5)
    #ici : tracer un rectangle qui encadre les alcalins
    rectangle=mpatches.Rectangle((0.5*a, -b*8), a, b*7, color=coul1, ec='k')
    ax.add_artist(rectangle)
    plt.text(a*4, -b*2.5, "alcalins", fontweight='bold', bbox=dict(facecolor=coul1))
    
    coul2=mcolors.to_rgba('yellowgreen', 0.5)
    #ici : tracer un rectangle qui encadre les alcalino-terreux
    rectangle=mpatches.Rectangle((1.5*a, -b*8), a, b*6, color=coul2, ec='k')
    ax.add_artist(rectangle)
    plt.text(a*4, -b*3.0, "alcalino-terreux", fontweight='bold', bbox=dict(facecolor=coul2))

Il vous sera facile de modifier les couleurs, et de supprimer ce dont vous n’avez pas besoin.

Le tracé optionnel des blocs :

if blocs==True:
    coulS=mcolors.to_rgba('orange', 0.5)#BLOC s
    rectangle=mpatches.Rectangle((a/2, -b*8), a, b*7, color=coulS, ec='k')#alcalins
    ax.add_artist(rectangle)
    rectangle=mpatches.Rectangle((1.5*a, -b*8), a, b*6, color=coulS, ec='k')#col2
    ax.add_artist(rectangle)
    rectangle=mpatches.Rectangle((17.5*a, -b*2), a, b, color=coulS, ec='k')#He
    ax.add_artist(rectangle)
    plt.text(4*a, -b*1.75, "bloc s", fontweight='bold', bbox=dict(fc=coulS))
    
    coulP=mcolors.to_rgba('darkslateblue',0.5)#BLOC p
    rectangle=mpatches.Rectangle((12.5*a, -b*8), 6*a, b*6, color=coulP, ec='k')
    ax.add_artist(rectangle)
    plt.text(6.5*a, -b*1.75, "bloc p", fontweight='bold', bbox=dict(fc=coulP))
    
    coulD=mcolors.to_rgba('greenyellow',0.5)#BLOC d
    rectangle=mpatches.Rectangle((2.5*a, -b*8), 10*a, b*4, color=coulD, ec='k')
    ax.add_artist(rectangle)
    plt.text(9.5*a, -b*1.75, "bloc d", fontweight='bold', bbox=dict(fc=coulD))

La fin du code crée un ou plusieurs fichiers image (png) et/ou pdf. J’ai pris l’habitude de définir un nom de fichier en incluant les options choisies (familles, blocs, ou simple). C’est facile, car le nom du fichier est une chaîne de caractères, que l’on peut concaténer selon les options, comme le montre cet exemple :

if blocs==True:
    NomFichier='TableauPerBlocs'
elif familles==True:
    NomFichier='TableauPerFamilles'
else: NomFichier='TableauPerSimple'
fig.savefig(NomFichier)
fig.savefig(NomFichier+'.png', dpi=400)
fig.savefig(NomFichier+'.pdf')

#plt.show()

Mise à jour avec une version qui :

  • affiche les masses molaires atomiques.
  • affiche les cases des éléments.
  • inclut le bloc d dans le repérage des blocs par couleurs.

Je souhaitais afficher les valeurs avec un nombre homogène de chiffres significatifs. N’ayant pas trouvé de fonction Python native satisfaisante, j’ai créé une fonction qui conserve 3 chiffres significatifs :

def chiffSignif(n):#3 chiffres significatifs
    n=n.replace('(','').replace(')','')
    n=float(n)
    if n<10:
        valeur='{:.2f}'.format(n)
    elif n<100:
        valeur= '{:.1f}'.format(n)
    else:
        valeur= '{:.0f}'.format(n)
    return valeur.replace('.',',')#virgule à la place du point

Remarque : voir aussi cet autre article, pour une autre version d’un tableau périodique, sans jeu de données mais avec le module mendeleev.

Soyez le premier à commenter

Laisser un commentaire