Entrées-sorties#

Commencer une ligne avec ! vous donne accès aux commandes du shell. Par exemple, ! ls (pour Linux ou Mac) ou ! dir (pour Windows) permet d’afficher les fichiers présents dans votre répertoire de travail :

! ls
01-dictionnaires-pandas.ipynb  banksy.txt	  exoplanets.csv
02-logique-filtrage.ipynb      europe.csv	  exoplanets.dat
03-iteration.ipynb	       europe.pkl	  test.txt
04-fonctions.ipynb	       exo-write.txt	  verne-lune.txt
05-entrees-sorties.ipynb       exoplanets-SI.dat

Vidéo: Lire et écrire#

Lire un fichier texte#

Nous avons extrait un passage du livre De la Terre à la Lune, trajet direct en 97 heures 20 minutes, paru en 1865 et écrit par Jules Verne (1828-1905). Il est disponible librement sur le site du Projet Gutenberg. Cet extrait est enregistré dans le fichier nommé verne-lune.txt.

Pour pouvoir importer le texte dans Python, il faut d’abord ouvrir le fichier avec la fonction open() :

# Ouverture du fichier texte
file = open('verne-lune.txt', mode = 'r', encoding = 'utf-8')

Nous avons donné à l’argument mode la valeur r pour ouvrir le fichier seulement en lecture et pas en écriture (read-only). Cela permet d’empêcher toute modification du fichier. Afin de lire tout le fichier, on peut utiliser la méthode .read() sur la variable file :

# Lecture du fichier
print(file.read())
                                  _Longs's-Peak, 12 décembre._

A MM. LES MEMBRES DU BUREAU DE L'OBSERVATOIRE DE CAMBRIDGE.

_Le projectile lancé par la Columbiad de Stone's-Hill a été aperçu par
MM. Belfast et J.-T. Maston, le 12 décembre, à huit heures
quarante-sept minutes du soir, la Lune étant entrée dans son dernier
quartier.

Ce projectile n'est point arrivé à son but.  Il a passé à côté, mais
assez près, cependant, pour être retenu par l'attraction lunaire.

Là, son mouvement rectiligne s'est changé en un mouvement circulaire
d'une rapidité vertigineuse, et il a été entraîné suivant une orbite
elliptique autour de la Lune, dont il est devenu le véritable
satellite.

Les éléments de ce nouvel astre n'ont pas encore pu être déterminés.
On ne connaît ni sa vitesse de translation, ni sa vitesse de rotation.
La distance qui le sépare de la surface de la Lune peut être évaluée à
deux mille huit cent trente-trois milles environ (-- 4,500 lieues).

Maintenant, deux hypothèses peuvent se produire et amener une
modification dans l'état des choses:

Ou l'attraction de la Lune finira par l'emporter, et les voyageurs
atteindront le but de leur voyage;

Ou, maintenu dans un ordre immutable, le projectile gravitera autour
du disque lunaire jusqu'à la fin des siècles.

C'est ce que les observations apprendront un jour, mais jusqu'ici la
tentative du Gun-Club n'a eu d'autre résultat que de doter d'un nouvel
astre notre système solaire._

                                                    J.-M. BELFAST.

Une fois fini, il faut libérer la mémoire en fermant le fichier. On peut vérifier si le fichier est ouvert ou fermé en affichant l’attribut closed de l’objet file :

# Vérifie si le fichier est fermé
print(file.closed)
False

Le fichier n’est pas fermé. On peut le fermer avec la méthode .close() :

# Fermeture du fichier
file.close()

# Vérifie si le fichier est fermé
print(file.closed)
True

On peut aussi ne lire qu’une seule ligne avec la méthode .readline(). Si on utilise de nouveau cette méthode, alors Python lit la ligne suivante :

# Ouverture du fichier texte
file = open('verne-lune.txt', mode = 'r', encoding = 'utf-8')

# Lecture des 3 premières lignes
print(file.readline())
print(file.readline())
print(file.readline())

# Fermeture du fichier
file.close()
                                  _Longs's-Peak, 12 décembre._



A MM. LES MEMBRES DU BUREAU DE L'OBSERVATOIRE DE CAMBRIDGE.

Exercice#

  1. Ouvrir le fichier banksy.txt en lecture seul

  2. Afficher le fichier

  3. Fermer le fichier

Hide code cell source
# Ouverture du fichier texte
file = open('banksy.txt', mode = 'r', encoding = 'utf-8')

# Lecture du fichier
print(file.read())

