Outils pour utilisateurs

Outils du site


besson_sylvain:etapes_fusion

Différences

Ci-dessous, les différences entre deux révisions de la page.

Lien vers cette vue comparative

Les deux révisions précédentes Révision précédente
Prochaine révision
Révision précédente
besson_sylvain:etapes_fusion [2021/05/11 12:48]
Sylvain Besson
besson_sylvain:etapes_fusion [2021/07/22 19:03] (Version actuelle)
Sylvain Besson [Recordlinkage]
Ligne 1: Ligne 1:
 Allez vers la [[etapes|page précédente - étapes]] Allez vers la [[etapes|page précédente - étapes]]
  
-====== ​Fusionner ​les bases de données ======+====== ​Importer ​les données ======
  
-Une fois que l'on a pris connaissance des données que l'on souhaite obtenir des différentes bases de données, il est possible de créer un serveur local afin de stocker les données. Cela permet aussi d'​aligner les bases de données pour avoir un vocabulaire contrôlé. Pour cela, nous avons fait le choix d'​utiliser **[[projets_individuels:​documentation_sparql&#vocabulaire|GraphDB]]** pour sa facilité d'​utilisation et ses fonctionnalités. L'​importation des données se fait en deux étapes, d'​abord nous importons les instances et ensuite leur(s) propriété(s).+Une fois que l'on a pris connaissance des données que l'on souhaite obtenir des différentes bases de données, il est possible de créer un serveur local afin de stocker les données. Cela permet aussi d'​aligner les bases de données pour avoir un vocabulaire contrôlé. Pour cela, nous avons fait le choix d'​utiliser **[[besson_sylvain:​documentation_sparql#​GraphDB|GraphDB]]** pour sa facilité d'​utilisation et ses fonctionnalités. L'​importation des données se fait en deux étapes, d'​abord nous importons les instances et ensuite leur(s) propriété(s).
  
-====Importation des instances==== +====Importation des instances ​de Wikidata==== 
-Nous avons fait le choix d'​importer en premier les données provenant de Wikidata car c'est le plus gros silos avec plus de 50 000 instances. Il est important d'​importer à la fois les économistes et les juristes afin de ne pas avoir des doublons pour les personnes qui serait dans les deux populations. Ensuite, nous donnons à chaque instance un [[https://​fr.wikipedia.org/​wiki/​Uniform_Resource_Name|URN]] unique afin que lorsque l'on ajoute d'​autres bases de données, l'URN correspond à une personne réelle si elle est présente sur plusieurs bases de données. Pour cela il faut utiliser la clause [[https://​www.w3.org/​TR/​sparql11-query/#​func-uuid|UUID]] qui présente un URN sous la forme: "​urn:​uuid:​b9302fb5-642e-4d3b-af19-29a8f6d894c9"​.+Nous avons fait le choix d'​importer en premier les données provenant de Wikidata car c'est le plus gros silos avec plus de 130 000 instances. Il est important d'​importer à la fois les économistes et les juristes afin de ne pas avoir des doublons pour les personnes qui serait dans les deux populations. Ensuite, nous donnons à chaque instance un [[https://​fr.wikipedia.org/​wiki/​Uniform_Resource_Name|URN]] unique afin que lorsque l'on ajoute d'​autres bases de données, l'URN correspond à une personne réelle si elle est présente sur plusieurs bases de données. Pour cela il faut utiliser la clause [[https://​www.w3.org/​TR/​sparql11-query/#​func-uuid|UUID]] qui présente un URN sous la forme: "​urn:​uuid:​b9302fb5-642e-4d3b-af19-29a8f6d894c9"​.
  
-Préalablement,​ il est aussi possible dans Wikidata de fusionner des pages (et leur URI) qui correspondent à une même personne (la méthode est sur cette **[[projets_individuels:​documentation_wikidata|page]]**). ​+Préalablement,​ il est aussi possible dans Wikidata de fusionner des pages (et leur URI) qui correspondent à une même personne (la méthode est sur cette **[[documentation_wikidata|page]]**). ​ 
 + 
 +La requête se présente de la façon suivante:
  
-La requête se présente de la façon suivante: ​ 
 <code sparql> <code sparql>
 PREFIX ​ xsd:  <​http://​www.w3.org/​2001/​XMLSchema#>​ PREFIX ​ xsd:  <​http://​www.w3.org/​2001/​XMLSchema#>​
Ligne 19: Ligne 20:
  
 INSERT {  INSERT { 
-# La clause INSERT créé des triplets et les insére dans un graphe+### La clause INSERT créé des triplets et les insére dans un graphe
   GRAPH <​http://​economists_jurists.org/​import_wikidata>​ {    GRAPH <​http://​economists_jurists.org/​import_wikidata>​ { 
-# Avec cette clause, nous précisons que cette dans ce graphe  +### Avec cette clause, nous précisons que cette dans ce graphe  
-# que nous voulons nos instances, cela permettra par la suite  +### que nous voulons nos instances, cela permettra par la suite  
-# de différencier les bases de données+### de différencier les bases de données
        #​CONSTRUCT{ ​        #​CONSTRUCT{ ​
-# la clause CONSTRUCT est utile pour voir les triplets ​  +### la clause CONSTRUCT est utile pour voir les triplets ​  
-# que cela crée avant de les mettre dans un graphe.+### que cela crée avant de les mettre dans un graphe. ​(elle est ici désactivée)
     ?uri <​http://​www.w3.org/​1999/​02/​22-rdf-syntax-ns#​type>​ ome:21 .      ?uri <​http://​www.w3.org/​1999/​02/​22-rdf-syntax-ns#​type>​ ome:21 . 
-# Nous précisons ici que les instances sont de type personne  +### Nous précisons ici que les instances sont de type personne  
-#​ ("​ome:​21"​ correspond à la classe personne dans ontoME)+###​ ("​ome:​21"​ correspond à la classe personne dans ontoME)
     ?uri owl:sameAs ?person .     ?uri owl:sameAs ?person .
-# avec la propriété sameAs, nous indiquons que les URI créés avec +### avec la propriété sameAs, nous indiquons que les URI créés avec 
-# la clause UUID correspond aux instances de Wikidata.+### la clause UUID correspond aux instances de Wikidata.
   }   }
 } }
Ligne 38: Ligne 39:
 WHERE WHERE
   { SERVICE <​https://​query.wikidata.org/​sparql> ​   { SERVICE <​https://​query.wikidata.org/​sparql> ​
-# La SERVICE service permet d'​aller chercher les données Wikidata sur GrapheDB. ​+### La SERVICE service permet d'​aller chercher les données Wikidata sur GrapheDB. ​
       { SELECT DISTINCT ​ ?person       { SELECT DISTINCT ​ ?person
         WHERE         WHERE
-          ​{ { ?​person ​ wdt:​P106 ​ wd:Q188094 ; # Correpond aux économistes +           { { ?​person ​ wdt:​P106 ​ wd:Q188094 ; # économiste ("​economist) 
-                       ​wdt:​P569 ​ ?birthDate  +                       ​wdt:​P569 ​ ?​birthDate 
-#​ birthDate est la seule propriété qu'il faut conserver +               ​### 'birthDate' ​est la seule propriété qu'il faut conserver 
-# afin d'​être à l'​intérieur de nos limites chronologiques+               ### afin d'​être à l'​intérieur de nos limites chronologiques
               FILTER ( ?birthDate >= "​1770-01-01"​^^xsd:​dateTime )               FILTER ( ?birthDate >= "​1770-01-01"​^^xsd:​dateTime )
             }             }
             UNION             UNION
-           { ?​person ​ wdt:​P106 ​ wd:Q185351 ; # Correspond aux juristes+           { ?​person ​ wdt:​P106 ​ wd:Q185351 ; # juriste ("​jurist"​)
                        ​wdt:​P569 ​ ?birthDate                        ​wdt:​P569 ​ ?birthDate
               FILTER ( ?birthDate >= "​1770-01-01"​^^xsd:​dateTime )               FILTER ( ?birthDate >= "​1770-01-01"​^^xsd:​dateTime )
Ligne 56: Ligne 57:
       }       }
     BIND(uuid() AS ?uri)      BIND(uuid() AS ?uri) 
-# La clause UUID permet de créer un URN unique que  +### La clause UUID permet de créer un URN unique que  
-# nous pouvons lier avec la clause BIND aux instance. +### nous pouvons lier avec la clause BIND aux instance. 
-# LIMIT 10 Lors de l'​utilisation de la clause CONSTRUCT il vaut mieux n'​afficher qu'un petit nombre de résultats.+# LIMIT 10  
 +### Lors de l'​utilisation de la clause CONSTRUCT il vaut mieux n'​afficher qu'un petit nombre de résultats.
   }   }
 </​code>​ </​code>​
  
-==== Importation des propriétés ====+==== Importation des propriétés ​de Wikidata ​====
  
 Un fois que l'on a importé les instances, il est désormais possible d'​importer les propriétés une à une. Importer les propriétés individuellement permet d'​éviter les doublons d'​instance et c'est aussi plus facilement modulable. ​ Un fois que l'on a importé les instances, il est désormais possible d'​importer les propriétés une à une. Importer les propriétés individuellement permet d'​éviter les doublons d'​instance et c'est aussi plus facilement modulable. ​
Ligne 68: Ligne 70:
  
   * Nom   * Nom
 +  * Genre
   * Date de naissance   * Date de naissance
   * Date de mort   * Date de mort
Ligne 74: Ligne 77:
   * Nationalité   * Nationalité
   * École(s) fréquenté(s)   * École(s) fréquenté(s)
 +  * Poste(s) occupé(s)
   * URI VIAF   * URI VIAF
   * URI BnF Data   * URI BnF Data
Ligne 79: Ligne 83:
 Nous voulions ajouté à cela les personnes qui les ont influencées et celles qu'​elles ont influencées,​ ainsi que les écoles de pensées auxquelles elles ont appartenu, mais le nombre de personnes ayant ces propriétés est inférieur à 1% donc cela n'est pas pertinent. ​ Nous voulions ajouté à cela les personnes qui les ont influencées et celles qu'​elles ont influencées,​ ainsi que les écoles de pensées auxquelles elles ont appartenu, mais le nombre de personnes ayant ces propriétés est inférieur à 1% donc cela n'est pas pertinent. ​
  
-La méthode pour importer les propriétés est assez similaire à celle des instances. ​Elle se construit de la façon suivante: ​+La méthode pour importer les propriétés est assez similaire à celle des instances. ​L'​exemple suivant a pour but d'​importer les lieux de naissance dans la base de données. La requête ​se construit de la façon suivante: ​ 
 <code sparql> <code sparql>
 PREFIX ​ owl:  <​http://​www.w3.org/​2002/​07/​owl#>​ PREFIX ​ owl:  <​http://​www.w3.org/​2002/​07/​owl#>​
Ligne 89: Ligne 94:
  
 INSERT { INSERT {
-GRAPH <​http://​economists_jurists.org/​import_wikidata>​ { +  ​GRAPH <​http://​economists_jurists.org/​import_wikidata>​ { 
-   CONSTRUCT { +#CONSTRUCT { 
-    ?uri rdfs:label ?label . # Cela insert ​les étiquettes des personnes ​et les lie aux URI+    ?uri wdt:P19 ?placeOfBirth ​ ### Cela insert ​le lieu de naissance de chaque personne ​et les lie aux URI
   }   }
 } }
 WHERE WHERE
-  { ?uri  owl:​sameAs ​ ?person # Cela indique que les instances sont égales aux URI+  { ?uri  owl:​sameAs ​ ?​person ​ ### Cela indique que les instances sont égales aux URI
     { SERVICE <​https://​query.wikidata.org/​sparql>​     { SERVICE <​https://​query.wikidata.org/​sparql>​
-        { SELECT DISTINCT ​ ?person ?label+        { SELECT DISTINCT ​ ?person ?placeOfBirth
           WHERE           WHERE
-            { ?​person  ​rdfs:label  ?label +            { ?​person  ​wdt:P19  ?placeOfBirth 
-              {   { ?​person ​ wdt:​P106 ​   wd:Q188094 ; +              {   { ?​person ​ wdt:​P106 ​ wd:Q188094 ;  # economiste ("​economist"​) 
-                             ​wdt:​P569 ​   ?​birthDate ​+                             ​wdt:​P569 ​ ?birthDate
-                             ​rdfs:​label ​ ?label+
                     FILTER ( ?birthDate >= "​1770-01-01"​^^xsd:​dateTime )                     FILTER ( ?birthDate >= "​1770-01-01"​^^xsd:​dateTime )
                   }                   }
                 UNION                 UNION
-                  { ?​person ​ wdt:​P106 ​   wd:Q185351 ; +                  { ?​person ​ wdt:​P106 ​ wd:Q185351 ; # juriste ("​jurist"​) 
-                             ​wdt:​P569 ​   ?​birthDate ​+                             ​wdt:​P569 ​ ?birthDate
-                             ​rdfs:​label ​ ?label+
                     FILTER ( ?birthDate >= "​1770-01-01"​^^xsd:​dateTime )                     FILTER ( ?birthDate >= "​1770-01-01"​^^xsd:​dateTime )
                   }                   }
               }               }
-              FILTER ( lang(?​label) IN ("​en",​ "​fr"​) ) # le choix est de ne garder que les étiquettes en français et en anglais. 
             }             }
         }         }
Ligne 119: Ligne 121:
 #LIMIT 10 #LIMIT 10
 </​code>​ </​code>​
 +
 +Cette requête est utile, mais elle permet seulement d'​insérer dans la base de données, les URI de Wikidata des lieux de naissance. Il est plus intéressant de pouvoir insérer aussi d'​autres éléments comme l'​étiquette du lieu ou bien ses coordonnées géographiques. Une fois encore la requête est assez similaire, mais demande quelques raffinements supplémentaires : 
 +
 +<code sparql>
 +PREFIX ​ bd:   <​http://​www.bigdata.com/​rdf#>​
 +PREFIX ​ owl:  <​http://​www.w3.org/​2002/​07/​owl#>​
 +PREFIX ​ wdt:  <​http://​www.wikidata.org/​prop/​direct/>​
 +PREFIX ​ ome:  <​https://​ontome.net/​class/>​
 +PREFIX ​ wikibase: <​http://​wikiba.se/​ontology#>​
 +PREFIX ​ xsd:  <​http://​www.w3.org/​2001/​XMLSchema#>​
 +PREFIX ​ rdfs: <​http://​www.w3.org/​2000/​01/​rdf-schema#>​
 +PREFIX ​ wd:   <​http://​www.wikidata.org/​entity/>​
 +PREFIX p: <​http://​www.wikidata.org/​prop/>​
 +PREFIX psv: <​http://​www.wikidata.org/​prop/​statement/​value/>​
 +PREFIX ps: <​http://​www.wikidata.org/​prop/​statement/>​
 +
 +INSERT {
 +  GRAPH <​http://​economists_jurists.org/​import_wikidata>​ {
 +#CONSTRUCT {
 +    ?uri wdt:P19 ?​placeOfBirth . # à chaque instance tu ajoutes le lieu de naissance
 +    ?​placeOfBirth rdfs:label ?​labelOfPlaceOfBirth . # à ce lieu de naissance, tu lui ajoutes son étiquette
 +    ?​placeOfBirth wikibase:​geoLatitude ?lat . # À chaque lieu de naissance, tu lui ajoutes sa latitude
 +    ?​placeOfBirth wikibase:​geoLongitude ?long . # À chaque lieu de naissance, tu lui ajoutes sa longitude
 +  }
 +}
 +WHERE
 +  { ?uri  owl:​sameAs ​ ?person
 +    { SERVICE <​https://​query.wikidata.org/​sparql>​
 +        { SELECT ​ ?person ?​placeOfBirth ?​placeOfBirthLabel ?​labelOfPlaceOfBirth ?lat ?long
 +          WHERE
 +          { { SELECT DISTINCT ​ ?person ?​placeOfBirth ?​placeOfBirthLabel ?latitude ?longitude
 +          WHERE
 +            {  ?​person ​ wdt:P19 ?​placeOfBirth .
 +               ?​placeOfBirth p:P625 [ # p: Lie les entités aux déclarations (cf.https://​www.mediawiki.org/​wiki/​Wikibase/​Indexing/​RDF_Dump_Format#​Prefixes_used)
 +                             ​psv:​P625 [ # psv: lie profondément une valeur à un déclaration
 +                                        wikibase:​geoLatitude ?latitude; # permet d'​aller chercher la lattitude d'un lieu
 +                                        wikibase:​geoLongitude ?longitude;
 +                                      ] ;
 +                             ​ps:​P625 ?coord # ps: lie une valeur à une déclaration
 +                                    ] .
 +                SERVICE wikibase:​label # service qui permet d'​importer les étiquettes
 +                      { bd:​serviceParam
 +                                  wikibase:​language ​ "​en"​ # permet d'​aller chercher uniquement les étiquettes en anglais
 +                      }
 +              {   { ?​person ​ wdt:​P106 ​ wd:Q188094 ;  # économiste ("​economist"​)
 +                             ​wdt:​P569 ​ ?birthDate
 +                    FILTER ( ?birthDate >= "​1770-01-01"​^^xsd:​dateTime )
 +                  }
 +                UNION
 +                  { ?​person ​ wdt:​P106 ​ wd:Q185351 ; # juriste ("​jurist"​) ​
 +                             ​wdt:​P569 ​ ?birthDate
 +                    FILTER ( ?birthDate >= "​1770-01-01"​^^xsd:​dateTime )
 +                  }
 +              }
 +            }
 +        }
 + ​BIND(str(?​placeOfBirthLabel) AS ?​labelOfPlaceOfBirth)
 + ​BIND(str(?​latitude) AS ?lat)
 + ​BIND(str(?​longitude) AS ?long)
 +# La clause BIND permet d'​importer les données en enlevant les indications de format ou la langue dans laquelle l'​étiquette est importée. ex: transforme "​Paris"​@en en "​Paris"​.
 +    }
 +  }
 +}
 +}
 +#LIMIT 100
 +</​code>​
 +
 +Comme on peut le voir, Wikidata intègre pas mal de préfixes qui permettent d'​aller assez loin dans les données qui sont possibles d'​étudier ou de récupérer. Ces services sont documentés avec de nombreux exemples qu'il est possible de tester comme c'est le cas sur cette [[https://​www.wikidata.org/​wiki/​Wikidata:​SPARQL_query_service/​queries/​examples#​Wikibase_predicates|page]]. Pour ce qui est d'​importer les coordonnées géographiques,​ cette [[https://​en.wikibooks.org/​wiki/​SPARQL/​WIKIDATA_Precision,​_Units_and_Coordinates|ressource]] est très utile. ​
 +
 +Dans ce type de requête, il est aussi très important de bien les structurer afin de limiter le nombre d'​erreurs et des contre-sens dans les résultats obtenues. Un premier réflexe est de toujours réaliser un CONSTRUCT avant d'​importer les données, puisque qu'il permet vraiment de comprendre comment les données se trouveront dans la base de données.
 +
 +Il est aussi important dans ce type de requête avec beaucoup d'​éléments de bien imbriquer les différentes parties de la requête. Il est nécessaire par exemple de réaliser une sous-requête avec les instances que l'on souhaite récupérer,​ puis une requête où l'on cherche à obtenir une propriété. Ensuite, il est aussi très important de structurer la requête en remettant un SELECT lorsque l'on cherche à obtenir autres choses pour bien faire comprendre à la machine les différentes étapes. Ici, par exemple, un premier SELECT cherche les données “brutes” et dans un second SELECT la clause BIND, qui permet d'​enlever les formats, est utilisé. Cette façon de faire est d'​autant plus nécessaire lorsque l'on souhaite insérer les données.
 +
 +==== Importation des instances de BnF Data ====
 +Nous importons ensuite les instances de BnF Data. La méthode est assez similaire à celle de Wikidata, mais elle demande quelques étapes préliminaires. Avant d'​attribuer aux instances un URN, il faut d'​abord aligner les instances de BnF Data avec celle de Wikidata. Pour cela, plusieurs méthodes sont disponibles. Tout d'​abord,​ il est possible lier les instances de Wikidata qui renvoient vers une instance de BnF Data. Il faut commencer par importer la propriété BnF Data présente dans Wikidata. La requête peut être réalisée de la façon suivante :
 +
 +<code sparql>
 +PREFIX ​ owl:  <​http://​www.w3.org/​2002/​07/​owl#>​
 +PREFIX ​ wdt:  <​http://​www.wikidata.org/​prop/​direct/>​
 +PREFIX ​ ome:  <​https://​ontome.net/​class/>​
 +PREFIX ​ xsd:  <​http://​www.w3.org/​2001/​XMLSchema#>​
 +PREFIX ​ rdfs: <​http://​www.w3.org/​2000/​01/​rdf-schema#>​
 +PREFIX ​ wd:   <​http://​www.wikidata.org/​entity/>​
 +
 +INSERT {
 +  GRAPH <​http://​economists_jurists.org/​import_wikidata>​ {
 +# CONSTRUCT {
 +    ?uri wdt:P268 ?uri_bnf .
 +  }
 +}
 +WHERE
 +  { ?uri  owl:​sameAs ​ ?person
 +    { SERVICE <​https://​query.wikidata.org/​sparql>​
 +        { SELECT DISTINCT ​ ?person ?uri_bnf
 +          WHERE
 +            { ?​person ​ wdt:​P268 ​ ?uri_bnf1
 +              {   { ?​person ​ wdt:​P106 ​ wd:Q188094 ; # economist
 +                             ​wdt:​P569 ​ ?birthDate ;
 +                             ​wdt:​P268 ​ ?uri_bnf1
 +                    FILTER ( ?birthDate >= "​1770-01-01"​^^xsd:​dateTime )
 +                  }
 +                UNION
 +                  { ?​person ​ wdt:​P106 ​ wd:Q185351 ; # jurist
 +                             ​wdt:​P569 ​ ?birthDate ;
 +                             ​wdt:​P268 ​ ?uri_bnf1
 +                    FILTER ( ?birthDate >= "​1770-01-01"​^^xsd:​dateTime )
 +                  }
 +              }
 +              BIND(uri(concat("​https://​data.bnf.fr/​ark:/​12148/​cb",​ strafter(str(?​uri_bnf1),​ ""​))) AS ?uri_bnf2)
 +              BIND(uri(concat(str(?​uri_bnf2),​ "#​about"​)) AS ?uri_bnf) # Les deux BIND sont essentiels car dans wikidata seul la partie de l'URI propre à chaque URI est indiqué donc il est necessaire d'​ajouter les autres partie de l'URI pour pouvoir lier les instance de Wikidata à celles de BnF Data. 
 +            }
 +        }
 +    }
 +  }
 +# LIMIT 10
 +</​code>​
 +
 +Ensuite, il est possible de lier ces instances avec un identifiant VIAF qu'​elles ont en commun. Il faut bien sûr préablement importer la propriété VIAF présente dans Wikidata. Nous procédons de la façon suivante:
 +
 +<code sparql>
 +PREFIX ​ xsd:  <​http://​www.w3.org/​2001/​XMLSchema#>​
 +PREFIX ​ egr:  <​http://​rdvocab.info/​ElementsGr2/>​
 +PREFIX ​ owl:  <​http://​www.w3.org/​2002/​07/​owl#>​
 +PREFIX ​ ome:  <​https://​ontome.net/​class/>​
 +PREFIX ​ wdt:  <​http://​www.wikidata.org/​prop/​direct/>​
 +
 +INSERT {
 +  GRAPH <​http://​economists_jurists.org/​import_bnf_data>​
 +# CONSTRUCT
 +  {
 +    ?URI owl:sameAs ?person_bnf . 
 +### cela lie les instances ayant un identifiant VIAF avec celles ​
 +### qui sont déjà présentes dans le serveur qui ont un identifiant VIAF commmun.
 +  }
 +}
 +WHERE
 +  { SELECT DISTINCT ?URI ?person_bnf
 +    WHERE
 +      { ?URI  wdt:​P214 ​ ?uri_viaf # la requête cherche les identifiants VIAF dans la base de données locales
 +        { SERVICE <​https://​data.bnf.fr/​sparql>​
 +            { # La sous-requête cherche les identifiants VIAF dans BnF Data
 +              { ?​person_bnf ​ egr:​biographicalInformation ​ ?bio ;
 +                             ​egr:​dateOfBirth ​             ?bd ;
 +                             ​owl:​sameAs ​                  ?​uri_viaf
 +                  FILTER regex(?​uri_viaf,​ "​viaf.org",​ "​i"​)
 +                  BIND(strbefore(strafter(str(?​bd),​ "​http://​data.bnf.fr/​date/"​),​ "/"​) AS ?bd1)
 +                  FILTER ( ( ( ( ( regex(?bio, "​juriste",​ "​i"​) || regex(?bio, "​professeur de droit",​ "​i"​) ) || regex(?bio, "​docteur en droit",​ "​i"​) ) || regex(?bio, "​avocat",​ "​i"​) ) || regex(?bio, "​juge",​ "​i"​) ) || regex(?bio, "​magistrat",​ "​i"​) )
 +                  FILTER ( ?bd1 > "​1770"​ )
 +                }
 +              UNION
 +                { ?​person_bnf ​ egr:​biographicalInformation ​ ?bio ;
 +                               ​egr:​dateOfBirth ​             ?bd ;
 +                               ​owl:​sameAs ​                  ?​uri_viaf
 +                  FILTER regex(?​uri_viaf,​ "​viaf.org",​ "​i"​)
 +                  BIND(strbefore(strafter(str(?​bd),​ "​http://​data.bnf.fr/​date/"​),​ "/"​) AS ?bd1)
 +                  FILTER ( ( ( regex(?bio, "​économiste",​ "​i"​) || regex(?bio, "​Economiste",​ "​i"​) ) || regex(?bio, "​professeur d'​économie",​ "​i"​) ) || regex(?bio, "​docteur en économie",​ "​i"​) )
 +                  FILTER ( ?bd1 > "​1770"​ )
 +                }
 +            }
 +        }
 +      }
 +  }
 +# LIMIT 10
 +</​code>​
 +
 +Enfin une fois que l'on a réalisé ces étapes préliminaires pour lier les personnes présentes dans les deux bases de données, il est possible d'​importer les personnes qui ne sont pas présentes dans la base de données graphe. Il est nécessaire d'​enlever avec la clause MINUS les personnes qui sont déjà présentes, car comme le but est de créer un URI unique, si l'on ne procède pas ainsi les données seront dupliqués comme l'URI locale sera différent. C'est donc une étape qu'il faut faire avec beaucoup de précautions. Il est préférable de faire un SELECT seul au préalable afin de déterminer si les personnes ne sont effectivement pas déjà dans la base de données locale. Si les résultats ne correspondent à aucun URI BnF, alors il est possible de faire l'​insertion. Elle se réalise de la façon suivante (la même requête peut être réalisée pour le SELECT, il faudra enlever la partie INSERT) :
 +
 +<code sparql>
 +PREFIX ​ xsd:  <​http://​www.w3.org/​2001/​XMLSchema#>​
 +PREFIX ​ egr:  <​http://​rdvocab.info/​ElementsGr2/>​
 +PREFIX ​ owl:  <​http://​www.w3.org/​2002/​07/​owl#>​
 +PREFIX ​ ome:  <​https://​ontome.net/​class/>​
 +PREFIX ​ wdt:  <​http://​www.wikidata.org/​prop/​direct/>​
 +
 + ​INSERT {
 + GRAPH <​http://​economists_jurists.org/​import_bnf_data>​ {
 +# CONSTRUCT{
 +    ?uri <​http://​www.w3.org/​1999/​02/​22-rdf-syntax-ns#​type>​ ome:21 .
 +    ?uri owl:sameAs ?person_bnf .
 +  }
 +}
 + WHERE
 + { { SELECT DISTINCT ?person_bnf
 +   WHERE
 +    {
 +    { SERVICE <​https://​data.bnf.fr/​sparql>​
 +        {
 +          { ?​person_bnf ​ egr:​biographicalInformation ​ ?bio ;
 +                       ​egr:​dateOfBirth ​             ?bd
 +          BIND(strbefore(strafter(str(?​bd),​ "​http://​data.bnf.fr/​date/"​),​ "/"​) AS ?bd1)
 +          FILTER ( ( ( ( ( regex(?bio, "​juriste",​ "​i"​) || regex(?bio, "​droit",​ "​i"​) ) || regex(?bio, "​avocat",​ "​i"​) ) || regex(?bio, "​juge",​ "​i"​) ) || regex(?bio, "​magistrat",​ "​i"​) ) )
 +           ​FILTER ( ?bd1 > "​1770"​ )
 +          }
 +          UNION
 +          { ?​person_bnf ​ egr:​biographicalInformation ​ ?bio ;
 +                       ​egr:​dateOfBirth ​             ?bd
 +          BIND(strbefore(strafter(str(?​bd),​ "​http://​data.bnf.fr/​date/"​),​ "/"​) AS ?bd1)
 +              FILTER ( ( ( regex(?bio, "​économiste",​ "​i"​) || regex(?bio, "​economiste",​ "​i"​) ) || regex(?bio, "​professeur d'​économie",​ "​i"​) ) || regex(?bio, "​docteur en économie",​ "​i"​) )
 +           ​FILTER ( ?bd1 > "​1770"​ )
 +          }
 +        }
 +    }
 +     ​MINUS ​ {
 +     ?​uri_wikidata owl:sameAs ?person_bnf
 +    FILTER regex(str(?​person_bnf),​ "​bnf",​ "​i"​)
 +            }
 +}
 +        ORDER BY ?person_bnf
 +      }
 +    BIND(uuid() AS ?uri)
 +}
 +#LIMIT 100
 +</​code>​
 +
 +Toute la méthode réalisée pour lier les instances BnF Data et Wikidata peut être réalisée pour DBpedia.
 +Nous l'​allons donc pas la détailler ici. Mais vous pouvez retrouver l'​ensemble des requêtes dans notre espace **[[https://​github.com/​Semantic-Data-for-Humanities/​Economists_Jurists/​blob/​main/​Notebooks/​data/​sparql_queries.db|Github]]** en téléchargeant la base de données et en l'​ouvrant avec un logiciel de requêtage de base de données comme [[https://​dbeaver.io/​|DBeaver]] ([[https://​dbeaver.io/​download/​|lien]] vers le téléchargement,​ Mac Os, Windows et Linux).
 +
 +
 +Enfin la dernière méthode qui permet de connaître si des personnes sont dans plusieurs bases de données, c'est le Recordlinkage. Nous la présentons en détail ci-dessous.
 +
 ==== Recordlinkage ==== ==== Recordlinkage ====
  
-Ensuite, il est possible de réaliser la fusion à proprement parler. Pour cela, il faut utiliser une méthode ​de //record linkage// qui permet de calculer la proximité entre deux chaînes de caractères. ​La méthode ​est répétée entre chaque ligne pour les variables choisies. Nous avons choisis d'​utiliser cette méthode sur le nom de la personne, sa date de naissance et sa date de décès. Les deux derniers ont l'​avantage d'​avoir un format strict (YYYY-MM-DD),​ donc améliore considérablement les scores.  ​+Cette méthode permet de calculer la proximité entre deux chaînes de caractères. ​Elle est répétée entre chaque ligne pour les variables choisies. Nous avons choisis d'​utiliser cette méthode sur le nom de la personne, sa date de naissance et sa date de décès. Les deux derniers ont l'​avantage d'​avoir un format strict (YYYY-MM-DD),​ donc améliore considérablement les scores.  ​
  
 Pour réaliser cela, plusieurs bibliothèques python existent. Deux serons présentées ici, et elles sont présentées dans ce très bon article [[https://​pbpython.com/​record-linking.html|recordlinking]] posté par Chris Moffitt. Pour réaliser cela, plusieurs bibliothèques python existent. Deux serons présentées ici, et elles sont présentées dans ce très bon article [[https://​pbpython.com/​record-linking.html|recordlinking]] posté par Chris Moffitt.
Ligne 169: Ligne 391:
 </​code>​ </​code>​
  
-Il est possible de le faire sur l'​ensemble des données, mais au regard volume et de la puissance de calcul nécessaire,​ nous le faisons que pour une seule variable. À nous choisissons celle qui donne le plus grand nombre candidat potentiel. ​+Il est possible de le faire sur l'​ensemble des données, mais au regard volume et de la puissance de calcul nécessaire,​ nous le faisons que pour une seule variable. À nous choisissons celle qui donne le plus grand nombre candidat potentiel.  Il est aussi de possible de réaliser la même méthode sur un seul jeu de données en mettant deux fois la même variable et le même jeu de données. Cela nous a été notamment très utilisé pour déceler des personnes présentes deux fois sous un URI différent.
  
 Ensuite, il faut réaliser les comparaisons:​ compare = recordlinkage.Compare() Ensuite, il faut réaliser les comparaisons:​ compare = recordlinkage.Compare()
Ligne 185: Ligne 407:
 features = compare.compute(candidates,​ BnF_Data, DBpedia) features = compare.compute(candidates,​ BnF_Data, DBpedia)
 </​code>​ </​code>​
 +
 Pour les dates de naissance et de mort, nous avons fait le choix de prendre les valeurs exacts afin de contrer des éventuels différences de formats (ex: dans BnF Data certaines dates sont écrites ainsi: "​18.."​ la bibliothèque n'​arrive pas à les interpréter et ne sort pas de résultats). ​ Pour les dates de naissance et de mort, nous avons fait le choix de prendre les valeurs exacts afin de contrer des éventuels différences de formats (ex: dans BnF Data certaines dates sont écrites ainsi: "​18.."​ la bibliothèque n'​arrive pas à les interpréter et ne sort pas de résultats). ​
  
 Il faut ensuite de fusionner les données prenant les comparaisons avec un haut score. Ce dernier indique sur combien de variable un score élevé à été trouvé et les additionnent:​ Il faut ensuite de fusionner les données prenant les comparaisons avec un haut score. Ce dernier indique sur combien de variable un score élevé à été trouvé et les additionnent:​
 +
 <code python> <code python>
 potential_matches = features[features.sum(axis=1) > 1].reset_index() potential_matches = features[features.sum(axis=1) > 1].reset_index()
Ligne 193: Ligne 417:
 potential_matches potential_matches
 </​code>​ </​code>​
-L'​ensemble de la méthode est disponible sur un **[[https://​github.com/​Semantic-Data-for-Humanities/​Economists_Jurists/​blob/​development/​Notebooks/​Merge/​Merge_DBpedia_BnF_Data_Wikidata.ipynb|carnet]]** sur Github.+ 
besson_sylvain/etapes_fusion.1620730109.txt.gz · Dernière modification: 2021/05/11 12:48 par Sylvain Besson