1. Test technique

1.1. Le contexte

Ce cours suppose les bases du langage Python maitrisées. Le test technique ci dessous est là pour s’en assurer.

Ce test couvre la lecture de données, les principales structures de données, l’organisation du code en fonctions et les doctests. Le réussir atteste que vous savez manipuler les éléments essentiels du langage.

Chaque fonction doit être évaluée par les doctests associés. Vous devez obtenir 100 % en 03:00 maximum. Si ce n’est pas le cas, les compétences de base doivent être renforcées.

1.2. Le sujet

1.2.1. Les communes en France

En France, la commune est à la fois une collectivité territoriale et une circonscription administrative.

La réforme des collectivités territoriales de 2010 oblige chaque commune à adhérer à un Etablissement Public de Coopération Intercommunale, qui peut être :

1.2.2. Les données

Les données sont produites par l”INSEE et enrichies et mises en ligne par opendatasoft. Elles sont disponibles dans le fichier population.csv. Ce fichier est encodé en utf8.

Les champs sont désignés ci dessous :

  • Code Officiel Région ;

  • Nom Officiel Région ;

  • Code Officiel Département ;

  • Code Officiel Arrondissement Départemental ;

  • Code Officiel Commune / Arrondissement Municipal ;

  • Nom Officiel Commune / Arrondissement Municipal ;

  • Population municipale ;

  • Population comptée à part ;

  • Population totale ;

  • Année de recensement ;

  • Année d’entrée en vigueur ;

  • Année de référence géographique ;

  • Nom Officiel EPCI ;

  • Code Officiel EPCI ;

  • Nom Officiel Département.

L’objectif est d’écrire un ensemble de fonctions permettant de lire et manipuler ces données. On utilisera le fichier squelette population.py.

1.2.3. Lecture des données

Ecrire une fonction read_file() :

  • qui prend en argument un nom de fichier sous forme de chaîne de caractères

  • et retourne une list de dict, un par ligne de données.

Jeter un oeil aux doctests pour des exemples d’appel/retour de la fonction.

Quelques indications :

  • il sera très pratique d’utiliser la classe DictReader du module csv ;

  • lors de l’ouverture du fichier, on portera une attention particulière à l’encodage du fichier csv ;

  • lors de la lecture, on portera une attention particulière au délimiteur utilisé.

Utiliser la fonction main() pour la mise au point, puis valider la fonction en exécutant les doctests associés.

1.2.4. Construire la liste des départements

Ecrire une fonction build_list_departements() :

  • qui prend en argument la liste de dictionnaires retournée par read_file()

  • et retourne la list ordonnée des tuple composés de la façon suivante :
    • Code Officiel Département (str) ;

    • Nom Officiel Département (str).

Jeter un oeil aux doctests pour des exemples d’appel/retour de la fonction.

Quelques indications :

  • une structure de données est particulièrement appropriée pour construire une liste sans doublons ;

  • pour certaines lignes, le département n’est pas renseigné ;

  • la fonction sorted() est utile pour trier une séquence.

Utiliser la fonction main() pour la mise au point, puis valider la fonction en exécutant les doctests associés.

1.2.5. Construire la liste des communes

De façon similaire, écrire une fonction build_list_communes() :

  • qui prend en argument la liste de dictionnaires retournée par read_file()

  • et retourne, pour les communes incluses dans les données, la list ordonnée des tuple composés de la façon suivante :
    • Code Officiel Commune / Arrondissement Municipal (str);

    • Nom Officiel Commune / Arrondissement Municipal (str).

Jeter un oeil aux doctests pour des exemples d’appel/retour de la fonction.

Quelques indications :

  • une structure de données est particulièrement appropriée pour construire une liste sans doublons ;

  • la fonction sorted() est utile pour trier une séquence ;

  • on peut passer une clé de tri multi critères à la fonction sorted().

Utiliser la fonction main() pour la mise au point, puis valider la fonction en exécutant les doctests associés.

