Skip to content

Commit

Permalink
Merge pull request #9 from tgrandje/dev
Browse files Browse the repository at this point in the history
Release 1.0.0
  • Loading branch information
tgrandje authored Jul 28, 2024
2 parents 0c28ff9 + 25803a1 commit 5ea36ea
Show file tree
Hide file tree
Showing 30 changed files with 2,013 additions and 34,613 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ jobs:
#----------------------------------------------
- name: Install dependencies
if: steps.cached-poetry-dependencies.outputs.cache-hit != 'true'
run: poetry install --no-interaction --without dev --extras "full"
run: poetry install --no-interaction --without dev

#----------------------------------------------
# add only pytest for tests (not the full dev dependencies to avoid installing spyder)
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ jobs:
#----------------------------------------------
- name: Install dependencies
if: steps.cached-poetry-dependencies.outputs.cache-hit != 'true'
run: poetry install --no-interaction --without dev --extras "full"
run: poetry install --no-interaction --without dev

#----------------------------------------------
# add only pytest for tests (not the full dev dependencies to avoid installing spyder)
Expand Down
73 changes: 28 additions & 45 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,20 +1,32 @@
# french-cities
Toolbox on french cities: set vintage, find departments, find cities...
This repo contains the documentation of the python french-cities package, a
package aimed at improving the referencing of municipalities in French 🇫🇷
datasets.

# Documentation

