Linked (Open) Data - Von der Theorie zur Praxis

Daten verknüpfen

  Daten verknüpfen
  SPARQL-Endpoint für Abfragen (Demo)

 

 

Daten verknüpfen

Grundsätzlich kann die Verknüpfung von Daten auf zwei unterschiedliche Wege realisiert werden.

Die erste Möglichkeit wäre, dass in einzelnen Datensätzen Links auf einen andern Datensatz implementiert werden. In unserer Beispielapplikation hätten wir demnach in die Bibliotheksstatistik Links eingepflegt, die jede Bibliothek einem Datensatz eines Bezirks auf Geonames zugeordnet hätte. Wir haben uns gegen diese Möglichkeit entschieden, da diese Lösung mit enorm viel Handarbeit verbunden gewesen wäre. Wie das realisiert werden kann, erläutern wir in der LED Beispielsapplikation.

Die zweite Möglichkeit verknüpft die Daten ‚on the fly‘ also bei der Abfrage. In unserer Beispielapplikation nutzen wir den SPARQL-Endpoint unseres Sesame-Stores, um Suchabfragen abzusetzen, die die verschiedenen Datensätze miteinander verknüpfen. Wir suchen somit in der Bibliotheksstatistik nach den Bibliotheken, die uns interessieren und gewinnen daraus u.a. die Postleitzahl. Aus dem lokalen Datensatz der Geonames-Daten gewinnen wir zu den Postleitzahlen die Bezirksdaten. Auf diese Weise können wir mit der Abfrage z.B. die Zahl der Bibliotheken in einem Bezirk eruieren.

Die SPARQL-Abfrage zur Anzahl der Bibliotheken pro Bezirk, die im SPARQL-Endpoint unserer Beispielsapplikation (Link zum Sesame-Store einfügen) eingegeben werden kann, lautet wie folgt:

PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
PREFIX gont: <https://gont.ch/>
PREFIX owl: <http://www.w3.org/2002/07/owl#>
PREFIX dbo: <http://dbpedia.org/ontology/>
PREFIX lgd: <http://linkedgeodata.org/triplify/>
PREFIX lgdo: <http://linkedgeodata.org/ontology/>
PREFIX lgd-adress: <http://linkedgeodata.org/ontology/addr/>
PREFIX lod-libraries: <http://linkeddata.fh-htwchur.ch/libraryData#>
PREFIX gn: <http://www.geonames.org/ontology#>
PREFIX lod-swissmaps: <http://linkeddata.fh-htwchur.ch/swissmap#>
SELECT ?district_shortName (COUNT(?district_shortName) as ?numberOfLibraries)
WHERE {
    SERVICE <http://data.admin.ch/query> { 
        SELECT ?muni ?muni_Version ?dbpediaSameAs ?maxgontdate ?district_shortName
        WHERE {
            # get current admission event
            { 
            SELECT ?municipality_id (MAX($gontdate) AS ?maxgontdate)
                WHERE {
                    ?municipality           rdf:type                      gont:Municipality; 
                                            gont:id                       ?municipality_id;
                                            gont:municipalityVersion      ?municipalityVersion . 
                    ?municipalityVersion    gont:admissionEvent           ?admissionEvent . 
                    ?admissionEvent         gont:date                     ?gontdate
                }
                GROUP BY ?municipality_id
                # ORDER BY DESC(?gontdate)
            }
                
            ?muni           rdf:type                    gont:Municipality; 
                            gont:id                     ?municipality_id;
                            owl:sameAs             		?dbpediaSameAs;
                            gont:municipalityVersion 	?muni_Version . 
                                     
                
                
            ?muni_Version   gont:admissionEvent         ?admi_Event; 
                            gont:district               ?district . 
            ?district       gont:id                     ?district_id; 
                            gont:longName               ?district_longName ; 
                            gont:shortName              ?district_shortName .
            ?admi_Event     gont:date                   ?maxgontdate . 
                
        }
        ORDER BY ?municipality_id
    }
     
        SERVICE <http://dbpedia.org/sparql> {
        ?dbpediaSameAs dbo:postalCode ?postalcode
    }    
     
     
    ?library_statDat lod-libraries:postalCode_stat ?postalcode;
    rdfs:label ?libstat_label 
}
GROUP BY ?district_shortName

Und das Ergebnis stellt sich dann so dar (Auszug):

Aarau 4
Affoltern 15
Aigle 7
Albula 3
Andelfingen 15
Arbon 7
Arlesheim 6
Baden 6
Bellinzona 3
Bernina 2
Bern-Mittelland 77
Biel/Bienne 3
Bremgarten 10
Brig 3

Was hier in der Beschreibung einfach klingt, wäre in einer traditionellen Umgebung nur mit viel Handarbeit oder mittels Programmierung möglich. Da die Daten jetzt in RDF vorliegen und über einen SPARQL-Endpoint zur Verfügung stehen, kann das Ergebnis mit einer Abfrage ermittelt werden. Ändert sich die Fragestellung (z.B. Anzahl Bibliotheksbücher pro Kanton), kann dies mit der Veränderung der Abfrage erreicht werden.