# Fermeture du fichier
file.close()
                        .s$$$Ss.
            .8,         $$$. _. .              ..sS$$$$$"  ...,.;
 o.   ,@..  88        =.$"$'  '          ..sS$$$$$$$$$$$$s. _;"'
  @@@.@@@. .88.   `  ` ""l. .sS$$.._.sS$$$$$$$$$$$$S'"'
   .@@@q@@.8888o.         .s$$$$$$$$$$$$$$$$$$$$$'
     .:`@@@@33333.       .>$$$$$$$$$$$$$$$$$$$$'
     .: `@@@@333'       ..>$$$$$$$$$$$$$$$$$$$'
      :  `@@333.     `.,   s$$$$$$$$$$$$$$$$$'
      :   `@33       $$ S.s$$$$$$$$$$$$$$$$$'
      .S   `Y      ..`  ,"$' `$$$$$$$$$$$$$$
      $s  .       ..S$s,    . .`$$$$$$$$$$$$.
      $s .,      ,s ,$$$$,,sS$s.$$$$$$$$$$$$$.
      / /$$SsS.s. ..s$$$$$$$$$$$$$$$$$$$$$$$$$.
     /`.`$$$$$dN.ssS$$'`$$$$$$$$$$$$$$$$$$$$$$$.
    ///   `$$$$$$$$$'    `$$$$$$$$$$$$$$$$$$$$$$.
   ///|     `S$$S$'       `$$$$$$$$$$$$$$$$$$$$$$.
  / /                      $$$$$$$$$$$$$$$$$$$$$.
                           `$$$$$$$$$$$$$$$$$$$$$s.
                            $$$"'        .?T$$$$$$$
                           .$'        ...      ?$$#\
                           !       -=S$$$$$s
                         .!       -=s$$'  `$=-_      :
                        ,        .$$$'     `$,       .|
                       ,       .$$$'          .        ,
                      ,     ..$$$'
                          .s$$$'                 `s     .
                   .   .s$$$$'                    $s. ..$s
                  .  .s$$$$'                      `$s=s$$$
                    .$$$$'                         ,    $$s
               `   " .$$'                               $$$
               ,   s$$'                              .  $$$s
            ` .s..s$'                                .s ,$$
             .s$$$'                                   "s$$$,
          -   $$$'                                     .$$$$.
        ."  .s$$s                                     .$',',$.
        $s.s$$$$S..............   ................    $$....s$s......
         `""'           `     ```"""""""""""""""         `""   ``

Gestionnaire de contexte#

Le flux d’instructions introduit précédemment peut poser problème : si une erreur intervient avant la fermeture du fichier (avec la méthode .close()), celui-ci ne sera pas fermé et des données pourraient être perdues.

La meilleure manière de faire est d’utiliser un gestionnaire de contexte, que l’on utilise avec l’instruction with de la façon suivante :

# Ouverture et lecture du fichier avec un gestionnaire de contexte
with open('verne-lune.txt', mode = 'r', encoding = 'utf-8') as file:
    print(file.readline())
                                  _Longs's-Peak, 12 décembre._

La fermeture du fichier est alors implicite. On peut aussi écrire dans un fichier avec la méthode .write(). Pour cela, il faut ouvrir le fichier avec le mode 'w' (write) :

# Ouverture et écriture dans un fichier avec un gestionnaire de contexte
with open('test.txt', mode = 'w') as file:
    file.write('Je sais écrire dans un fichier !\nSuper !')

Le caractère spécial \n dans la chaîne de caractères permet le retour à la ligne.

Il existe de nombreux modes d’ouverture d’un fichier, les principaux étant :

Caractère

Signification

'r'

ouvre en lecture (par défaut)

'w'

ouvre en écriture, tronquant le fichier

'x'

ouvre pour une création exclusive, échouant si le fichier existe déjà

'a'

ouvre en écriture, ajoutant à la fin du fichier s’il existe

Exercice#

En utilisant un gestionnaire de contexte et une boucle for :

  1. Afficher la ligne 13 du fichier verne-lune.txt. Pour cela il faut exécuter 12 fois la méthode .readline() sans print(), puis une treizième fois avec print()

  2. Créez un fichier nommé exo-write.txt dans lequel vous écrirez tous les entiers entre 1 et 100 (avec un entier par ligne)

Hide code cell source
# 1. Ouverture et lecture du fichier avec un gestionnaire de contexte
with open('verne-lune.txt', mode = 'r', encoding = 'utf-8') as file:
    for i in range(12):
        file.readline()
    print(file.readline())
Là, son mouvement rectiligne s'est changé en un mouvement circulaire
Hide code cell source
# 2. Ouverture et écriture dans un fichier avec un gestionnaire de contexte
with open('exo-write.txt', mode = 'w') as file:
    for i in range(1, 101):
        file.write(str(i) + '\n')