A full documentation with usecases is available at [https://tgrandje.github.io/french-cities/](https://tgrandje.github.io/french-cities/).
A full documentation with usecases is available at
[https://tgrandje.github.io/french-cities/](https://tgrandje.github.io/french-cities/).
Obviously, it is only available in french as yet.
Any help is welcome to build a multi-lingual documentation website.

Until then, a basic english documentation will stay available in the present README.

# Installation
# Why french-cities?

Do you have any data:
* which municipal locations are provided through approximate addresses, or via geographical 🗺️ coordinates?
* which municipalities are referenced by their postal codes and their labels 😮?
* which departments are written in full text 🔡?
* which spelling are dubious (for instance, torturing the _<del>Loire</del> Loir-et-Cher_) or obsolete
(for instance, referencing _Templeuve_, a city renamed as _Templeuve-en-Pévèle_ since 2015)?
* or compiled over the years and where cities' codes are a patchwork of multiple 🤯 vintages?

`pip install french-cities[full]`
**Then 'french-cities' is for you 🫵!**

Note that the "full" installation will also install geopy, which might use
Nominatim API for city recognition as a last resort.
# Installation

`pip install french-cities`

# Configuration

Expand All @@ -41,20 +53,9 @@ variables for `pynsee` and `geopy`.

## Basic usage

### Why french-cities?
There are already available packages and APIs meant to be used for basic french
cities management. For instance, `pynsee` uses the INSEE's API to retrieve
multiple data (including departement, region, ...). `geopy` can also retrieve
cities from their names using the BAN (Base Adresse Nationale) API or the
Nominatim geocoding service.

The difference is that `french-cities` is primarly meant to perform against whole
pandas series/dataframes. It should handle better performance than multiple API
calls and will optimize the call to each endpoints.

### Retrieve departements' codes
`french-cities` can retrieve departement's codes from postal codes or official
(COG/INSEE) codes.
`french-cities` can retrieve departement's codes from postal codes, official
(COG/INSEE) codes or labels.

Working from postal codes will make use of the BAN (Base Adresse Nationale)
and should return correct results. The case of "Cedex" codes is only partially
Expand All @@ -64,9 +65,9 @@ This consumes the freemium API and no authentication is included:
the user of the present package should check the current API's legal terms
directly on OpenDataSoft's website.

Working from official codes may give wrong results when working on an old
dataset and with cities which have changed of departments (which is rarely seen).
This is deliberate: it will use the first characters of the cities' codes
Working from official codes may sometime give empty results (when working on an old
dataset and with cities which have changed of departments, which is rarely seen).
This is deliberate: it will mostly use the first characters of the cities' codes
(which is a fast process and 99% accurate) instead of using an API (which is
lengthy though foolproof).

Expand All @@ -82,32 +83,14 @@ df = pd.DataFrame(
"deps": ["59", "977", "2A"],
}
)
df = find_departements(df, source="code_postal", alias="dep_A", type_code="postcode")
df = find_departements(df, source="code_commune", alias="dep_B", type_code="insee")
print(df)
```

One can also work directly from departement's names, using
`find_departements_from_names` instead :

```
from french_cities import find_departements_from_names
import pandas as pd
df = pd.DataFrame(
{
"deps": ["Corse sud", "Alpe de Haute-Provence", "Aisne", "Ain"],
}
)
df = find_departements_from_names(df, label="deps")
df = find_departements(df, source="code_postal", alias="dep_A", type_field="postcode")
df = find_departements(df, source="code_commune", alias="dep_B", type_field="insee")
df = find_departements(df, source="communes", alias="dep_C", type_field="label")
print(df)
```

For a complete documentation on `find_departements` or
`find_departements_from_names`, please type `help(find_departements)` or
`help(find_departements_from_names)`.
For a complete documentation on `find_departements`, please type `help(find_departements)`.

### Retrieve cities' codes
`french-cities` can retrieve cities' codes from multiple fields. It will work
Expand Down Expand Up @@ -256,4 +239,4 @@ Thomas GRANDJEAN (DREAL Hauts-de-France, service Information, Développement Dur
GPL-3.0-or-later

## Project Status
In production.
Stable.
46 changes: 30 additions & 16 deletions docs/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,27 +11,41 @@ nav_order: 3
## Ajout des clefs API INSEE

`french-cities` utilise `pynsee`, qui nécessite des cles API INSEE pour être
fonctionnel. Jusqu'à quatre clefs peuvent être spécifiées à l'aide de variables
d'environnement :
fonctionnel. Deux variables d'environnement doivent impérativement être spécifiées :

* insee_key
* insee_secret
* http_proxy (le cas échéant, pour accès web derrière un proxy professionnel)
* https_proxy (le cas échéant, pour accès web derrière un proxy professionnel)

Merci de se référer à
[la documentation de `pynsee`](https://pynsee.readthedocs.io/en/latest/api_subscription.html)
pour plus d'information sur les clefs API et la configuration.

A noter que la configuration des proxy par variable d'environnement sera
fonctionnelle pour à la fois pynsee et geopy.

## Gestion des sessions web
`pynsee` et `geopy` utilisent leurs propres gestionnaires de session web.

Ainsi, les objets `Session` passés en argument à french-cities ne seront
**PAS** partagés avec `pynsee` ou `geopy`.

Cela explique la possibilité de passer une session en argument alors même que
des proxy professionnels peuvent être spécifiés par variables d'environnement
pour `pynsee` et `geopy`).
Pour mémoire, il est tout à fait possible de fixer des variables d'environnement
depuis un environnement python, à l'aide des instructions suivantes :

```python
import os
os.environ["insee_key"] = "ma-clef-applicative"
os.environ["insee_secret"] = "ma-clef-secrete"
```
## Configuration des proxies

Les requêtes web fournies `french-cities` sont de trois types :
* celles générées par `pynsee`, interrogeant les API INSEE ;
* celles générées par `geopy`, interrogeant l'API Nominatim ;
* celles générées en propre par `french-cities` pour interroger l'API de la
Base Adresse Nationale et l'API Base officielle des codes postaux.

Dans le cas où l'on souhaiterait utiliser des proxies professionnels
pour connexion internet, il suffit de fixer deux variables d'environnement
supplémentaires :

* http_proxy
* https_proxy

Néanmoins, si l'utilisateur souhaite configurer son propre objet session
et le fournir en argument optionnel à `french-cities`, il lui revient :
* de fixer par lui-même le(s) proxy(ies) attachés à sa session ;
* de continuer à fixer les variables d'environnement `https_proxy` et
`http_proxy` (utilisées en propre par `pynsee` et `geopy` qui utilisent
leurs propres objets session).
5 changes: 3 additions & 2 deletions docs/external_doc.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ nav_order: 7

`french-cities` utilise plusieurs API externes. N'hésitez pas à consulter :

* la [documentation](https://adresse.data.gouv.fr/api-doc/adresse) (en Français) de l'API Adresse ;
* la [documentation](https://public.opendatasoft.com/explore/dataset/correspondance-code-cedex-code-insee/api/?flg=fr&q=code%3D68013&lang=fr) (en Français) de l'API OpenDataSoft ;
* la [documentation](https://adresse.data.gouv.fr/api-doc/adresse) de l'API Adresse ;
* la [documentation](https://public.opendatasoft.com/explore/dataset/correspondance-code-cedex-code-insee/api/?flg=fr&q=code%3D68013&lang=fr) de l'API OpenDataSoft ;
* la [documentation](https://datanova.laposte.fr/datasets/laposte-hexasmal) de la base officielle des codes postaux ;
* la [politique d'usage](https://operations.osmfoundation.org/policies/nominatim/) de Nominatim.
134 changes: 88 additions & 46 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,52 +9,94 @@ nav_order: 1

# Documentation du package python `french-cities`

Ce site contient la documentation du package python `french-cities`.
Ce site contient la documentation du package python `french-cities`,
un package visant à améliorer le référencement des communes dans les jeux
de données français 🇫🇷.

## Pourquoi french-cities ?

Des packages et des API sont déjà disponibles en langage python pour des
recherches usuelles.
Par exemple, l'excellent package `pynsee` utilise les API de l'INSEE pour
retrouver de multiples données :
* départements,
* régions,
* etc.

`geopy` peut également retrouver des communes à partir de leurs noms en
s'appuyant sur la BAN (Base Adresse Nationale) ou sur le service de géocodage
Nominatim.

`french-cities` est quant à lui optimisé pour travailler avec des données
fournies sous la forme de Series ou DataFrames `pandas`.
Les packages pré-cités (`pynsee`, `geopy`) sont toujours utilisés et
constituent au demeurant des dépendances importantes de `french-cities` :
le présent package peut être considéré comme une surcouche plus adaptée aux
volumes importants de données.

## Mise en cache et optimisation des algorithmes

Les algorithmes proposés tiennent globalement compte de la disponibilité
des différentes sources de données. Par exemple, l'API Nominatim n'autorise
pas plus d'une requête par seconde, ce qui explique que `geopy` constitue une
dépendance optionnelle permettant d'utliser Nominatim en dernier recours.

En outre, `french-cities` s'appuie sur deux systèmes de mise en cache :
* `pynsee` gère sa mise en cache nativement ;
* et pour d'autres requêtes web (base adresse nationale, API opendatasoft sur
les codes Cedex), les requêtes sont mises en cache avec une durée d'expiration
de 30 jours grâce au module `requests-cache` (ce qui explique la création de
fichiers SQLite au droit de l'exécution des scripts) ;
* seul le requêtage de l'API Nominatim (s'appuyant sur geopy) n'est pas mis en
cache à date.

A moins d'utiliser spécifiquement Nominatim à haute capacité (ce que les
algorithmes visent justement à prévenir), on peut donc considérer que
`french-cities` est relativement sûr à utiliser pour des tâches automatisées
répétitives. Bien évidemment, chaque utilisateur se doit d'utiliser ce package
de manière responsable et sous son entière responsabilité : il lui revient de
contrôler finement les résultats avant de se lancer dans des automatisations à
grande échelle.

Il est également conseillé de consulter [les documentations externes](./external_doc)
des sources utilisées.
Avez-vous des données :
* dont la localisation communale est fournie par le biais d'adresses
approximatives, ou via des coordonnées géographiques 🗺️ ?
* dont les communes sont référencées par leurs codes postaux et
leurs libellés 😮 ?
* dont les départements sont écrits en toutes lettres 🔡 ?
* dont les libellés sont douteux (torturant par exemple
le _<del>Loire</del> Loir-et-Cher_) ou obsolètes (mentionnant
par exemple _Templeuve_, une commune renommée en
_Templeuve-en-Pévèle_ depuis 2015) 🤦‍♂️ ?
* ou compilées au fil des années et dont les codes communes
constituent un patchwork de millésimes différents 🤯 ?

**Alors `french-cities` est fait pour vous 🫵 !**

## Contexte

Ce projet est né des besoins de la
[DREAL Hauts-de-France](https://www.hauts-de-france.developpement-durable.gouv.fr/)
pour la consolidation de jeux de données (opendata ou internes) 📊.
Et curieusement, nous n'avons pas trouvé de solution sur étagère
répondant à ce besoin.

Nous avons alors construit, amélioré et maintenu notre propre algorithme.
Aujourd'hui, une nouvelle étape a été franchie avec la publication et
l'ouverture 🔓 de cet algorithme dans un package python.

Ce package s'appuie bien sûr sur des librairies déjà existantes.
Car si aucune réponse globale au problème n'a été trouvée,
de nombreuses solutions *partielles* ont été identifiées. La plus-value
de `french-cities` tient justement dans l'articulation de ces
solutions et à leur optimisation pour répondre aux cas d'usage.

Par exemple, les API de l'INSEE permettent de réaliser des projections
d'un code commune vers un millésime donné. L'excellente implémentation
en python du package `pynsee` le permet effectivement.
Oui, mais voilà, pour
exécuter une projection, il faut connaître la date de départ, ce qui
est souvent une gageure 🕵️. En outre, la consommation des API de l'INSEE
est plafonnée à 30 requêtes par minute, ce qui pour un petit jeu
de données de 1000 lignes nous emmène déjà à plus de 30 minutes
d'exécution ⌛.

D'un autre côté, l'API Base Adresse Nationale (BAN) a déjà été
intégrée dans le package `geopy`, au côté d'autres geocodeurs 🌍 comme
le moteur de recherche OpenStreetMap Nominatim.
Mais là aussi, l'exercice ne tient pas la charge d'un jeu de données
un tant soit peu volumineux : `geopy` exécute des requêtes individuelles
à la BAN (limitées à 50 appels par seconde) alors qu'elle dispose
d'un point d'entrée CSV. Pour un jeu de données conséquent de 100 000 lignes,
cela nous amène tout de même à 30 minutes de temps de traitement.
Quant à Nominatim, les conditions générales d'utilisation limitent
sa consommation au rythme de 30 requêtes par minute ⌛.

En outre, si `pynsee` dispose d'un cache pour les jeux de données
nationaux, ce n'est pas totalement le cas pour les projections de millésime.
Ce n'est pas non plus le cas de `geopy` (alors même qu'il s'agit d'une
recommandation forte de Nominatim, même pour un usage raisonné).

`french-cities` a quant à lui été optimisé pour travailler avec des
jeux de données volumineux (sans avoir la prétention d'être le plus
efficace pour de petits datasets)[^1]. A cet effet, il
travaille essentiellement à partir de Series ou DataFrames `pandas` 🐼.

[^1]:A titre d'exemple, les utilisations publiées dans la présente
documentation s'appuient sur le traitement de jeux de données
entre 200 et 12000 lignes ; des tests sur des jeux de données à plus de
1 million de lignes ont également été effectués.

Les premières utilisations vous sembleront particulièrement lentes,
mais le système de mise en cache devrait permettre des résultats
plus rapides lors d'utilisation ultérieures.
En particulier, l'optimisation du cache permet à `french-cities`
d'être parfaitement adapté à une utilisation
dans des tâches automatisées 🕙.

Nous vous invitons aussi à consulter [les documentations externes](./external_doc)
des sources utilisées.

## Bugues

Aucun package n'est parfait, et sûrement pas du premier coup.
Si vous détectez des bugues, vous êtes invités à nous les partager
sur [le guichet](https://github.com/tgrandje/french-cities/issues)
du projet.
10 changes: 0 additions & 10 deletions docs/install.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,8 @@ nav_order: 2

# Installation du package

## Installation classique

`french-cities` est hébergé [sur pypi](https://pypi.org/project/french-cities/).

Il peut donc être installé très classiquement à l'aide de la commande suivante :

`pip install french-cities`

## Installation avec dépendances optionnelles

`pip install french-cities[full]`

L'installation "full" permet d'installer `geopy` qui constitue une dépendance
optionnelle utilisable en dernier ressort pour la reconnaissance de communes.

Loading

0 comments on commit 5ea36ea

Please sign in to comment.