1.2.6. Rechercher la population d’une commune

Ecrire une fonction get_pop_commune() :

  • qui prend en argument la liste de dict retournée par read_file() et le code de la commune (str) ;

  • et retourne un tuple composé de la façon suivante :
    • nom de la commune (str) ;

    • population totale de la commune (int), pour l’année de recensement la plus récente ;

    • année de recensement la plus récente (str).

Jeter un oeil aux doctests pour des exemples d’appel/retour de la fonction.

Quelques indications :

  • si la commune n’existe pas (ou est mal orthographiée), la fonction ne retourne rien ;

  • les données dans le fichier ne sont pas forcément ordonnées ;

  • il sera judicieux de ne pas parcourir plus de données que nécessaire.

Utiliser la fonction main() pour la mise au point, puis valider la fonction en exécutant les doctests associés.

1.2.7. Construire un dictionnaire des départements

Ecrire une fonction build_dict_departements() :

  • qui prend en argument la liste de dictionnaires retournée par read_file() ;

  • et retourne un dict dont la clé est le département (str) et la valeur un tuple composé de la façon suivante :
    • nombre de communes du département (int) ;

    • population totale du département (int) lors de l’année de recensement la plus récente.

Jeter un oeil aux doctests pour des exemples d’appel/retour de la fonction.

Quelques indications :

  • les données réelles sont rarement (jamais ?) idéalement structurées. Ici l’ambigüité dans la désignation de la commune peut générer des erreurs dans les doctest. La source d’une éventuelle ambigüité :

    • un même code INSEE/EPCI peut être attribué à deux communes différentes. Par exemple, Roussac et Saint-Pardoux-le-Lac ont le même code INSEE (87128) ;

    • l’orthographe a pu évoluer entre deux années de recensement : Coeuvres-et-Valsery puis Cœuvres-et-Valsery, ou Les Trois Châteaux puis Les Trois-Châteaux ;

    • il y a pu y avoir des regroupements de communes entre deux années de recensement. Par exemple, la fusion des communes de Conilhac-de-la-Montagne et de Roquetaillade a donné naissance à la commune de Roquetaillade-et-Conilhac qui a le statut de commune nouvelle ;

  • selon la façon dont sont gérées les données ambigües, l’exécution des fonctions peut conduire à un résultat différent de ceux utilisés pour les doctests ;

  • un dictionnaire multi dimensionnel est une structure de données appropriée pour ce problème ;

    • avec le dict standard, il faut gérer la présence/absence de clés. En supposant que data est ici une liste de tuples

      d = dict()
      for dpt, commune, annee, pop in data:
          if dpt not in d.keys(): d[dpt] = dict()
          if commune not in d[dpt].keys(): d[dpt][commune] = dict()
          if annee not in d[dpt][commune].keys(): d[dpt][commune][annee] = dict()
          d[dpt][commune][annee] = pop
      
    • l’utilisation d’un defaultdict à plusieurs dimensions facilite le travail:

      from collections import defaultdict
      def multi_dict(K, type):
          if K == 1:
              return defaultdict(type)
          else:
              return defaultdict(lambda: multi_dict(K-1, type))
      
      md = multi_dict(3, str)
      for dpt, commune, annee, pop in data:
          md[dpt][commune][annee] = pop # si la clé n'existe pas, un :class:`~collections.defaultdict` est créé
      

Utiliser la fonction main() pour la mise au point, puis valider la fonction en exécutant les doctests associés.

1.2.8. Extraire les données par département

Ecrire une fonction stat_by_dpt() :

  • qui prend en argument le dictionnaire des départements (retourné par build_dict_departements()) et le département d’intérêt (str) ;

  • et retourne un tuple dont le premier élément est le nombre de communes du département (int) et le second élément la population totale du département (int).

Utiliser la fonction main() pour la mise au point, puis valider la fonction en exécutant les doctests associés.