.. Outils de manipulation de données en ligne de commande
created by sphinx-quickstart on Tue Jan 18 18:17:52 2022.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
======================================================
Outils de manipulation de données en ligne de commande
======================================================
.. toctree::
:maxdepth: 2
:caption: Contents:
Formats d'échange de données
============================
J'ai acheté un livre dont le titre m'a amusé `Data Science at the Command
Line`_ où j'ai découvert 2 utilitaires, `csvkit`_ et `jq`_, que j'utilise très
fréquemment (le livre est en ligne accessible gratuitement).
Le livre utilise une définition de la science des données comme étant composée
de cinq étapes :
1. Obtenir les données
2. Nettoyer les données
3. Explorer les données
4. Mettre en forme les données
5. Interpréter les données
Dans les projets, on rencontre différents formats de données que l'on peut
avoir à manipuler, à analyser. Parmi ces formats, on rencontre souvent : CSV,
JSON, XML, ... et des déclinaisons particulières de ces formats (XML-TEI,
GeoXXX, ...)
Ces formats sont faits pour permettre à des programmes d'échanger des données
et ces données deviennent rapidement illisibles par un humain lorsqu'elles
grossissent.
Il ne faut pas oublier que ces formats sont souvent bien spécifiés par des RFC,
des recommandations W3C, etc. Il est important de les connaître pour ne pas
partir sur des mises en oeuvre exotiques (typique avec le CSV, headers et
séparateurs particuliers).
- le format CSV (Comma-Separated Values) est décrit par la `RFC 4180`_
- le format JSON (JavaScript Object Notation) est décrit par la `RFC 7159`_
- le XML ou (eXtensible Markup Language) est un langage informatique de
balisage générique, spécifié par `XML 1.1 W3C recommandation`_
L'avantage des outils en ligne de commande, c'est que l'on peut les chaîner
avec ``|``.
.. _Data Science at the Command Line: https://datascienceatthecommandline.com/
.. _csvkit: https://csvkit.rtfd.org/
.. _jq: https://stedolan.github.io/jq/
.. _RFC 4180: https://datatracker.ietf.org/doc/html/rfc4180
.. _RFC 7159: https://datatracker.ietf.org/doc/html/rfc7159
.. _XML 1.1 W3C recommandation: https://www.w3.org/TR/xml11/
Outils CSV
==========
Données manipulées
------------------
.. ~/COVID-19/clinicregisterDWH/clinicregisterdwh.ruben/clinicdwh/media/import/oms/COVID19-web_ITrCX3P.csv
.. ~/Progs/python/csvkit/69.csv => donnees-foncieres-2019-rhone.csv
.. ~/Donnees/demande-valeur-fonciere/15-12-2020/geolocalise/cadastre.data.gouv.fr/data/etalab-dvf/latest/csv/
- ``COVID19-web_ITrCX3P.csv`` : fichier OMS des essais cliniques menés dans le
monde pour le COVID-19 (projet COVID INS2I-INSERM)
- ``donnees-foncieres-2019-rhone.csv`` : transactions immobilières et foncières
dans le Rhône (data.gouv.fr)
- ``corrections_aurehal_19juin2015.xlsx`` : liste de corrections faites par
INRIA sur les structures de recherche dans AUReHAL
csvkit
------
`csvkit`_ est une suite d'outils pour manipuler et convertir des fichiers CSV.
installation
............
`csvkit`_ est codé en Python, il s'installe classiquement dans un environnement
virtuel avec pip.
.. code-block:: bash
$ pip install csvkit
https://projet.liris.cnrs.fr/edp/cafes-developpeur-liris/2019-01-15-environnements-virtuels-en-python.html
streaming versus buffering
..........................
Attention lorsqu'il y a de gros fichiers, il y a des commandes qui chargent
toutes les données en mémoire et des commandes qui travaillent en flux.
https://csvkit.readthedocs.io/en/latest/contributing.html#streaming-versus-buffering
csvlook
.......
Visualisation tabulaire des données CSV
.. code-block:: bash
$ < COVID19-web_ITrCX3P.csv head -n 100 | csvlook | less -S
| TrialID | Last Refreshed on | Public title
| -------------------- | ----------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------
| NCT00173563 | 2015-02-19 | Induction of Cytokines in Human Monocytes by SARS-CoV in Adults and Children
| NCT00523276 | 2015-02-19 | SARS Survivor Evaluations
| NCT00533741 | 2015-02-19 | SARS Coronavirus Vaccine (SARS-CoV)
| NCT01376765 | 2015-02-19 | Phase I Dose Escalation SARS-CoV Recombinant S Protein, With and Without Adjuvant, Vaccine Study
| IRCT20180223038837N1 | 2019-01-14 | Comparative study of radiofrequency ablation & foam sclerotherapy for treatment patients suffering from varicose veins
| NCT04246242 | 2020-02-03 | A Randomized Multicenter Controlled Clinical Trial of Arbidol in Patients With 2019 Novel Coronavirus (2019-nCoV)
| ChiCTR2000029953 | 2020-02-17 | Construction and Analysis of Prognostic Predictive Model of Novel Coronavirus Pneumonia (COVID-19)
| ChiCTR2000029949 | 2020-02-17 | A Medical Records Based Study for the Effectiveness of Extracorporeal Membrane Oxygenation in Patients with Severe Novel Coronavirus Pneumonia (COVID-19)
| ChiCTR2000029947 | 2020-02-17 | A Randomized Controlled Trial for Qingyi No. 4 Compound in the treatment of Convalescence Patients of Novel Coronavirus Pneumonia (COVID-19)
``less -S`` permet d'afficher les données sans lire l'intégralité des données
avant de commencer (mais ``csvlook`` charge l'intégralité des données d'où le
`head -n 100``) et l'option ``-S`` permet d'éviter les retours à la ligne
lorsque les lignes dépassent la taille de l'écran (no wrap)
Limiter le nombre de colonnes affichées avec ``--max-columns``
.. code-block:: bash
$ < donnees-foncieres-2019-rhone.csv head -n 1000 | csvlook --max-columns 6 | less -S
| id_mutation | date_mutation | numero_disposition | nature_mutation | valeur_fonciere | adresse_numero | ... |
| ----------- | ------------- | ------------------ | ---------------------------------- | --------------- | -------------- | --- |
| | 2017-01-10 | 1 | Vente | 21 000,00 | 9 | ... |
| | 2017-01-05 | 1 | Vente | 578 800,00 | 31 | ... |
| | 2017-01-05 | 1 | Vente | 578 800,00 | 31 | ... |
| | 2017-01-03 | 1 | Vente | 569 800,00 | 21 | ... |
in2csv
......
**in2csv** convertit le fichier Excel en CSV (le premier onglet par défaut).
.. code-block:: bash
$ in2csv corrections_aurehal_19juin2015.xlsx >corrections_aurehal_19juin2015.csv
/home/fconil/Progs/python/csvkit/.venv/lib/python3.8/site-packages/agate/utils.py:276: UnnamedColumnWarning: Column 7 has no name. Using "h".
**in2csv** peut lister les onglets du fichier Excel et exporter un onglet spécifié
.. code-block:: bash
$ in2csv -n TrialDetails63.xlsx
TRIAL
SECONDARY ID
HEALTH CONDITION
CONDITION CODE
INTERVENTION CODE
PRIMARY OUTCOME
SECONDARY OUTCOME
HOSPITAL
POSTCODE
COUNTRY OUTSIDE AUSTRALIA
FUNDING SOURCE
SECONDARY SPONSOR
OTHER COLLABORATOR
ETHICS COMMITTEE
CONTACTS
DATA SHARING STATEMENT
SUMMARY RESULTS
$ in2csv --sheet "COUNTRY OUTSIDE AUSTRALIA" TrialDetails63.xlsx | head | csvlook
| TRIAL ID | COUNTRY | STATE |
| -------- | -------------- | ---------------------------------- |
| 383 488 | Turkey | Izmir |
| 383 373 | India | Several districts of Uttar Pradesh |
| 383 203 | Turkey | Izmir |
| 382 922 | United Kingdom | |
| 383 360 | Bangladesh | Several districts |
| 383 303 | Indonesia | West Java |
csvcut
......
Découvrir les colonnes d'un CSV avec ``csvcut -n``
.. code-block:: bash
$ < COVID19-web_ITrCX3P.csv csvcut -n
1: TrialID
2: Last Refreshed on
3: Public title
4: Scientific title
...
Sélectionner les colonnes d'un CSV avec ``csvcut -c``, je référence
préférentiellement les colonnes par leur numéro (plus rapide et pas de problème
de caractères particuliers dans les noms de commande).
.. code-block:: bash
$ < COVID19-web_ITrCX3P.csv head -n 100 | csvcut -c 1,2,7,10,12,18 | csvlook | less -S
i TrialID | Last Refreshed on | Date registration | Source Register | Recruitment Status | Target size >
| -------------------- | ----------------- | ----------------- | ------------------ | ------------------ | ----------------------------------------------------------------------------------------------->
| NCT00173563 | 2015-02-19 | 12/09/2005 | ClinicalTrials.gov | Recruiting | 10 >
| NCT00523276 | 2015-02-19 | 30/08/2007 | ClinicalTrials.gov | Not recruiting | 0 >
| NCT00533741 | 2015-02-19 | 20/09/2007 | ClinicalTrials.gov | Not recruiting | 0 >
| NCT01376765 | 2015-02-19 | 16/06/2011 | ClinicalTrials.gov | Not recruiting | 0 >
| IRCT20180223038837N1 | 2019-01-14 | 2018-12-08 | IRCT | Recruiting | 60 >
| NCT04246242 | 2020-02-03 | 27/01/2020 | ClinicalTrials.gov | Not recruiting | 500 >
| ChiCTR2000029953 | 2020-02-17 | 2020-02-17 | ChiCTR | Not Recruiting | survival group:200;died:200; >
| ChiCTR2000029949 | 2020-02-17 | 2020-02-16 | ChiCTR | Not Recruiting | Case series:40; >
| ChiCTR2000029947 | 2020-02-17 | 2020-02-16 | ChiCTR | Not Recruiting | experimental group :100;control group :100; >
options générales
.................
- `-d` pour spécifier le délimiteur si ce n'est pas une virgule
- `-t` pour indiquer qu'il s'agit d'un fichier tabulaire
- `-e` pour spécifier l'encodage
Voir https://csvkit.readthedocs.io/en/latest/common_arguments.html
csvgrep
.......
Recherche de chaîne de caractère dans une colonne :
- ``-c`` indique la colonne concernée
- ``-m`` recherche une correspondance exacte avec la chaîne de caractère passée
en paramètre
.. code-block:: bash
$ csvgrep -c Sigle -m BEAGLE corrections_aurehal_19juin2015.csv | csvlook
| Sigle | idStructureHal | siidRNSR | sigleCRI | libelleEN | Id HAL disparus après modification | OK le | h |
| ------ | -------------- | ---------- | -------- | ---------------------------------------------- | ---------------------------------- | ---------- | - |
| BEAGLE | 160 051 | 201120997E | GRA | Artificial Evolution and Computational Biology | 397 150 | 2015-06-17 | |
Avec ``-r`` on peut faire une recherche avec une expression régulière
.. code-block:: bash
$ csvgrep -c Sigle -r "(?i)beagle" corrections_aurehal_19juin2015.csv | csvlook
| Sigle | idStructureHal | siidRNSR | sigleCRI | libelleEN | Id HAL disparus après modification | OK le | h |
| ------ | -------------- | ---------- | -------- | ---------------------------------------------- | ---------------------------------- | ---------- | - |
| BEAGLE | 160 051 | 201120997E | GRA | Artificial Evolution and Computational Biology | 397 150 | 2015-06-17 | |
Je l'ai utilisé lorsque je recevais périodiquement les fichiers Excel de
modifications de structures faites dans AUReHAL, cela me permettait de chercher
rapidement si la structure que l'on surveillait était concernée.
csvsort
.......
.. code-block:: bash
$ < donnees-foncieres-2019-rhone.csv head -n 1000 | csvsort -c type_local | csvlook --max-columns 6 | less -S
| id_mutation | date_mutation | numero_disposition | nature_mutation | valeur_fonciere | adresse_numero | ... |
| ----------- | ------------- | ------------------ | ---------------------------------- | --------------- | -------------- | --- |
| | 2017-01-05 | 1 | Vente | 578 800,00 | 31 | ... |
| | 2017-01-05 | 1 | Vente | 578 800,00 | 31 | ... |
| | 2017-01-03 | 1 | Vente | 569 800,00 | 21 | ... |
| | 2017-01-05 | 1 | Vente | 240 000,00 | 31 | ... |
| | 2017-01-09 | 1 | Vente | 240 000,00 | 50 | ... |
csvstat
.......
Compter le nombre de lignes. Il peut y avoir des sauts de ligne dans les champs
donc ``wc -l`` n'est pas sûr.
.. code-block:: bash
$ csvstat --count TrialDetails63.1.csv
258
$ wc -l TrialDetails63.1.csv
8901 TrialDetails63.1.csv
Affiche des statistiques sur les colonnes du CSV, permet d'avoir une
information sur les données contenues et un aperçu de leur qualité.
.. code-block:: bash
$ csvstat COVID19-web_ITrCX3P.csv
...
12. "Recruitment Status"
Type of data: Text
Contains null values: True (excluded from calculations)
Unique values: 6
Longest value: 14 characters
Most common values: Recruiting (5030x)
Not recruiting (3837x)
Not Recruiting (3207x)
Authorised (442x)
None (43x)
...
16. "Inclusion gender"
Type of data: Text
Contains null values: True (excluded from calculations)
Unique values: 21
Longest value: 32 characters
Most common values: All (6643x)
Both (2237x)
None (1789x)
Female: yes
Male: yes
(571x)
Both, male and female (410x)
csvsql
......
**ATTENTION** : L'utilisation naïve de ``csvsql`` peut conduire à des noms de
tables et de colonnes difficilement utilisables dans les requêtes.
- Il faut éviter les tirets dans le nom de la table
- Il faut éviter les espaces, les tirets et les caractères spéciaux dans les
noms de colonnes
Si cela n'est pas fait, on doit mettre le nom de la table et le nom des champs
entre guillemets.
utilisation simple
^^^^^^^^^^^^^^^^^^
``csvsql --db`` va créer et remplir une base de données SQLite à partir du CSV
en inférant le type des colonnes. Sans l'option ``--insert``, les données ne
sont pas insérées dans la table.
On précise le nom de la table créée avec ``--tables`` pour éviter un nom de
table compliqué à manipuler.
.. code-block:: bash
$ csvsql --db sqlite:///donnees-foncieres-2019-rhone.db --tables immobilier --insert donnees-foncieres-2019-rhone.csv
.. code-block:: sqlite3
sqlite> select count(id), type_local from immobilier group by type_local;
count(id) = 24988
type_local =
count(id) = 25171
type_local = Appartement
count(id) = 20118
type_local = Dépendance
count(id) = 6013
type_local = Local industriel. commercial ou assimilé
count(id) = 9708
type_local = Maison
``csvsql --query`` permet d'exécuter une requête SQL sur un fichier csv qui est
chargé dans une base SQLite en mémoire.
.. code-block:: bash
$ csvsql --query "select count(id), type_local from 'donnees-foncieres-2019-rhone' group by type_local;" donnees-foncieres-2019-rhone.csv | csvlook
| count(id) | type_local |
| --------- | ---------------------------------------- |
| 24 988 | |
| 25 171 | Appartement |
| 20 118 | Dépendance |
| 6 013 | Local industriel. commercial ou assimilé |
| 9 708 | Maison |
Bien que ce soit intéressant, j'ai abandonné cet usage :
- dès que le fichier CSV est gros cela prend du temps
- car on a souvent plusieurs requêtes à exécuter, ou on adapte souvent la
requête initiale en fonction des résultats et cela implique de recréer la
base à chaque fois
- on est confronté à plusieurs difficultés liés à la création et au remplissage
de la table
difficultés
^^^^^^^^^^^
Les noms de colonnes du header de ``COVID19-web_ITrCX3P.csv`` contiennent des
espaces.
.. code-block:: bash
$ csvsql --db sqlite:///COVID19.db --tables COVID19 --insert COVID19-web_ITrCX3P.csv
.. code-block:: sqlite3
sqlite> select distinct("Recruitment Status") from COVID19;
Recruitment Status = Recruiting
Recruitment Status = Not recruiting
Recruitment Status = Not Recruiting
Recruitment Status =
Recruitment Status = Authorised
Recruitment Status = Not Available
``csvsql -i`` va inférer le type des colonnes (comme ``csvstat``) pour générer
une instruction ``CREATE TABLE`` pour un type de base de données spécifié. Cela
permet d'adapter le nom de la table et des colonnes pour créer une base propre.
.. code-block:: bash
$ csvsql -i sqlite COVID19-web_ITrCX3P.csv
CREATE TABLE "COVID19-web_ITrCX3P" (
"TrialID" VARCHAR NOT NULL,
"Last Refreshed on" DATE NOT NULL,
"Public title" VARCHAR,
"Scientific title" VARCHAR,
"Acronym" VARCHAR,
...
);
Mais je n'ai pas réussi à charger les données avec ``csvsql`` et des noms de
colonnes modifiées
.. code-block:: bash
# KO
$ csvsql --db sqlite:///COVID19_fixed.db --before-insert "$(cat create-table-COVID19-fixed.sql)" --insert COVID19-web_ITrCX3P.csv
# KO
$ < COVID19-web_ITrCX3P.csv sed "1d" > COVID19-web_ITrCX3P.no-header.csv
$ csvsql --db sqlite:///COVID19_fixed.db --no-create --table COVID19 --before-insert "$(cat create-table-COVID19-fixed.sql)" -H --insert COVID19-web_ITrCX3P.no-header.csv
On a une erreur plus parlante en pur SQLite où l'import fonctionne en enlevant
les contraintes CHECK générées.
.. code-block:: sqlite3
sqlite> .read create-table-COVID19-fixed.sql
sqlite> .mode csv
sqlite> .import COVID19-web_ITrCX3P.no-header.csv COVID19
...
COVID19-web_ITrCX3P.no-header.csv:25792: INSERT failed: CHECK constraint failed: COVID19
sqlite> .read create-table-COVID19-fixed-no-check.sql
sqlite> .mode csv
sqlite> .import COVID19-web_ITrCX3P.no-header.csv COVID19
sqlite> select count(TrialID) from COVID19;
count(TrialID)
12564
Même sans ces contraintes CHECK, cela ne fonctionne toujours pas avec
``csvsql``.
.. code-block:: bash
$ csvsql --db sqlite:///COVID19_fixed.db --no-create --table COVID19 --before-insert "$(cat create-table-COVID19-fixed-no-check.sql)" -H --insert COVID19-web_ITrCX3P.no-header.csv
**Il faut renommer les colonnes dans le header.**
.. code-block:: bash
$ < COVID19-web_ITrCX3P.csv sed "1s/ /_/g" > COVID19-web_ITrCX3P.header-fixed.csv
$ csvsql --db sqlite:///COVID19_header_fixed.db --table COVID19 --insert COVID19-web_ITrCX3P.header-fixed.csv
Commandes non utilisées
.......................
sql2csv
^^^^^^^
Je ne m'en suis jamais servie, quand j'ai une base SQL, j'ai tendance à
utiliser SQL directement.
.. code-block:: bash
$ sql2csv --db sqlite:///donnees-foncieres-2019-rhone.db --query "select count(id), type_local from immobilier group by type_local;" | csvlook
| count(id) | type_local |
| --------- | ---------------------------------------- |
| 24 988 | |
| 25 171 | Appartement |
| 20 118 | Dépendance |
| 6 013 | Local industriel. commercial ou assimilé |
| 9 708 | Maison |
csvclean
^^^^^^^^
La commande place placer les lignes valides dans un fichier ``[basename]_out.csv``
et les lignes erronées dans un fichier ``[basename]_err.csv``.
Avec l'option ``-n``, la commande fonction en dry-run.
https://csvkit.readthedocs.io/en/latest/scripts/csvclean.html
csvjoin
^^^^^^^
Permet de fusionner 2 fichiers CSV ou plus (avec des colonnes différentes
j'imagine).
https://csvkit.readthedocs.io/en/latest/scripts/csvclean.html
csvstack
^^^^^^^^
Permet de fusionner les lignes de plusieurs CSV.
https://csvkit.readthedocs.io/en/latest/scripts/csvstack.html
diff de CSV
-----------
csv-diff
........
https://datasette.io/tools/csv-diff
.. ~/Progs/python/datasette
.. code-block:: bash
$ pip install csv-diff
Différence entre 2 fichiers récupérés sur data.gouv.fr et qui semble avoir la
même clé pour les député et à peu près les même colonnes.
.. code-block:: bash
$ csv-diff --key id --format csv deputes-historique/deputes-historique.csv deputes-actifs/deputes-15.csv | more
2 columns added, 2 columns removed, 575 rows changed, 1022 rows removed
2 columns added
placeHemicycle
premiereElection
2 columns removed
active
legislatureLast
575 rows changed
id: PA605036
age: "41" => "40"
twitter: "@damienabad" => ""
facebook: "damienabad.depute" => ""
experienceDepute: "10 ans" => "9 ans"
scoreParticipationSpecialite: "0.21" => "0.30"
scoreLoyaute: "0.911" => "0.913"
scoreMajorite: "0.352" => "0.345"
dateMaj: "2022-01-19" => "2021-02-14"
id: PA719866
age: "45" => "44"
experienceDepute: "5 ans" => "4 ans"
scoreParticipation: "0.30" => "0.29"
scoreLoyaute: "0.991" => "0.992"
scoreMajorite: "0.991" => "0.992"
dateMaj: "2022-01-19" => "2021-02-14"
...
Voir aussi https://datasette.io/
daff
....
- https://paulfitz.github.io/daff/
- https://github.com/paulfitz/daff
.. code-block:: bash
$ pip install daff
.. ~/Progs/python/daff
Peut s'utiliser en ligne de commande et programmatiquement.
Indiqué dans la documentation csvkit pour le diff de fichiers CSV :
https://csvkit.readthedocs.io/en/latest/cli.html
.. code-block:: bash
$ daff --input-format csv --id id ~/Donnees/deputes/data.gouv.fr/deputes-historique/deputes-historique.csv ~/Donnees/deputes/data.gouv.fr/deputes-actifs/deputes-15.csv
Envoie le résultat dans un navigateur avec ``--www``
.. code-block:: bash
$ daff --www --input-format csv --id id ~/Donnees/deputes/data.gouv.fr/deputes-historique/deputes-historique.csv ~/Donnees/deputes/data.gouv.fr/deputes-actifs/deputes-15.csv
Voir le résultat : `tmpz1kk4q6q.html <_static/tmpz1kk4q6q.html>`_.
Ignorer une ou plusieurs colonnes
.. code-block:: bash
$ daff --www --input-format csv --ignore "Export date" --id TrialID COVID19-web_FlhWKml.csv COVID19-web_PXQX3YP.csv
Voir le résultat : `tmpk49hjf6k.html <_static/tmpk49hjf6k.html>`_.
validation de csv
-----------------
Je souhaitais trouver une solution de validation de la qualité des fichiers CSV.
CSV Schema
..........
http://digital-preservation.github.io/csv-schema/
.. ~/docker/csv-validator
Voir mesnotes
frictionless
............
- https://frictionlessdata.io/software/
- https://framework.frictionlessdata.io/
.. ~/Progs/python/frictionless
.. code-block:: bash
$ pip install frictionless[sql]
Validation simple d'un fichier CSV
.. code-block:: bash
$ frictionless validate COVID19-web_ITrCX3P.csv
# -------
# invalid: COVID19-web_ITrCX3P.csv
# -------
===== ===== ========== =============================================================================================================================
row field code message
===== ===== ========== =============================================================================================================================
593 38 type-error Type error in the cell "True " in row "593" and field "Bridging flag truefalse" at position "38": type is "boolean/default"
602 38 type-error Type error in the cell "True " in row "602" and field "Bridging flag truefalse" at position "38": type is "boolean/default"
Génération d'un schéma YAML pour la validation du CSV.
Table Schema (frictionless standard) : https://specs.frictionlessdata.io/table-schema/#language
.. code-block:: bash
$ frictionless describe COVID19-web_ITrCX3P.csv --type schema >oms-schema.yaml
.. code-block:: yaml
# --------
# metadata: COVID19-web_ITrCX3P.csv
# --------
fields:
- name: TrialID
type: string
- name: Last Refreshed on
type: string
- name: Public title
type: string
- name: Scientific title
type: string
- name: Acronym
type: any
- name: Primary sponsor
type: string
- name: Date registration
type: string
- name: Date registration3
type: integer
- name: Export date
type: string
- name: Source Register
type: string
- name: web address
type: string
- name: Recruitment Status
type: string
...
- name: Retrospective flag
type: string
- name: Bridging flag truefalse
type: boolean
- name: Bridged type
type: string
- name: results yes no
type: any
Les valeurs True contiennent un espace en trop dans le CSV pour le champ
"Bridging flag truefalse" : "True "
.. code-block:: bash
$ frictionless validate COVID19-web_ITrCX3P.csv --schema oms-schema.yaml
# invalid: COVID19-web_ITrCX3P.csv
# -------
===== ===== ========== =============================================================================================================================
row field code message
===== ===== ========== =============================================================================================================================
593 38 type-error Type error in the cell "True " in row "593" and field "Bridging flag truefalse" at position "38": type is "boolean/default"
602 38 type-error Type error in the cell "True " in row "602" and field "Bridging flag truefalse" at position "38": type is "boolean/default"
626 38 type-error Type error in the cell "True " in row "626" and field "Bridging flag truefalse" at position "38": type is "boolean/default"
639 38 type-error Type error in the cell "True " in row "639" and field "Bridging flag truefalse" at position "38": type is "boolean/default"
$ < ~/Documents/RstDocs/mespres/data-tools-in-cmde-line/csv/COVID19-web_ITrCX3P.csv csvcut -c 1,13,37,38,40 | sed -n "1p;590,600p"
TrialID,other records,Retrospective flag,Bridging flag truefalse,results yes no
ChiCTR2000030331,No,No,False,
ChiCTR2000030328,No,Yes,False,
EUCTR2020-001301-23-FR,No,Yes,False,
EUCTR2020-001188-96-FR,Yes,Yes,True ,
EUCTR2020-001457-43-FR,No,Yes,False,
EUCTR2020-001442-19-ES,No,No,False,
EUCTR2020-001409-21-ES,No,No,False,
EUCTR2020-001492-33-FR,No,Yes,False,
EUCTR2020-001194-69-ES,No,No,False,
EUCTR2020-001565-37-ES,No,No,False,
EUCTR2020-001421-31-ES,No,No,False,
pandas
------
Une fois les données explorées, on utilise la bibliothèque `pandas`_ pour
manipuler les données.
.. _pandas: https://pandas.pydata.org/
Outils JSON
===========
Données manipulées
------------------
.. ~/COVID-19/Trial-registries/clinicaltrialsregister.eu/eu-parser/analyzing-json-extraction-with-jq.sh
.. ~/Progs/jq/covid <= ~/COVID-19/clinicregisterDWH/clinicregisterdwh.merwan/clinicdwh/cache/eu/EudraCT_*.json (plus de json stocké sur disque ni utilisé)
.. ~/Donnees/assemblee-nationale/votes (ls -1 votes | wc -l => 4326)
- ``VTANR5L15V*.json`` : `Votes de l'Assemblée Nationale `_
- ``EudraCT_*.json`` : Stockage d'essais clinique du registre européen (extraits du site web) au format json
jq
--
- Manuel : https://stedolan.github.io/jq/manual/
- Tutorial : https://stedolan.github.io/jq/tutorial/
- Source : https://github.com/stedolan/jq
- Tester l'utilitaire en ligne : https://jqplay.org/
- Cookbook : https://github.com/stedolan/jq/wiki/Cookbook#list-keys-used-in-any-object-in-a-list
- FAQ : https://github.com/stedolan/jq/wiki/FAQ
S'il s'agit de données que l'on récupère (Assemblée nationale), on a besoin de
découvrir progressivement l'architecture des données json que l'on récupère.
.. code-block:: bash
$ < votes/VTANR5L15V3732.json jq '.'
$ < votes/VTANR5L15V3732.json jq '.' -C | less -R
Le plus basique consiste à tout afficher, ``jq`` applique une coloration et une
mise en page qui facilite la lecture de données json qui peuvent être sur une
seule ligne.
.. code-block:: json
{
"scrutin": {
"@xmlns": "http://schemas.assemblee-nationale.fr/referentiel",
"@xmlns:xsi": "http://www.w3.org/2001/XMLSchema-instance",
"uid": "VTANR5L15V3732",
"numero": "3732",
"organeRef": "PO717460",
"legislature": "15",
"sessionRef": "SCR5A2021O1",
"seanceRef": "RUANR5L15S2021IDS24293",
"dateScrutin": "2021-04-17",
"quantiemeJourSeance": "2",
"typeVote": {
"codeTypeVote": "SPO",
"libelleTypeVote": "scrutin public ordinaire",
"typeMajorite": "majorité absolue des suffrages exprimés"
},
"sort": {
"code": "rejeté",
"libelle": "L'Assemblée nationale n'a pas adopté"
},
"titre": "l'amendement n° 4548 de M. Balanant et les amendements identiques suivants après l'article 71 du projet de loi portant lutte contre le dérèglement climatique et renforcement de la résilience face à ses effets (première lecture).",
"demandeur": {
Ici la structure de base est un dictionnaire avec une seule clé.
.. code-block:: bash
$ < votes/VTANR5L15V3732.json jq 'keys'
.. code-block:: json
[
"scrutin"
]
On va donc extraire la valeur de la clé "scrutin", qui est un dictionnaire et
dont on va demander les clés en combinant les filtres avec ``|``
.. code-block:: bash
$ < votes/VTANR5L15V3732.json jq '.scrutin | keys'
[
"@xmlns",
"@xmlns:xsi",
"dateScrutin",
"demandeur",
"legislature",
"miseAuPoint",
"modePublicationDesVotes",
"numero",
"objet",
"organeRef",
"quantiemeJourSeance",
"seanceRef",
"sessionRef",
"sort",
"syntheseVote",
titre",
"typeVote",
"uid",
"ventilationVotes"
]
On peut extraire quelques valeurs du dictionnaire dans un objet avec ``{}`` où
l'on reprend les clés à extraire.
.. code-block:: bash
$ < votes/VTANR5L15V3732.json jq '.scrutin | {titre, uid, demandeur}'
.. code-block:: json
{
"titre": "l'amendement n° 4548 de M. Balanant et les amendements identiques suivants après l'article 71 du projet de loi portant lutte contre le dérèglement climatique et renforcement de la résilience face à ses effets (première lecture).",
"uid": "VTANR5L15V3732",
"demandeur": {
"texte": "Présidente du groupe \"Socialistes et apparentés\"",
"referenceLegislative": null
}
}
Il y a un vote par fichier, comment obtenir une liste des votes et leur fichier ?
Pour exporter en csv (``@csv``), il faut générer un tableau json avec ``[]``.
``-r`` permet d'éviter que chaque ligne soit encadrée par des guillemets.
.. ~/Progs/jq
.. code-block:: bash
$ echo '"uid","titre"' >liste-votes.csv
$ ls -1 votes/*.json | while read A
> do
> jq -r '.scrutin | [.uid, .titre] | @csv' $A >>liste-votes.csv
> done
::
< covid/EudraCT_2020-001385-11_ES.json jq -C '.' | less -R
< covid/EudraCT_2020-001385-11_ES.json jq 'keys'
# Ouvrir l'URL de l'essai
< covid/EudraCT_2020-001385-11_ES.json jq '.metadata'
< covid/EudraCT_2020-001385-11_ES.json jq -C '.sections' | less -R
< covid/EudraCT_2020-001385-11_ES.json jq -C '.sections[2] ' | less -R
# Afficher toutes les sous-sections en supprimant le tableau englobant de la sous-section
< covid/EudraCT_2020-001385-11_ES.json jq -C '.sections[] | .subsections | .[] ' | less -R
# Extraire le nom de toutes les sous-sections
< covid/EudraCT_2020-001385-11_ES.json jq -C '.sections[] | .subsections[] | .name'
# Y a-t-il une sous-section qui se répète
# Pas pratique sûrement combinable
< covid/EudraCT_2020-001385-11_ES.json jq -C '.sections[] | .subsections[] | .name | contains(": 2")'
< covid/EudraCT_2020-001385-11_ES.json jq -C '.sections[] | .subsections | .[]' | grep ": 2"
# Récupérer l'élément "A.1" des sections A des fichiers
ls -1 covid/EudraCT_*.json | while read A; do echo $A; cat $A | jq '.sections[] | select (.id == "section-a") | .subsections[] | .data[] | select(.id == "A.1")'; done
Outils XML
==========
**Matériel - code**
::
Voir ~/COVID-19/Trial-registries/ISRCTN-UK
~/Progs/xml/xslt
~/PyEnvs-a-trier/PyEnv3.5.2/webscraping/automate-liris-tei-export
1er cas : XML-TEI pour HAL
--------------------------
Format utilisé pour le transfert des publications du Système d'Information
LIRIS vers HAL.
**Besoin** :
1. Reformatter le fichier reçu du Système d'Information pour le rendre
humainement lisible
2. Vérifier que le fichier est syntaxiquement correct par rapport au schéma XSD
du CCSD-HAL
Outil utilisé `xmllint`_, xmllint est un outil associé à `libxml2`_ et qui
nécessite le paquet libxml2-utils.
**Installation** : ``$ sudo apt install libxml2-utils``
.. code-block:: bash
$ wget -O xmltei/hal-tei-01529866.xml -nv https://hal.archives-ouvertes.fr/hal-01529866v1/tei
**Reformatter le fichier** (pas nécessaire pour celui récupéré de HAL)
.. code-block:: bash
$ xmllint --format "./xmltei/liris-tei-2853.xml" >"liris-tei-2853.xml"
**Vérifier que le fichier est syntaxiquement valide**
La vérification ne fonctionne pas avec le fichier que je viens de récupérer de
HAL parce que le schéma XSD que j'utilise est un peu ancien et qu'il servait à
vérifier les fichiers déposés il y a quelques années. Mais ça montrer
l'affichage de ``Schemas validity error``.
.. code-block:: bash
$ xmllint --schema ./tei/aofr-sword.xsd "liris-tei-2853.xml" >"./xmltei/liris-tei-2853.xml" 2>&1
$ xmlstarlet val -s tei/aofr-sword.xsd "liris-tei-2853.xml"
2ème cas : Projet COVID INS2I-INSERM
------------------------------------
**Présentation simple** : On récupère des métadonnées des études cliniques de
différents pays / continents. On en extrait une partie que l'on transforme pour
éviter au CRESS qui analyse ces études de récupérer ces métadonnées
manuellement.
On manipule une grande variété de formats : CSV, XML, accès BD, scrapping de
site web, ...
Dernièrement, j'ai voulu extraire les métadonnées du "registre" anglais puisque
les anglais ne déposent plus les métadonnées de leurs études dans le registre
européen depuis le Brexit. Une API est fournie qui renvoie du XML.
Il m'a fallu plonger un peu plus sur la façon dont je pouvais analyser puis
extraire des données de ce format. J'ai consulté quelques documentations
(`Structurez vos données avec XML`_) et regardé XPath (XML Path Language) pour
l'extraction.
XMLStarlet
..........
J'avais vu qu'il existait un outil `XMLStarlet`_ que j'ai utilisé pour explorer
les données récupérées du registre anglais. Voir `mémo xmlstarlet`_ et
`documentation xmlstarlet`_.
``el -v`` : afficher les éléments de la structure du document.
.. code-block:: bash
$ xmlstarlet --help
$ xmlstarlet el -v liris-tei-2853.xml
``sel -l -v "//biblFull/titleStmt/title"`` : extraire des éléments selon un
template XPath.
Malheureusement, l'utilisation d'un namespace par défaut dans le XML pose
problème aux 2 outils que j'ai utilisés et la commande ci dessous ne retourne
rien.
.. code-block:: bash
$ xmlstarlet sel -t -v "//biblFull/titleStmt/title" liris-tei-2853.xml
$ head liris-tei-2853.xml | bat
Le 1er contournement nécessite de modifier tous les éléments du chemin XPath :
bof
.. code-block:: bash
$ xmlstarlet sel -N x="http://www.tei-c.org/ns/1.0" -t -v "//x:biblFull/x:titleStmt/x:title" liris-tei-2853.xml
# Plus simplement
$ xmlstarlet sel -t -v "//_:biblFull/_:titleStmt/_:title" liris-tei-2853.xml
La 2ème solution consiste à transformer le XML en un format pyx, dont on
supprime les namespace et que l'on transforme à nouveau en XML.
C'est "bourrin" mais ça m'a suffit pour mon exploration des données.
.. code-block:: bash
$ xmlstarlet pyx liris-tei-2853.xml | grep -v "^Axmlns" | xmlstarlet p2x
| xmlstarlet sel -t -v "//biblFull/titleStmt/title"
J'ai pris l'habitude de nommer le fichier résultant ``xxx.no-default-ns.xml``
.. code-block:: bash
$ xmlstarlet pyx query-covid-and-recruiting-curl_internal.xml | grep -v "^Axmlns"
| xmlstarlet p2x >query-covid-and-recruiting-curl_internal.no-default-ns.xml
J'ai ensuite pu faire les explorations souhaitées sur le résultat d'une
recherche sur les études COVID.
.. code-block:: bash
$ xmlstarlet sel -t -v "/allTrials/@totalCount" isrctn/query-covid-and-recruiting-curl_internal.no-default-ns.xml
$ xmlstarlet sel -t -v "//fullTrial/trial/externalRefs/doi" isrctn/query-covid-and-recruiting-curl_internal.no-default-ns.xml
$ xmlstarlet sel -t -v "count(//fullTrial/trial/externalRefs/doi)" isrctn/query-covid-and-recruiting-curl_internal.no-default-ns.xml
``tr xsl/transform-search-3.xsl`` : transformation selon la feuille de style
``transform-search-3.xsl``.
Exemple de quelques transformations XSLT
.. code-block:: bash
$ xmlstarlet tr xsl/transform-search-3.xsl isrctn/query-covid-and-recruiting-curl_internal.no-default-ns.xml
$ xmlstarlet tr xsl/transform-search-3.txt.xsl isrctn/query-covid-and-recruiting-curl_internal.no-default-ns.xml
.. warning::
**xmlslarlet** repose sur la bibliothèque C **libxslt** qui met en œuvre la
version 1.0 de XSLT. Elle ne dispose donc pas de certaines fonctions comme
``replace()``.
https://www.oreilly.com/library/view/xslt-2nd-edition/9780596527211/ch05s06s02.html
.. code-block:: bash
$ sudo apt-cache depends xmlstarlet
xmlstarlet
Dépend: libc6
Dépend: libxml2
Dépend: libxslt1.1
$ sudo apt show libxslt1.1
Description: traitement XSLT 1.0 - bibliothèque d'exécution
xml2json
........
Test de l'outil `xml2json`_ par curiosité.
.. code-block:: bash
$ < isrctn/ISRCTN17072692-internal.no-default-ns.xml xml2json | jq '.'
xmllint
.......
Retour sur l'outil `xmllint`_ et son option ``--shell`` très intéressante pour
explorer un fichier XML.
Dommage que l'on ne puisse pas rappeler les commandes précédentes.
.. code-block:: bash
$ xmllint --shell isrctn/query-covid-and-recruiting-curl_internal.no-default-ns.xml
/ > help
> xpath //fullTrial/trial/externalRefs/doi/text()
> xpath count(//fullTrial/trial/externalRefs/doi)
> cd allTrials
allTrials > dir
allTrials > cd fullTrial[1]
fullTrial > ls
fullTrial > cd trial
trial > ls
trial > cat externalRefs
-------
10.1186/ISRCTN79815558
2021-000522-97
Nil known
VLA2001-301, IRAS 294164
trial > du
trial > grep doi
trial > cd trialDescription
trialDescription > ls
trialDescription > pwd
lxml
....
Une fois l'exploration terminée, codage de l'extraction de données en Python
avec `lxml`_.
.. _xmllint: http://xmlsoft.org/xmllint.html
.. _libxml2: http://xmlsoft.org/
.. _XMLStarlet: http://xmlstar.sourceforge.net/
.. _mémo xmlstarlet: http://xmlstar.sourceforge.net/doc/xmlstarlet.txt
.. _documentation xmlstarlet: http://xmlstar.sourceforge.net/doc/UG/xmlstarlet-ug.html
.. _xml2json: https://github.com/Inist-CNRS/node-xml2json-command
.. _lxml: https://lxml.de/installation.html
.. _Structurez vos données avec XML: https://tutoriel-xml.rolandl.fr/
Outils YAML
===========
yq
--
`yq`_ est utilisé par Emmanuel pour manipuler des fichiers YAML qui peuvent
être nombreux dans des outils comme Kubernetes.
.. _yq: http://mikefarah.github.io/yq/
Indices and tables
==================
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`