Vidéos: Outils supplémentaires#

Lire le clavier#

La fonction input() lit tout ce qui est écrit au clavier comme une chaîne de caractères :

# Lire ce qui est écrit au clavier (finir avec la touche entrée)
chaine = input("Entrez votre message avec input:\n")

# Afficher ce qui a été écrit
print("Vous avez entré :", chaine)
print(type(chaine))
---------------------------------------------------------------------------
StdinNotImplementedError                  Traceback (most recent call last)
Cell In[14], line 2
      1 # Lire ce qui est écrit au clavier (finir avec la touche entrée)
----> 2 chaine = input("Entrez votre message avec input:\n")
      4 # Afficher ce qui a été écrit
      5 print("Vous avez entré :", chaine)

File /opt/hostedtoolcache/Python/3.8.18/x64/lib/python3.8/site-packages/ipykernel/kernelbase.py:1269, in Kernel.raw_input(self, prompt)
   1267 if not self._allow_stdin:
   1268     msg = "raw_input was called, but this frontend does not support input requests."
-> 1269     raise StdinNotImplementedError(msg)
   1270 return self._input_request(
   1271     str(prompt),
   1272     self._parent_ident["shell"],
   1273     self.get_parent("shell"),
   1274     password=False,
   1275 )

StdinNotImplementedError: raw_input was called, but this frontend does not support input requests.

Exercice#

  1. Avec la fonction input(), demander à l’utilisateur d’entrer un entier au clavier et affecter la valeur à une variable sn

  2. Convertir la variable sn en entier avec la fonction int(), et affecter la valeur à une variable n

  3. Afficher le carré de n à l’écran

Hide code cell source
# Entrée au clavier
sn = input("Entrer un entier :\n")

# Conversion en entier
n = int(sn)

# Afficher le carré de n
print(n * n)
---------------------------------------------------------------------------
StdinNotImplementedError                  Traceback (most recent call last)
Cell In[15], line 2
      1 # Entrée au clavier
----> 2 sn = input("Entrer un entier :\n")
      4 # Conversion en entier
      5 n = int(sn)

File /opt/hostedtoolcache/Python/3.8.18/x64/lib/python3.8/site-packages/ipykernel/kernelbase.py:1269, in Kernel.raw_input(self, prompt)
   1267 if not self._allow_stdin:
   1268     msg = "raw_input was called, but this frontend does not support input requests."
-> 1269     raise StdinNotImplementedError(msg)
   1270 return self._input_request(
   1271     str(prompt),
   1272     self._parent_ident["shell"],
   1273     self.get_parent("shell"),
   1274     password=False,
   1275 )

StdinNotImplementedError: raw_input was called, but this frontend does not support input requests.

Format défini#

Dans l’exemple suivant nous affichons les valeurs des variables x et n avec un format défini. La syntaxe est similaire à celle d’autres langages comme Matlab ou C :

# Création des variables
x = 45.9876
n = 6

# Affichage avec un format défini
print('x = %5.3f, n = %d' % (x, n))
print('x = %5.3e, n = %5d' % (x, n))
x = 45.988, n = 6
x = 4.599e+01, n =     6

%5d

  • 5 est le nombre de caractères imprimés, y compris les espaces blancs

  • d indique un entier

%5.3f

  • 5 est le nombre total de caractères imprimés, y compris le signe - si le chiffre est négatif, ou tout autre caractère. Cependant le nombre de caractères imprimés sera supérieur à ce chiffre si il est trop petit pour pouvoir atteindre la pécision requise dans l’affichage : dans l’exemple donné, le nombre total de caractères imprimés est 6

  • 3 est le nombre de chiffres après le point décimal (6 par défaut)

  • f donne le type, ici le format fixe pour un réel

%5.3e

  • la seule différence est e qui donne le type d’affichage exponentiel

Une syntaxe alternative a été introduite depuis Python 2.6 avec la méthode .format() des chaînes de caractères :

print('x = {:f}, n = {}'.format(x, n))
print('x = {:8.3f}, n = {:d}'.format(x, n))
x = 45.987600, n = 6
x =   45.988, n = 6

Un bon endroit pour découvrir toutes les possibilités offertes par le formatage en Python, dans les deux styles d’écriture, est le site pyformat.

Lire et écrire des tableaux Numpy#

Pour importer les données d’un fichier texte dans un tableau Numpy on peut utiliser la fonction loadtxt(). Chaque ligne du fichier doit avoir le même nombre de valeurs. Par défaut les lignes commençant par # ne sont pas lues. Elles sont généralement utilisées pour décrire les données dans l’en-tête du fichier.

