.. 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`