Allerdings gilt es festzuhalten, dass die SPARQL-Abfragen selbst auch nicht zu unterschätzen sind, da diese nur korrekt formuliert werden, wenn die Struktur der Daten bekannt ist und selbst in einer Sprache abzufassen sind, die nicht leicht zu lernen ist.

Analog zur Verknüpfung der Bibliotheksstatistik mit Daten von Geonames kann das auch mit Daten von OpenStreetMap gemacht werden. So gewinnen wir beispielsweise die Adressen oder die Koordinaten der Bibliotheken.

Und schliesslich können wir von unserem SPARQL-Endpoint auch auf jeden anderen öffentlich zugänglichen SPARQL-Endpoint zugreifen und können so unsere Bibliotheksstatistikdaten auch mit andern Daten in Verbindung bringen. Mit einer federated query können wir nun beispielsweise ermitteln, auf wie viele Einwohner in einem Bezirk eine Bibliothek kommt. Diese Abfrage wäre allerdings sehr komplex. Wir wählen daher einen einfacheren Weg. Wir suchen nach der Anzahl Bibliotheken pro Bezirk einerseits und nach Anzahl Einwohner pro Bezirk. Erst im Anschluss daran verrechnen wir die beiden Ergebnisse.

Die Abfrage nach den Einwohnern pro Bezirk lautet:

PREFIX skos: <http://www.w3.org/2004/02/skos/core#>
PREFIX bfs: <http://data.admin.ch/bfs/property/>
PREFIX qb: <http://purl.org/linked-data/cube#>
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX gont: <https://gont.ch/>
PREFIX bfsclass: <http://data.admin.ch/bfs/class/2011.2/>
PREFIX purlterms: <http://purl.org/dc/terms/>
SELECT ?district_shortName  SUM(?allnumber) AS ?district_number_persons
WHERE {
     {
          SELECT max(?gontdate) ?district ?district_longName ?district_shortName ?district_id ?munistat_label ?allnumber {
                ?municipalityVersion 
                     gont:admissionEvent ?admissionEvent; 
                     gont:district ?district . 
                ?admissionEvent gont:date ?gontdate . 
                ?district 
                     gont:longName ?district_longName ; 
                     gont:shortName ?district_shortName ;
                     gont:id ?district_id . 
          
                ?muni_district a gont:Municipality; 
                     gont:municipalityVersion  ?municipalityVersion;
                     gont:id ?munidistrict_id .
                     
                ?muni_stat  
                     skos:notation ?munistat_id; 
                     skos:prefLabel ?munistat_label . 
                
                {
                     SELECT (SUM(?number) AS ?allnumber) ?muni_stat
                     WHERE {
                          ?s <http://data.admin.ch/bfs/property/NB_PERSON>                      ?number;
                               <http://data.admin.ch/bfs/property/REPORTINGMUNICIPALITYID>      ?muni_stat; 
                               <http://data.admin.ch/bfs/property/POPULATIONTYPE>               ?ptype;
                               <http://data.admin.ch/bfs/property/HISTREPORTINGMUNICIPALITYID> 	?muniuri.
                          ?ptype <http://www.w3.org/2004/02/skos/core#notation>                 ?pnumber.
                          FILTER ((xsd:int(?pnumber)) <= 2)
                     }
                     GROUP BY ?muni_stat
                }
                Filter (xsd:integer(?munistat_id) = ?munidistrict_id)
          }
          ORDER BY ?district_shortName
     } 
}
GROUP BY ?district_shortName

Und das Ergebnis stellt sich dann so dar (Auszug):

Aarau 75'138
Affoltern 51'541
Aigle 43'674
Albula 8'177
Andelfingen 30'837
Arbon 54'281
Arlesheim 152'563
Baden 140'061
Bellinzona 50'803
Bernina 4'652
Bern-Mittelland 402'520
Biel/Bienne 97'928
Bremgarten 74'567
Brig 26'303

Nun können wir die beiden Ergebnisse miteinander verrechnen.

 

SPARQL-Endpoint für Abfragen (Demo)

Im SPARQL-Endpoint können Sie die vorbereitete SPARQL-Abfrage (federated query) zur Ermittlung der Anzahl Bibliotheken pro Bezirk ausführen. Den Bibliotheksdaten aus OpenStreetMap wird die jeweilige Postleitzahl entnommen und in den Linked Data Sets des Bundesamtes für Statistik wird über die Postleitzahl die Zugehörigkeit zum Bezirk ermittelt; schliesslich werden die zum gleichen Bezirk gehörenden Bibliotheken aufsummiert.

SPARQL-Endpoint mit vorbereiteter Abfrage aufrufen