Nous disposons d’un fichier exoplanets.dat qui décrit les caractéristiques de 256 exoplanètes. Ces données sont tirées du site internet exoplanets.org. Chaque ligne du fichier caractérise une exoplanète et l’étoile autour de laquelle elle gravite. Les quatre colonnes donnent :

  1. le demi-grand axe de l’orbite parcourue par la planète en unités astronomiques ;

  2. la période orbitale en jours ;

  3. la masse de la planète en unités de masse de Jupiter ;

  4. la masse de l’étoile autour de laquelle la planète gravite en unités de masse solaire.

Importons les données dans la variable data grâce à la fonction loadtxt() de Numpy :

# Importation du module numpy
import numpy as np

# Importation des données
data = np.loadtxt('exoplanets.dat')

Nous vérifions que data est bien un tableau Numpy de forme (256, 4) :

print(type(data))
print(data.shape)
<class 'numpy.ndarray'>
(256, 4)

Plutôt que de créer un tableau 2D, il est possible de créer 4 tableaux 1D contenant les données de chaque colonne directement avec l’argument unpack :

a, p, mp, me = np.loadtxt('exoplanets.dat', unpack = True)

Nous voulons changer les unités des données en unités S.I. et sauvegarder les nouvelles données dans un nouveau fichier. D’abord, changeons les unités :

# Set constants
au2m = 149597870700. # Conversion from astronomical units to m
day2seconds = 24. * 60. * 60. # Conversion from day to seconds
M_jupiter = 1.898e27 # Jupiter mass in kg
M_sun = 1.9891e30 # Solar mass in kg

# Change units to SI
a_SI = a * au2m
p_SI = p * day2seconds
mp_SI = mp * M_jupiter
me_SI = me * M_sun

On peut ensuite sauvegarder les nouvelles données dans le fichier exoplanets-SI.dat avec la fonction Numpy savetxt() :

np.savetxt('exoplanets-SI.dat', np.c_[a_SI, p_SI, mp_SI, me_SI], fmt = '%10.3e')

On a utilisé la commande Numpy c_[] pour concaténer en colonnes les tableaux 1D. L’argument fmt permet de spécifier le format d’écriture, avec la même syntaxe vue plus haut pour print.

Importer des données mixtes avec Pandas#

Il arrive souvent que les données à importer soient de types différents. Il n’est pas très pratique de les importer avec Numpy. On peut alors utiliser le module Pandas. Nous avons sauvegardé un fichier plus complet contenant les informations de 3236 exoplanètes, sous la forme d’un fichier csv. La signification des différentes colonnes est expliquée sur le site exoplanets.org. Ces colonnes contiennent à la fois des données numériques et du texte. On ne peut pas utiliser la fonction loadtxt().

Comme expliqué dans le module Dictionnaires et Pandas, on utilise la fonction read_csv() pour importer et créer un DataFrame :

# Importer le module pandas
import pandas as pd

# Importer les données dans un DataFrame
data = pd.read_csv('exoplanets.csv')

Nous vérifions que les données ont bien été importées :

# Affichage des 3 premières lignes
data.head(n = 3)
NAME MSINI A PER ECC OM T0 K ORBREF ORBURL FIRSTREF FIRSTURL
0 Kepler-107 d NaN 0.078010 7.958203 NaN 90.0 2.454971e+06 NaN Rowe 2014 http://adsabs.harvard.edu/abs/2014arXiv1402.6534R Rowe 2014 http://adsabs.harvard.edu/abs/2014arXiv1402.6534R
1 Kepler-1049 b NaN 0.034472 3.273461 0.0 90.0 NaN NaN Morton 2016 http://adsabs.harvard.edu/abs/2016ApJ...822...86M Morton 2016 http://adsabs.harvard.edu/abs/2016ApJ...822...86M
2 Kepler-813 b NaN 0.137610 19.129473 0.0 90.0 NaN NaN Morton 2016 http://adsabs.harvard.edu/abs/2016ApJ...822...86M Morton 2016 http://adsabs.harvard.edu/abs/2016ApJ...822...86M

On voit que les données manquantes ont été remplacées par NaN (Not-a-Number).

On peut maintenant utiliser ces données, par exemple pour savoir combien de planètes ont été reportées pour la première fois dans la référence Morton 2016 :

# Nombre de planètes dans Morton 2016
print(np.sum(data['FIRSTREF'] == 'Morton 2016'))
1283

Remarquez que dans cette commande on a sommé les éléments d’un objet Series contenant des booléens. Dans ce cas, la valeur True est considérée comme un 1, et la valeur False comme un 0.