Augmenter le niveau sémantique des données partagées

Bonjour,

Je vous propose ici un troisième sujet d’échange concernant les données tabulaires (et plus généralement les données partagées suivant un format texte).

Le premier sujet concernait l’amélioration de la qualité des jeux de données par la définition et le contrôle des relations entre champs qui se traduisait par l’ajout d’une nouvelle propriété « relationship » dans TableSchema ( sujet 3750 ). Vos retours étaient plutôt positifs sur l’intéret de l’ajout de cette
nouvelle propriété. La demande a été effectuée sur le site frictionless (issue #803) et est en attente de validation.

Le second sujet concernait le format d’échange des données tabulaires au travers d’un questionnement sur l’avenir du format CSV ( sujet 3946 ). Vous avez été nombreux à vous exprimer et il me reste à vous envoyer une synthèse (d’ici mi-mars).

Le troisième sujet concerne une proposition d’amélioration du niveau sémantique des données échangées qui permet également un référencement complet des données (voir ci-dessous et également le document de présentation).

Est-ce que ça vous semble intéressant, utile, simple à mettre en oeuvre ?
Est-ce que des réflexions de ce type ont déjà été engagées ?
Comment est-ce qu’un projet autour de ce sujet devrait être piloté ?

Merci d’avance pour vos remarques et retours !


0 - Principes

Aujourd’hui, le niveau niveau sémantique des données partagées reste peu élevé (bien souvent limité à des formats de base : nombre, chaînes de caractères). Ceci est du au faible typage des données des fichiers CSV (chaînes de caractères uniquement) et des données JSON (nombres, chaînes de caractères, ‹ array ›, ‹ object ›).
La proposition qui est faite consiste à ajouter aux données échangées un typage (facultatif) et un nommage (facultatif également).

Les entités ainsi construites (dénommées NTV pour named typed value) sont un triplet avec un élément obligatoire (la valeur au format JSON) et deux éléments optionnels (nom, type).

Par exemple, la localisation de Paris peut être représentée par :

  • un nom: « Paris »,
  • un type : les coordonnées d’un point selon le standard geoJSON,
  • une valeur : [ 2.3522, 48.8566]

La façon la plus simple pour ajouter ces informations est d’utiliser un JSON-object avec un seul membre utilisant la syntaxe JSON-ND pour le premier terme du membre et la valeur au format JSON pour le second terme du membre.

Pour l’exemple ci-dessus, la représentation JSON est :
{ « paris:point » : [2.3522, 48.8566] }

Avec cette approche, on peut définir trois entités NTV:

  • une entité primitive qui n’est composée d’aucune autre entité (NTV-single),
  • deux entités structurées : une collection non ordonnée d’entités NTV (NTV-set) et une séquence ordonnée d’entités NTV (NTV-list).

ainsi que deux formats JSON :

  • format simple lorsque le nom et le type ne sont pas présent (c’est le cas usuel des données CSV),
  • format nommé lorsque le nom ou le type est présent (cf exemple ci-dessus pour une entité NTV-single).

Exemple d’une entité composée de deux autres entités:

  • { « villes::point »: [[2.3522, 48.8566], [4.8357, 45.7640]] } pour une entité NTV-list
  • { « villes::point »: {« paris »:[2.3522, 48.8566], « lyon »:[4.8357, 45.7640]}} pour une entité NTV-set

Nota : Cette syntaxe peut également être utilisée pour les entêtes de fichier CSV

1 - Structure

Le triplet NTV (nom, type, valeur) est représenté suivant un format JSON inspiré du projet de RFC JSON-ND :

  • valeur (si nom et type ne sont pas documentés)
  • { « nom » : valeur } (si le nom est documenté mais pas le type)
  • { « :type » : valeur } pour les entités primitives et { « ::type » : valeur } pour les entités structurées (si le type est documenté mais pas le nom)
  • { « nom:type » : valeur } pour les entités primitives et { « nom::type » : valeur } pour les entités structurées (si nom et type sont documentés).

Par ailleurs, le type intègre une notion d’espaces de noms qui peuvent être imbriqués.

Par exemple, le type : « ns1.ns2.type » signifie que :

  • ns1. est un espace de noms défini dans l’espace de noms global,
  • ns2. est un espace de noms défini dans l’espace de noms ns1.,
  • type est défini dans l’espace de noms ns2.

Cette structuration du type permet de référencer tout type de données.

2 - Exemples de représentations JSON

  • NTV-single, format simple :
    • « lyon »
    • 52.5
    • { }
  • NTV-single, format nommé :
    • { « paris:point » : [2.3522, 48.8566] }
    • { « :point » : [4.8357, 45.7640] }
    • { « city » : « paris » }
  • NTV-list, format simple :
    • [ [2.3522, 48.8566], {« lyon » : [4.8357, 45.7640]} ]
    • [ { « :point » : [2.3522, 48.8566]}, {« :point » : [4.8357, 45.7640]} ]
    • [ 4, 45 ]
    • [ « paris » ]
  • NTV-list, format nommé :
    • { « villes::point » : [ [2.3522, 48.8566], [4.8357, 45.7640] ] }
    • { « ::point » : [ [2.3522, 48.8566], {« lyon » : [4.8357, 45.7640]} ] }
    • { « liste simple » : [ 4, 45.7 ] }
    • { « date generique::dat » : [ « 2022-01-28T18-23-54Z », « 2022-01-28 », 1234.78 ] }
  • NTV-set, format simple :
    • { "nom”: « white », « prenom »: « walter », « surnom »: « heisenberg » }
    • { « paris:point » : [2.3522, 48.8566] , « lyon » : « france » }
    • { « paris » : [2.3522, 48.8566], «  » : [4.8357, 45.7640] }
  • NTV-set, format nommé :
    • { « villes::point »: { « paris »: [2.352, 48.856], « lyon »: [4.835, 45.764]}}
    • { « villes » : { « paris:point » : [2.3522, 48.8566] , « lyon » : « france »} }
    • { « ville » : { « paris » : [2.3522, 48.8566] } }

3 - Typage des données

La structure des types par espace de noms permet d’avoir au niveau global des types correspondant aux standards reconnus.
Des types génériques peuvent également être définis (calcul du type exact lors du décodage de la valeur).

Par exemple, on pourrait avoir :

pour la datation (suivant ISO8601 et Posix) :

type (generic) exemple de valeur
year 1998
month 10
day 21
week 38
hour 20
minute 18
second 54
timeposix (dat) 123456.78
date (dat) “2022-01-28”
time (dat) “T18:23:54”, “18:23”, “T18”
datetime (dat) “2022-01-28T18-23-54Z”
timearray (dat) “2022-01-28T18-23-54+0400”
timeslot (dat) [date1, date2]

pour la durée (suivant ISO8601 et Posix) :

type (generique) exemple de valeur
timeinterval (dur) « 2007-03-01T13:00:00Z/2008-05-11T15:30:00Z »
durationiso (dur) « P0002-10- 15T10:30:20 »
durposix (dur) 123456.78

pour la localisation (suivant RFC7946 et Open Location Code):

type (generique) exemple de valeur
point (loc) [ 5.12, 45.256 ] (lon, lat)
line (loc) [ point1, point2, point3 ]
ring [ point1, point2, point3 ]
multiline [ line1, line2, line3]
polygon (loc) [ ring1, ring2, ring3]
multipolygon (loc) [ poly1, poly2, poly3 ]
bbox (loc) [ -10.0, -10.0, 10.0, 10.0 ]
geojson (loc) {“type”: “point”, “coordinates”: [40.0, 0.0] }
codeolc (loc) “8FW4V75V+8F6”

Pour les données tabulaires:

NTVtype NTVvalue
row JSON-array of JSON-NTV
field JSON-array of NTVvalue (following JSON-TAB format)
table JSON-array of NTVvalue of fields with the same length

Pour les chaînes de caractères normalisées :

Le type pourrait être uri, cf exemples :

Des espaces de noms pourraient également être définis pour référencer par exemple :

  • des entités géopolitiques : code pays ISO3166-1 (par exemple fr. pour la France)
  • des catalogues de données, par exemple :
NTVtype example JSON-NTV
schemaorg.
{ “:schemaorg.propertyID”: “NO2” }
{ “:schemaorg.unitText”:”µg/m3”}
darwincore. { “:darwincore.acceptedNameUsage”: “Tamias minimus” }

4 - Exemple d’utilisation d’un espace de noms fr.

Cet espace de noms est dédié au jeux de données associés à l’espace géopolitique France (voir également le document de présentation).

Un espace de noms définit :

  • des identifiants utilisés pour accéder à des données complémentaires,
  • des espaces de noms associés à des à des catalogues ou jeux de données,
  • des entités structurées utilisées pour faciliter l’utilisation de données

4.1 - Identifiants

Ils pourraient correspondre à des identifiants utilisés dans de nombreux jeux de données référencés (via un schéma de données ou un modèle de données).

Par exemple :

type définition exemple
fr.reg code région 93
fr.dep code département 60
fr.com code INSEE commune 77284
fr.cp code postal 76450
fr.iris code IRIS ilôt 977010101
fr.sirenc code SIREN commune 217702737
fr.epci code EPCI 244301107
fr.arm code arrondissement municipal 842
fr.can code canton 8208
fr.ctcd code collectivité territoriale 6AE
fr.cog code officiel géographique COG 99114
fr.cov code zone covoiturage 35238-C-012
fr.zfe code ZFE 200046977-ZFE-001
fr.bnls code lieu stationnement 04070-P-001
fr.uic code UIC gare 8757449
fr.iata code IATA aéroport CDG
fr.naf code NAF 23
fr.siret code SIRET enterprise 41844736300015
fr.siren code SIREN enterprise 418447363
fr.fantoir code FANTOIR voie 4500023086F
fr.parcel code parcelle 670010320165
fr.iua code id adresse 27115_0110_00017_bis
fr.struc code SP+ structure 3Tn8gzTdcz
fr.synop code SYNOP station météo 07130
fr.lcsqa code LCSQA station mesure FR01021
fr.uai code UAI établissement 0951099D
fr.aca code académies 22
fr.circo code circonscription 69002
fr.finessej code FINESS entité juridique 790001606
fr.finesset code FINESS établissement 790010375
fr.rna code WALDEC association 843S0843004860
fr.spi code SPI numéro fiscal 1899582886173
fr.nir code NIR sécurité sociale 164026005705953

4.2 Espaces de noms

Les espaces de noms pourraient correspondre à des catalogues ou des jeux de données dont les types de données sont identifiés dans des modèles de données ou bien dans des schémas de données référencés.

Par exemple :

type example JSON-NTV
fr.sandre.
{ « :fr.sandre.CdStationHydro »: K163 3010 01 }
{ « :fr.sandre.TypStationHydro »: « standard » }
fr.synop.
{ « :fr.synop.numer_sta »: 07130 }
{ « :fr.synop.t »: 300, « :fr.synop.ff »: 5 }
fr.IRVE.
{ « :fr.IRVE.nom_station »: « M2026 » }
{ « :fr.IRVE.nom_operateur »: « DEBELEC » }
fr.BAN.
{ « :fr.BAN.numero »: 54 }
{ « :fr.BAN.lon »: 3.5124 }

4.3 Entités

Elles pourraient correspondre à des assemblages de données associées à une structure définie.

Par exemple :

type example JSON-NTV
fr.parcelle
{“maParcelle:fr.parcelle”: [ 84500, 0, I, 97]}
(fr.cp, fr.cadastre.préfixe, fr.cadastre.section, fr.cadastre.numéro)
fr.adresse
{“monAdresse:fr.adresse”: [ 54, bis, rue de la mairie, 78730 ]
(fr.BAN.numero, fr.BAN.rep, fr.BAN.nom_voie, fr.cp)
1 « J'aime »

La sémantisation est intéressante au delà des données tabulaires et/ou partagée en format texte et permet de ne plus se poser de question sur le typage. Elle donne un sens à une valeur et on peut en déduire un typage (variable bien sûr en fonction de l’outil qui traitera ces données/valeurs).

La réflexion doit être globale, puis voir ensuite comment on peut indiquer la sémantique avec les données, que ce soit en CSV, en JSON, en XML… voire Parquet !

nombre, chaîne = typage mais pas sémantisation

Si je déclare que 69123 est un code INSEE de commune, ceci m’indiquera que c’est donc à typer sous forme de chaîne de 5 caractères, je peux même avoir une regexp pour n’accepter que des valeurs conformes (mais pas forcément valables).

XML gère cela depuis longtemps, son défaut est d’être verbeux et peu lisible.

Marrant de retrouve pas mal de ref:xxx d’OpenStreetMap avec une logique un peu différente.
fr.reg, fr.dep, fr.com c’est ud ref:INSEE dans OSM et on sait que c’est une région, un département ou une commune par un autre tag: admin_level.

On a aussi les ref:UIC ref:IATA ref:FR:FANTOIR ref:UAI…

NTV c’est quelque chose d’existant (rien trouvé) ou le standard N+1 sur le sujet ?

Remember: https://imgs.xkcd.com/comics/standards.png

1 « J'aime »

Merci Christian pour ce retour, les remarques et commentaires sont toujours riches et pertinents !

Effectivement, le terme « typage » est ambiguë : tel que je l’ai employé ça fait référence à des entités qui ont un sens indépendamment de leur représentation, par exemple un lieu géographique, une date, un département…Tel qu’il est employé dans les commentaires, il fait référence à une représentation (codage) comme par exemple une chaîne de caractères.
Sur le fond, je pense qu’on est d’accord pour différencier le codage d’une entité, de l’entité elle-même.

L’autre remarque est qu’une déclaration implicite ne permet pas de donner un sens explicite. Par exemple, si je déclare que [3.2, 45.8] est une coordonnée, je sais peut-être implicitement ce qu’est une coordonnée, mais je ne saurai pas interpréter automatiquement ce tableau de deux valeurs si je n’ai pas référencé explicitement l’entité « coordonnée » et défini clairement les règles de codage et de décodage de ce qu’est une « coordonnée ».

Comme indiqué dans le lien au chapitre 1, le fait d’associer un nom et un type à une valeur et de grouper nom et type dans une seule chaîne de caractère est issu du format JSON-ND qui a fait l’objet d’un projet de RFC et d’une demande IANA . Ce projet ne semble pas avoir abouti (j’ai contacté la personne qui a porté ce projet mais je n’ai pas encore eu son retour).

Sur la notion de standard N+1 je suis bien entendu d’accord et la meilleure façon de progresser est de faire converger des standards existants plutôt d’en créer de nouveau (d’autant plus que je n’ai ni le poids, ni la légitimité, ni l’autorité pour imposer un standard nouveau !). La proposition faite me semble aller dans ce sens :

  • elle propose d’enrichir le format JSON sans le remplacer (les représentations actuelles restent valables),
  • elle propose également d’enrichir les noms de champs des tableaux CSV en y ajoutant le type (mais ça reste facultatif),
  • elle propose de référencer explicitement les entités qui sont partagées de façon à en automatiser le codage/décodage. Cette tache est de toutes les façons à réaliser qu’elle que soit le standard qui serait utilisé (sachant qu’il s’agit avant tout de compiler ce qui existe et non d’inventer des entités nouvelles).

Plus globalement, est-ce que cette proposition fait écho à des attentes et vaut la peine d’être travaillée et instruite plus avant avec une organisation à définir ou bien au contraire ce type de réflexion risque « d’user le soleil » ?

Mon avis très global, c’est que plusieurs concepts sont mélangés: nommage, codage, typage, description sémantique

Il faudrait déjà d’accord sur chacun de ces concepts avant d’envisager les liens entre eux si ils sont nécessaires.

Exemple: le nommage des colonnes est un pis-aller par manque de description sémantique.

Le codage (plutôt un sujet interne, un choix d’implémentation dans du code: exemple savoir comment un BIGINT est codé au niveau binaire par Postgresql) et la représentation (plutôt un sujet externe… comment Postgresql va présenter ce BIGINT aux clients, et potentiellement il le fait de plusieurs façon en fonction d’un accès binaire ou textuel). Ceci n’est en général pas un sujet d’interopérabilité, car la sémantisation est un sujet d’interopérabilité des données.


Quand je parlais de standard N+1 c’est vraiment pour dire qu’il faut déjà bien connaître les standards actuels, leurs forces, leurs faiblesses avant d’imaginer quelque chose de plus (le sens du XKCD).

Bis: NVT ça sort d’où ?

Est-ce qu’on n’a pas déjà https://json-schema.org/ pour ça ? (au moins pour JSON; comme le dit Christian il faut sans doute voir plus large).

Je pense qu’en effet, on s’égare et que l’on pourrait s’appuyer d’une part sur les niveaux d’interopérabilité tel qu’ils sont définis dans le RGI : niveau technique, niveau syntaxique, niveau sémantique et d’autre par sur la notion de type qui me semble assez bien définie.

Par exemple si on considère une entité associée au profil géomatique (P5 du RGI) du niveau sémantique, celle-ci serait décrite par un modèle UML (cf recommandation RGI) et elle aurait par exemple un type « département » (la notion de type est clairement définie dans UML) et aurait par exemple un attribut « contour » représenté par une donnée au format GeoJSON.
Le format GeoJSON est classé au niveau syntaxique du RGI. Ce format définit lui aussi plusieurs types : types structurés (feature, feature collection) et types élémentaires (Point, MultiPoint, LineString, MultiLineString, Polygon, MultiPolygon, GeometryCollection) et s’appuie sur une représentation Json.
Le format Json est également classé au niveau syntaxique dans le RGI. Il définit également plusieurs types : types primitifs (strings, numbers, booleans, null) et deux types structurés (objects, arrays) et s’appuie sur une représentation Unicode (principalement UTF8).
Le format UTF8 est classé au niveau syntaxique / encodage du RGI. Il définit un seul type : caractère abstrait (associé à un point de code). Ces caractères abstraits sont représentés par plusieurs octets.

Le W3C a défini un modèle des données tabulaires ( Model for Tabular Data and Metadata on the Web - W3C Recommendation 17 December 2015 ) qui définit explicitement la notion de ‹ Datatype › et de ‹ header › pour les CSV.

Comme indiqué, la représentation JSON avec un nom, un type et une valeur est reprise du format JSON-ND (cf lien donné). Je n’ai pas d’autre références.
Sur le fait d’associer à une valeur, un type et un nom, c’est assez générique. On le trouve dans la majorité des outils et applications (ce n’est pas vraiment nouveau).

Finalement tout est type… comme dans unix ou tout est fichier :wink:

Oui, tout à fait, s’il existe un schéma de données (json-schema ou tableschema) associé au jeu de données, le type du champ sera défini dans le schéma de données (propriété associé au champ) et non dans le jeu de données.
Dans le cas ou cela ne concerne pas un champ complet mais uniquement certaines cellules, le type pourra être précisé de façon unitaire dans chaque cellule.

Il reste dans tous les cas à définir la liste des types qui sont applicables.

Ou comme on a un « payload » dans les couches réseau et protocoles.