W3 Consorcium v roce 2001 zakládá skupinu Semantic Web Activity, která mimo jiné definuje sémantický web (semantic web) tak, že:
K tomuto účelu bylo nezbytné popisovat zdroje na webu, např.:
Tyto uvedené představy jso už dnes historie, přesto si tato myšlenka nalezla své místo v jiných oblastech:
Zkratka RDF odkazuje na Resource Description Framework, což můžeme přeložit jako rámec popisu zdrojů. Jeho autorem je W3 Consorcium a byl převážně navržen pro popis zdrojů na webu. RDF není jen rámec, ale také datový model či slovník. RDF je navržen maximálně obecně, aby mohl být čten a pochopen strojem (počítačem, ale zobrazení informace témto jazykem nebylo pochopitelné pro lidi.
Zápis RDF využívá pro zápis:
RDF má tyto silné stránky:
RDF je založeno na atomickém prvku označovaném trojice (triple). Trojice popisuje vlastnost zdroje. Trojice se skládá ze tří částí:
Trojice tvoří vždy jedno platné tvrzení. Máme-li více tvrzení zapisujeme je jako odpovídající počet trojic. Spojením trojic nám vzniká popis reálného světa v podobě orientovaného grafu:
Vše jsou trojice (triples) resp. čtveřice (quads). Datové úložiště označujeme jako:
Příklad tvrzení: Rozvrhovou akci v pondělí 9.20 vede Petr. lze zapsat do trojice:
Vidíme, že tam jsou i další tvrzení, které se vztahují k rozvrhové akci a blíže ji specifikují. Jak budou vypadat další tvrzení?
Další tvrzení přepsaná RDF trojicemi:
<zdroj> <vlastnost> <objekt> . ======================================================= <Rozvrhová akce v pondělí 9.20> <vede> <Petr> . <Rozvrhová akce v pondělí 9.20> <den> <pondělí> . <Rozvrhová akce v pondělí 9.20> <zacina> <9.20> . <9.20> <hodin> <9> . <9.20> <minut> <20> . ...
Výše uvedený zápis (bez prvních dvou řádek) se označuje N-TRIPLE notace a každá trojice je vždy na samostatném řádku ukončená znakem tečky. Zdroje jsou uzavřeny ve špičatých závorkách a text je v uvozovkách.
Výše uvedený zápis tvrzení byl ilustrační a v této podobě by neměl odpovídající přínos/význam. Zdroje musí být jednoznačně identifikovatelné buď jako URI (Uniform Resource Identifier) nebo jako IRI (Internationalized Resource Identifier). URI nebo IRI má tento obecný tvar:
scheme:[//[user:password@]host[:port]][/]path[?query][#fragment]
IRI umožňuje oproti URI v řetězci použít Unicode znaky splňující pravidla dle RFC 3987. URI nebo IRI nabízí známé specializace:
scheme:path
Z důvodu možnosti propojení dat (v budoucnosti) na webu je doporučeno použití schéma/protokolu HTTP(S).
V našem příkladě použijeme např. jmenný prostor http://zcu.cz/rdf/ a zdroje i vlastnosti jím identifikujeme:
<zdroj> <vlastnost> <objekt> . ========================================================================= <http://zcu.cz/rdf/1> <http://zcu.cz/rdf/vede> "Petr" . <http://zcu.cz/rdf/1> <http://zcu.cz/rdf/den> "pondělí" . <http://zcu.cz/rdf/1> <http://zcu.cz/rdf/zacina> <http://zcu.cz/rdf/2> . <http://zcu.cz/rdf/2> <http://zcu.cz/rdf/hodin> "9" . <http://zcu.cz/rdf/2> <http://zcu.cz/rdf/minut> "20" . ...
Pro hodnoty lze definovat datový typ nebo jazyk. V notacích N-TRIPLE a Turtle:
"2"^^<http://www.w3.org/2001/XMLSchema#integer> "2012-04-18"^^<http://www.w3.org/2001/XMLSchema#date> "2012-09-19T23:20:00+0200"^^<http://www.w3.org/2001/XMLSchema#dateTime>
"address"@en "adresa"@cs
Ilustrační příklad ukazuje popis několika tvrzení, ale rozumíme mu (pravděpodobně) pouze my. Pro zajištění shody a pochopení ostatními lidmi i stroji je popis (zatím) nevhodný, resp. nedostatečný. Vlastnosti, které jsme si zavedli jsou naše a lokální. Kdokoliv jiný se na ně podívá, nemusí pochopit jejich správný význam nebo je správně interpretovat:
zcu:den – den v měsíci? den v roce?zcu:hodin – aktuální čas? čas události? 12/24 hod. – dopoledne nebo odpoledne?zcu:minut – počet minut od/k čeho/čemu?zcu:vede – vede projekt?zcu:zacina – co/kde/proč začíná?Takto uvedené vlastnosti jsou „vytrženy z kontextu“, chybí nám kontext nebo spíše význam – sémantika vlastností. Zjednodušeně řečeno, proto vznikají slovníky nebo ontologie, které definují a současně dokumentují potřebný kontext, vztahy, doménu, obor hodnot, ...
Vytvoříme-li si odpovídající slovník (resp. ontologii) a zveřejníme jej – data a informace může využít už i někdo další a mohou se sdílet na webu. Problém?! Když si úplně každý vytvoří svůj vlastní slovník, pak bude výsledek nepoužitelný, protože data budou sdílená, ale možnosti využití a interpretace ostatními budou minimální.
Řešením tohoto problému jsou existující základní RDF slovníky a ontologie, které přináší rámec jak nad RDF popisovat zdroje jednotným způsobem:
rdf,
rdf:type – určení, že je popisovaný zdroj nějakého typu/třídy.rdfs,
owl:sameAs – pro popis, že nějaké dvě „věci“ jsou totéž
Závěr: RDF definuje jak psát popis a OWL definuje co psát.
Pro účely přehlednějšího a stručnějšího (úspornějšího) zápisu se používají prefixy i další notace. Pro stejný příklad popisu pondělní rozvrhové akce použijeme další zápisy.
Přímo výše uvedený příklad zapíšeme v notaci Turtle:
<http://zcu.cz/rdf/1> http://zcu.cz/rdf/:vede "Petr" ; http://zcu.cz/rdf/:den "pondělí" ; http://zcu.cz/rdf/:zacina http://zcu.cz/rdf/2 . <http://zcu.cz/rdf/2> http://zcu.cz/rdf/:hodin "9" ; http://zcu.cz/rdf/:minut "20" .
Tvrzení patřící stejnému subjektu oddělujeme středníkem, více hodnot u stejné vlastnosti oddělujeme čárkou a za posledním tvrzením je tečka.
Ke zvolenému jmennému prostoru nadefinujeme prefix zcu a data ve výše uvedené notaci Turtle budou:
@prefix zcu: <http://zcu.cz/rdf/> . <http://zcu.cz/rdf/1> zcu:vede "Petr" ; zcu:den "pondělí" ; zcu:zacina zcu:2 . <http://zcu.cz/rdf/2> zcu:hodin "9" ; zcu:minut "20" .
Ve formátu Turtle může být na začátku deklarace prefixů začínající znakem zavináče a slovem prefix, za nímž následuje vlastní prefix a za dvojtečkou úplné URI/IRI jmenného prostoru za kterým je tečka.
Stejný zápis v provedení notace RDF/XML:
<rdf:RDF xmlns:zcu="http://zcu.cz/rdf/">
<rdf:Description rdf:about="http://zcu.cz/rdf/1">
<zcu:vede>Petr</zcu:vede>
<zcu:den>pondělí</zcu:den>
<zcu:zacina>
<rdf:Description rdf:about="http://zcu.cz/rdf/2">
<zcu:hodin>9</zcu:hodin>
<zcu:minut>20</zcu:minut>
</rdf:Description>
</zcu:zacina>
</rdf:Description>
</rdf:RDF>
Používá se jmenný prostor rdf, který je rezervovaný. Prefixy jsou definovány v kořenovém elementu rdf:RDF.
Příklad v základní RDF/XML notaci včetně použitého RDF jmenného prostoru:
<?xml version="1.0"?>
<rdf:RDF
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:zcu="http://zcu.cz/rdf/">
<rdf:Description rdf:about="http://zcu.cz/rdf/1">
<zcu:vede>Petr</zcu:vede>
<zcu:den>pondělí</zcu:den>
<zcu:zacina rdf:resource="http://zcu.cz/rdf/2" />
</rdf:Description>
<rdf:Description rdf:about="http://zcu.cz/rdf/2">
<zcu:hodin>9</zcu:hodin>
<zcu:minut>20</zcu:minut>
</rdf:Description>
</rdf:RDF>
Uvedené serializace RDF dat jsou vzájemně převoditelné – bezztrátově.
Nová tvrzení/trojice stačí k původním jen přidat.
<zdroj> <vlastnost> <objekt> . =========================================================== <Petr> <je> <Jméno> . <Petr> <je> <Osoba> . <Petr> <je> <Muž> . <pondělí> <je> <Den v týdnu> . ...
Tyto trojice mohou pocházet např. z jiného zdroje, slovníku nebo ontologie.
Lze se dotazovat u více zdrojů současně, a to prostřednictvím distribuované prostředí webu – SPARQL Endpoint.
Pokud zdroj používá jiné identifikátory, slovníky nebo ontologie, lze je propojit (merge) přidáním tvrzení s vlastnostmi
owl:sameAs, owl:differentFrom a owl:AllDifferent.
Kocept RDF a OWL je velmi obecný, avšak lze najít podobnost s OOP přístupem.
rdf:type) nějaké třídy nebo tříd.
Zásadní rozdíl je ve způsobu přístupu k datům.
Schéma databáze = datový model je pevně definován
Tabulka = entita
Sloupec tabulky = atribut entity
rdfs:domain u RDF vlastnosti navíc znamená:
rdfs:domain specifikuje.Datový typ atributu = povolený datový typ hodnot
rdfs:range).
rdfs:range u RDF vlastnosti navíc znamená, že všechny hodnoty, kde byl použit predikát s rdfs:range,
bude datového typu, který definoval.
Záznam = řádek tabulky
RDF úložiště
Programovací jazyky
Seznam dalších nástrojů souvisejících s RDF: https://www.w3.org/RDF/
SPARQL 1.0 (2008)
SPARQL 1.1 (W3C Recommendation 21 March 2013)
Jazyk umožňuje:
SELECT, DESCRIBE, CONSTRUCT)ASK, DESCRIBE),CONSTRUCT).Výběrové typy dotazu:
ASKCONSTRUCTDESCRIBESELECTFormát výsledku výběrových dotazů – závisí na typu dotazu:
ASK – vrací boolean (hodnotu true/false),CONSTRUCT a DESCRIBE – vrací RDF graf,SELECT – vrací tabulku – CSV/TSV, HTML, TXT, JSON, XML, … záleží na RDF úložišti/databázi, může poskytovat i např. XLS soubory.Aktualizace grafu:
INSERT DATA – vložení trojic (triple)DELETE DATA – odstranění trojic z grafu (quad)LOAD – čte data ze zadaného IRI
LOAD [SILENT] <iri_ref> INTO GRAPH <graph_name>
CLEAR – odstraní všechny trojice z daného/daných grafů (IRI, DEFAULT, NAMED, ALL).
Správa grafu:
CREATE – vytvoření nového grafu (může-li existovat prázdný graf),DROP – odstranění grafu i jeho obsahu,COPY – kopíruje obsah grafu do jiného,MOVE – přesune obsah grafu do jiného,ADD – duplikuje obsah jednoho grafu do jiného.
# deklarace prefixů
PREFIX foo: <http://example.com/resources/>
...
# definice datasetu/grafu/zdroje
FROM ...
# výsledek dotazu
SELECT ...
# podmínka — vzor dotazu (query pattern)
WHERE {
...
}
# modifikátory dotazu
ORDER BY ...
V části SELECT:
WHERE.
Proměnná začíná prefixem otazníku (dolaru): ?s ?p ?o.
V části WHERE:
AND mezi trojicemi (platí všechny současně),OPTIONAL,WHERE jsou spojeny s hodnotou odpovídající dané části trojice (zdroj, literál) a mohou být použity v části
SELECT (CONSTRUCT, DESCRIBE)BIND, které provede přiřazení hodnoty proměnné (explicitně zadané v dotazu např. v SELECT):
BIND (?hodnota * (1 - ?sleva) AS ?cena)
Filtrování hodnot:
FILTER pracuje s podmínkami typu boolean.!, &&, ||+, -, *, /=, !=, <, >, IN, NOT IN, ...isURI, isBlank, isLiteral, isNumeric, bound, !boundstr, lang, datatypesameTerm, langMatches, regex, REPLACE, ...IF, COALESCE, EXISTS, NOT EXISTSURI, BNODE, STRDT, STRLANG, UUID, STRUUIDSTRLEN, SUBSTR, UCASE, LCASE, STRSTARTS, STRENDS, CONTAINS, STRBEFORE, STRAFTER, CONCAT, ENCODE_FOR_URIabs, round, ceil, floor, randnow, year, month, day, hours, minutes, seconds, timezone, tzMD5, SHA1, SHA256, SHA384, SHA512FILTER (?hodnota > 1000 && langMatches(lang(?nazev), "EN")) .
lang získá jazyk specifikovaný u textového řetězcelangMatches porovná s uvedeným jazykem nebo jejich výčtem# https://www.w3.org/TR/2013/REC-sparql11-query-20130321/#construct @prefix foaf: <http://xmlns.com/foaf/0.1/> . # Friend-of-a-Friend <abc> foaf:name "Alice" . <abc> foaf:mbox <mailto:alice@example.org> .
Typy dotazů jsou:
SELECT všechny nebo podmnožinu proměnných z podmínkové části
# příklad 1 - všechny trojice
SELECT ?s ?p ?o
WHERE {
?s ?p ?o .
}
# příklad 1 - všechny vlastnosti a jejich hodnoty
SELECT ?p ?o
WHERE {
<abc> ?p ?o .
}
CONSTRUCT vrací RDF graf sestavený dle šablony trojic
# https://www.w3.org/TR/2013/REC-sparql11-query-20130321/#construct
PREFIX foaf: <http://xmlns.com/foaf/0.1/>
PREFIX vcard: <http://www.w3.org/2001/vcard-rdf/3.0#>
# příklad 1 - jen filtrování
CONSTRUCT WHERE { ?x foaf:name ?name . }
# příklad 2 - transformace schéma
CONSTRUCT {
?x vcard:FN ?name
}
WHERE {
?x foaf:name ?name .
}
ASK odpověď je datového typu boolean;
# https://www.w3.org/TR/2013/REC-sparql11-query-20130321/#ask
# příklad 1 - odpověď bude ’true’ nebo ’false’?
PREFIX foaf: <http://xmlns.com/foaf/0.1/>
ASK { ?x foaf:name "Alice" }
# příklad 2 - odpověď bude ’true’ nebo ’false’?
PREFIX vcard: <http://www.w3.org/2001/vcard-rdf/3.0#>
ASK { ?x vcard:FN "Alice" }
true, pokud vzor dotazu vyhovuje nějaké odpovědi,false.
DESCRIBE vrací RDF graf popisující zdroj
# https://www.w3.org/TR/2013/REC-sparql11-query-20130321/#describe
# příklad 1 - známe URI
DESCRIBE <abc>
# příklad 2 - neznáme konkrétní URI
PREFIX foaf: <http://xmlns.com/foaf/0.1/>
DESCRIBE ?x
WHERE {
?x foaf:name "Alice" .
}
Data vychází z datového formátu DASTA (DAtový STAndard) spravovaný Ministerstvem zdravotnictví ČR
V systému MRE používané ontologie jsou dokumentovány: https://mre.zcu.cz/ontology/ontologies.html V našem případě jsou:
dasta.owl – http://mre.zcu.cz/ontology/dasta.owldscl.owl http://mre.kiv.zcu.cz/ontology/dscl.owldicom.owl http://mre.kiv.zcu.cz/ontology/dicom.owldcm:Patient), k němuž se může vztahovat několik DICOM studií (dcm:Study).dcm:Series).dcm:CT_Image) obrazového vyšetření,
např. počítačová tomografie (CT), magnetická rezonance (MR).
sits.owl http://mre.kiv.zcu.cz/ontology/sits.owlnihss.owl http://mre.kiv.zcu.cz/ontology/nihss.owlSchéma vybraných tříd a vlastností v systému MRE
patient1-medical
Anon_666,
patient6-medical
patient1-medical,
patient7-medical
patient6-medical,
patient7-imaging
patient7-medical,Použité datové formáty (koncovka):
nt N-TRIPLE
ttl TURTLE
xml RDF/XML
PREFIX id: <http://mre.zcu.cz/id/> PREFIX ds: <http://mre.zcu.cz/ontology/dasta.owl#> PREFIX dscl: <http://mre.zcu.cz/ontology/dscl.owl#> PREFIX dcm: <http://mre.zcu.cz/ontology/dcm.owl#> PREFIX sits: <http://mre.zcu.cz/ontology/sits.owl#> PREFIX nihss: <http://mre.zcu.cz/ontology/nihss.owl#> PREFIX mre: <http://mre.zcu.cz/ontology/mre.owl#> PREFIX acl: <http://www.w3.org/ns/auth/acl#> PREFIX dc: <http://purl.org/dc/elements/1.1/> PREFIX nfo: <http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#> PREFIX owl: <http://www.w3.org/2002/07/owl#> PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#> PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
Kompletní výčet a detaily viz https://mre.zcu.cz/ontology/ontologies.html.
Stáhněte si program z adresy https://jena.apache.org/download/, rozbalte jej a z rozbaleného adresáře spusťte
./fuseki-server
fuseki-server.bat
a následně v konzoli uvidíme start serveru. Server běží ve výchozím nastavení na portu 3030. Ve webovém prohlížeči zadáme adresu: http://localhost:3030 a vše je připraveno k nahrání dat a zkoušení příkladů.
id:cd3f0c85b158c08a2b113464991810cf2cdfc387
a ds:Patient , ds:Male ;
ds:address id:3840aecb9edac9f7d7c9172f2f4be82b08ab3ddf ;
ds:clinicalEvent id:be8d011f882326495f8d06c58f22db51d95cc7bf ;
ds:datetimeBirth "1938-08-13"^^xsd:date ;
ds:lastName "Anon_666" ;
ds:patientID "666" ;
ds:sex "M" ;
ds:sexNCLPTPS dscl:NCLPTPS_M ;
ds:sexPOHLAV dscl:POHLAV_1 ;
dc:title "Anon_666 (M) * 1938-08-13" .
id:3840aecb9edac9f7d7c9172f2f4be82b08ab3ddf
a ds:PermanentAddress ;
ds:addressCity "Město 1" ;
ds:addressZIP "00001" ;
dc:title "Město 1 (00001)" .
id:be8d011f882326495f8d06c58f22db51d95cc7bf
a ds:MedicalExamination ;
ds:datetimeEvent "2012-09-19T23:20:00+0200"^^xsd:dateTime ;
ds:diagnosis id:2c545600eb7a2722809d64c2753de714a5154b6b ,
id:2285692c932c88f8673a162ef7b5c997993da41c ;
ds:dsclExaminationContent dscl:LZSOZ_NL ;
ds:dsclExaminationOrigin dscl:LZTOZV_J ;
ds:dsclExaminationRequest dscl:LZTZOV_D ;
ds:dsclExaminationState dscl:LZSZZ_K ;
ds:imagingStudyNumber "00000078" ;
ds:originator id:44040e7024d5a4cc177bf0ed29683c2185dbd05b ;
ds:reportText "CT mozku:..." ;
ds:reportTitle "032/002 - CT mozku: s k.l. iv." ;
dc:title "2012-09-19 23:20: 032/002 - CT mozku: s k.l. iv." .
id:2c545600eb7a2722809d64c2753de714a5154b6b
a ds:ActualDiagnosis ;
ds:datetimeEvent "2012-04-18"^^xsd:date ;
ds:diagCode dscl:MKN10_5_J180 ;
ds:diagDetail "Bronchopneumonie NS" ;
ds:diagOrder 1 ;
ds:patient id:cd3f0c85b158c08a2b113464991810cf2cdfc387 ;
dc:title "2012-04-18 (1) J18.0 - Bronchopneumonie NS" .
id:2285692c932c88f8673a162ef7b5c997993da41c
a ds:ActualDiagnosis ;
ds:datetimeEvent "2012-04-18"^^xsd:date ;
ds:diagCode dscl:MKN10_5_I639 ;
ds:diagDetail "Mozkový infarkt" ;
ds:diagOrder 2 ;
ds:patient id:cd3f0c85b158c08a2b113464991810cf2cdfc387 ;
dc:title "2012-04-18 (2) I63.9 - Mozkový infarkt" .
id:44040e7024d5a4cc177bf0ed29683c2185dbd05b
a ds:OriginatorDepartment ;
ds:departmentName "Nemocnice na ..." ;
dc:title "Nemocnice na ..." .
Níže uvedené dotazy budou aplikovány nad datovou sadou patient1-medical.
Chceme-li získat všechny trojice, musíme v části WHERE mít vzor trojice se třemi proměnnými.
Ty vyhovují všem trojicím. V části SELECT je uvedeme.
# základní forma dotazu SELECT
# 01a
SELECT ?subject ?predicate ?object
WHERE {
?subject ?predicate ?object .
}
# na názvech proměnných nezáleží, jsou oddělovány jen mezerou
# 01b
SELECT ?s ?p ?o
WHERE {
?s ?p ?o .
}
Dotaz vrátí všechny trojice, tj. tři sloupce s hodnotami dle celkového počtu trojic v datasetu.
Omezení počtu lze provést použitím klíčového slova LIMIT.
# 02
SELECT ?s ?p ?o
WHERE {
?s ?p ?o
}
LIMIT 10
Dotaz vrátí 10 trojic ve třech sloupcích odpovídajících trojicím. Vedle LIMIT, lze použít také OFFSET.
# získání všech vlastností a jejich hodnot SELECTem
# 03a
SELECT ?p ?o
WHERE {
<http://mre.zcu.cz/id/cd3f0c85b158c08a2b113464991810cf2cdfc387> ?p ?o .
}
# s využitím prefixu v zápisu
# 03b
PREFIX id: <http://mre.zcu.cz/id/>
SELECT ?p ?o
WHERE {
id:cd3f0c85b158c08a2b113464991810cf2cdfc387 ?p ?o .
}
Výsledkem dotazu je 11 dvojic (pár vlastnost a její hodnota) pro výše uvedené URI.
V podobě grafu lze prakticky totéž získat prostřednictvím DESCRIBE:
# získání všech vlastností a jejich hodnot příkazem DESCRIBE # 03c PREFIX id: <http://mre.zcu.cz/id/> DESCRIBE id:cd3f0c85b158c08a2b113464991810cf2cdfc387
Vrátí RDF graf s 11 trojicemi, které mají jako subjekt uvedené URI.
ds:patientID)
# získání hodnot(y) vybrané vlastnosti
# 04a
PREFIX id: <http://mre.zcu.cz/id/>
PREFIX ds: <http://mre.zcu.cz/ontology/dasta.owl#>
SELECT ?o
WHERE {
id:cd3f0c85b158c08a2b113464991810cf2cdfc387 ds:patientID ?o .
}
# přejmenování proměnné na ?rc
# 04b
PREFIX id: <http://mre.zcu.cz/id/>
PREFIX ds: <http://mre.zcu.cz/ontology/dasta.owl#>
SELECT ?rc
WHERE {
id:cd3f0c85b158c08a2b113464991810cf2cdfc387 ds:patientID ?rc .
}
Dotaz vrátí jeden řádek s jednou hodnotou (jeden sloupec). V části WHERE může být i celé URI bez zkrácení prefixem.
Hodnota vlastnosti ds:patientID bude svázána (bind) s proměnnou ?rc a dostupná jako výsledek v části SELECT.
ds:patientID).
# získání subjectu (pacienta) na základě znalosti jeho rč
# 05
PREFIX ds: <http://mre.zcu.cz/ontology/dasta.owl#>
SELECT ?patient
WHERE {
?patient ds:patientID "666" .
}
Výsledkem dotazu je jedna hodnota URI.
Níže uvedené dotazy budou aplikovány nad datovou sadou patient6-medical.
ds:patientID.
# získání identifikace pacienta a jeho rč
# 06
PREFIX ds: <http://mre.zcu.cz/ontology/dasta.owl#>
SELECT ?pacient ?rc
WHERE {
?pacient ds:patientID ?rc .
}
Výsledkem dotazu je šest hodnot URI a rodných čísel.
WHERE.SELECT.
# každá vlastnost tvoří jednu RDF trojici ve WHERE
# 07a
PREFIX id: <http://mre.zcu.cz/id/>
PREFIX ds: <http://mre.zcu.cz/ontology/dasta.owl#>
SELECT ?rc ?jmeno
WHERE {
id:cd3f0c85b158c08a2b113464991810cf2cdfc387 ds:patientID ?rc .
id:cd3f0c85b158c08a2b113464991810cf2cdfc387 ds:lastName ?jmeno .
}
# vlastnosti ke stejnému zdroji jsou oddělené středníkem
# 07b
PREFIX id: <http://mre.zcu.cz/id/>
PREFIX ds: <http://mre.zcu.cz/ontology/dasta.owl#>
SELECT ?rc ?jmeno
WHERE {
id:cd3f0c85b158c08a2b113464991810cf2cdfc387
ds:patientID ?rc ;
ds:lastName ?jmeno .
}
V obou příkladech dostaneme stejný výsledek.
# v dalších dotazech budeme využívat zkráceného zápisu
# 08a
PREFIX ds: <http://mre.zcu.cz/ontology/dasta.owl#>
SELECT ?pacient ?rc ?jmeno
WHERE {
?pacient ds:patientID ?rc ;
ds:lastName ?jmeno .
}
a použijeme hvězdičku (*), chceme-li zobrazit všechny proměnné:
# použitá * pro vypsání všech proměnných
# 08b
PREFIX ds: <http://mre.zcu.cz/ontology/dasta.owl#>
SELECT *
WHERE {
?pacient ds:patientID ?rc ;
ds:lastName ?jmeno .
}
ds:datetimeBirthds:datetimeDeath
# nejdříve dohledáme datum narození
# 09a
PREFIX ds: <http://mre.zcu.cz/ontology/dasta.owl#>
SELECT *
WHERE {
?pacient ds:patientID ?rc ;
ds:lastName ?jmeno ;
ds:datetimeBirth ?narozeni .
}
# a potom datum úmrtí
# 09b
PREFIX ds: <http://mre.zcu.cz/ontology/dasta.owl#>
SELECT *
WHERE {
?pacient ds:patientID ?rc ;
ds:lastName ?jmeno ;
ds:datetimeBirth ?narozeni ;
ds:datetimeDeath ?umrti .
}
Jaký je výsledek dotazu? Proč? Správné řešení:
# datum úmrtí je nepovinný - přidáme konstrukci OPTIONAL
# 09c
PREFIX ds: <http://mre.zcu.cz/ontology/dasta.owl#>
SELECT *
WHERE {
?pacient ds:patientID ?rc ;
ds:lastName ?jmeno ;
ds:datetimeBirth ?narozeni .
OPTIONAL {
?pacient ds:datetimeDeath ?umrti .
}
}
Výsledek (počet řádek) dle zvoleného příkladu:
patient1-medical,patient6-medical,patient7-medical.
# pozor - pokud je proměnná prvně zavedena v konstrukci OPTIONAL
# a až následně použita ve zbytku vzoru, výsledek se změní.
# 09d
PREFIX ds: <http://mre.zcu.cz/ontology/dasta.owl#>
SELECT *
WHERE {
OPTIONAL {
?pacient ds:datetimeDeath ?umrti .
}
?pacient ds:patientID ?rc ;
ds:lastName ?jmeno ;
ds:datetimeBirth ?narozeni .
}
Význam klíčového slova OPTIONAL – ovlivní počet výsledků.
Filtrování hodnoty rodného čísla:
# filtrační podmínka - volání funkce FILTER
# 10a
PREFIX ds: <http://mre.zcu.cz/ontology/dasta.owl#>
SELECT *
WHERE {
?pacient ds:patientID ?rc ;
ds:lastName ?jmeno ;
ds:datetimeBirth ?narozeni .
OPTIONAL {
?pacient ds:datetimeDeath ?umrti .
}
FILTER ( ?rc > 200 )
}
Všechny hodnoty rodného čísla porovná s filtrem. Funguje?
Hodnota ds:patientID je string, nikoliv číslo, porovnáváme text s číslem.
# převedem hodnotu rc na číslo
# 10b
PREFIX ds: <http://mre.zcu.cz/ontology/dasta.owl#>
SELECT *
WHERE {
?pacient ds:patientID ?rc ;
ds:lastName ?jmeno ;
ds:datetimeBirth ?narozeni .
OPTIONAL {
?pacient ds:datetimeDeath ?umrti .
}
FILTER ( ?rc > "200" )
}
Asi to funguje. Opravdu?
# změního hodnotu rc na 20
# 10c
PREFIX ds: <http://mre.zcu.cz/ontology/dasta.owl#>
SELECT *
WHERE {
?pacient ds:patientID ?rc ;
ds:lastName ?jmeno ;
ds:datetimeBirth ?narozeni .
OPTIONAL {
?pacient ds:datetimeDeath ?umrti .
}
FILTER ( ?rc > "20" )
}
Proč to přestalo fungovat? Odpovědí je lexikografické řazení. Řešení: musíme na číslo přetypovat hodnotu rc.
# nutné přetypování rc na číslo
# 10d
PREFIX ds: <http://mre.zcu.cz/ontology/dasta.owl#>
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
SELECT *
WHERE {
?pacient ds:patientID ?rc ;
ds:lastName ?jmeno ;
ds:datetimeBirth ?narozeni .
OPTIONAL {
?pacient ds:datetimeDeath ?umrti .
}
FILTER ( xsd:int(?rc) > 200 )
}
Obdoba předchozích příkladů, jen je navíc požadováno řazení:
# řazení pacientů podle jejich rc - ORDER BY
# 11a
PREFIX ds: <http://mre.zcu.cz/ontology/dasta.owl#>
SELECT *
WHERE {
?pacient ds:patientID ?rc ;
ds:lastName ?jmeno ;
ds:datetimeBirth ?narozeni .
OPTIONAL {
?pacient ds:datetimeDeath ?umrti .
}
}
ORDER BY DESC (?rc)
# stejný problém jako u funkce FILTER
# 11b
PREFIX ds: <http://mre.zcu.cz/ontology/dasta.owl#>
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
SELECT *
WHERE {
?pacient ds:patientID ?rc ;
ds:lastName ?jmeno ;
ds:datetimeBirth ?narozeni .
OPTIONAL {
?pacient ds:datetimeDeath ?umrti .
}
}
ORDER BY DESC ( xsd:int(?rc) )
Vhodné použít data patient6-medical nebo patient7-medical. Výsledek dotazu (počet řádek) dle zvoleného příkladu:
patient1-medical,patient6-medical,patient7-medical
Porovnejte různé varianty části ORDER BY – rozdíl mezi znakovým a číselným řazením:
ORDER BY ?rc ORDER BY ASC ( ?rc ) ORDER BY DESC ( ?rc ) ORDER BY DESC ( xsd:int(?rc) ) ORDER BY ?jmeno DESC ( ?rc )
Výsledek dotazu lze ovlivnit modifikátorem ORDER BY pro řazení, ASC, DESC určují směr řazení.
V případě chybějícího datového typu se jedná obecně o string:
xsd:int(?rc).Pacienti narozeni od 1. ledna 1930 do současnosti.
# pacienti narozeni 1.1.1930 a dříve
# 12a
PREFIX ds: <http://mre.zcu.cz/ontology/dasta.owl#>
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
SELECT *
WHERE {
?pacient ds:patientID ?rc ;
ds:lastName ?jmeno ;
ds:datetimeBirth ?narozeni .
OPTIONAL {
?pacient ds:datetimeDeath ?umrti .
}
FILTER ( ?narozeni >= "1930-01-01" )
}
Ve filtru uvedeme i datový typ hodnoty:
# funguje s uvedením datového typu hodnoty
# 12b
PREFIX ds: <http://mre.zcu.cz/ontology/dasta.owl#>
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
SELECT *
WHERE {
?pacient ds:patientID ?rc ;
ds:lastName ?jmeno ;
ds:datetimeBirth ?narozeni .
OPTIONAL {
?pacient ds:datetimeDeath ?umrti .
}
FILTER ( ?narozeni >= "1930-01-01"^^xsd:date )
}
Pacienti narozeni ve 30. letech 20. století.
# více FILTERů je spojeno implicitně AND
# 13a
PREFIX ds: <http://mre.zcu.cz/ontology/dasta.owl#>
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
SELECT *
WHERE {
?pacient ds:patientID ?rc ;
ds:lastName ?jmeno ;
ds:datetimeBirth ?narozeni .
OPTIONAL {
?pacient ds:datetimeDeath ?umrti .
}
FILTER ( ?narozeni >= "1930-01-01"^^xsd:date )
FILTER ( ?narozeni <= "1939-12-31"^^xsd:date )
}
# složitější logická podmínka ve FILETRu
# 13b
PREFIX ds: <http://mre.zcu.cz/ontology/dasta.owl#>
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
SELECT *
WHERE {
?pacient ds:patientID ?rc ;
ds:lastName ?jmeno ;
ds:datetimeBirth ?narozeni .
OPTIONAL {
?pacient ds:datetimeDeath ?umrti .
}
FILTER ( ?narozeni >= "1930-01-01"^^xsd:date &&
?narozeni <= "1939-12-31"^^xsd:date )
}
Konstrukcí EXISTS můžeme testovat, zda daný grafový vzor lze najít v datech. Chceme-li výsledek negovat, aplikujeme operátor NOT.
# pacienti, kteří nemají uveden datum úmrtí - NOT EXISTS
# 14a
PREFIX ds: <http://mre.zcu.cz/ontology/dasta.owl#>
SELECT *
WHERE {
?pacient ds:patientID ?rc ;
ds:lastName ?jmeno ;
ds:datetimeBirth ?narozeni .
FILTER ( NOT EXISTS {
?pacient ds:datetimeDeath ?umrti .
} )
}
Srovnatelného výsledku dosáhneme voláním logické funkce BOUND, která testuje, zda proměnná má hodnotu nebo ne.
Negaci funkce dosáhneme operátorem !
# pacienti, kteří mají uveden datum úmrtí - BOUND
# 14b
PREFIX ds: <http://mre.zcu.cz/ontology/dasta.owl#>
SELECT *
WHERE {
?pacient ds:patientID ?rc ;
ds:lastName ?jmeno ;
ds:datetimeBirth ?narozeni .
OPTIONAL {
?pacient ds:datetimeDeath ?umrti .
}
FILTER (BOUND (?umrti))
}
# pacienti, kteří nemají uveden datum úmrtí - negace BOUND
# 14c
PREFIX ds: <http://mre.zcu.cz/ontology/dasta.owl#>
SELECT *
WHERE {
?pacient ds:patientID ?rc ;
ds:lastName ?jmeno ;
ds:datetimeBirth ?narozeni .
OPTIONAL {
?pacient ds:datetimeDeath ?umrti .
}
FILTER (! BOUND (?umrti))
}
Z předchozích dotazů víme, že 2 pacienti jsou již po smrti. Proto jim můžeme spočítat věk, kterého se dožili.
Každý výraz v klauzuli SELECT musí být doplněn o alias a celý zápis odvozeného sloupce musí být uzavřen v závorce.
# u pacienta chceme spočítat, jakého věku se dožil
# potřebujeme odvozený sloupec
# 15a
PREFIX ds: <http://mre.zcu.cz/ontology/dasta.owl#>
SELECT ?rc
?jmeno
?narozeni
?umrti
((?umrti - ?narozeni) AS ?vek)
WHERE {
?pacient ds:patientID ?rc ;
ds:lastName ?jmeno ;
ds:datetimeBirth ?narozeni ;
ds:datetimeDeath ?umrti .
}
Vypočtený výsledek je ve dnech, chceme spočítat léta.
# spočítáme dožitý věk v letech
# 15b
PREFIX ds: <http://mre.zcu.cz/ontology/dasta.owl#>
SELECT ?rc
?jmeno
?narozeni
?umrti
(FLOOR (DAY (?umrti - ?narozeni) / 365) AS ?vek)
WHERE {
?pacient ds:patientID ?rc ;
ds:lastName ?jmeno ;
ds:datetimeBirth ?narozeni ;
ds:datetimeDeath ?umrti .
}
Pokud bychom chtěli filtrovat jen ty pacienty, kteří se dožili 80 let a více, tak máme smůlu.
Potom je potřeba využít funkce BIND.
# u pacienta chceme spočítat, jakého věku se dožil
# potřebujeme odvozený sloupec - BIND
# 15c
PREFIX ds: <http://mre.zcu.cz/ontology/dasta.owl#>
SELECT *
WHERE {
?pacient ds:patientID ?rc ;
ds:lastName ?jmeno ;
ds:datetimeBirth ?narozeni ;
ds:datetimeDeath ?umrti .
BIND (FLOOR (DAY (?umrti - ?narozeni) / 365) AS ?vek)
}
Filtrace 80 letých pacientů je snadná funkcí FILTER.
# nad takto odvozenou hodotou můžeme stanovit filtrační podmínku
# 15d
PREFIX ds: <http://mre.zcu.cz/ontology/dasta.owl#>
SELECT *
WHERE {
?pacient ds:patientID ?rc ;
ds:lastName ?jmeno ;
ds:datetimeBirth ?narozeni ;
ds:datetimeDeath ?umrti .
BIND (FLOOR (DAY (?umrti - ?narozeni) / 365) AS ?vek)
FILTER ( ?vek > 80 )
}
ds:Patient.rdf:type.
# získání URI patřící jen pacientům
# 16a
PREFIX ds: <http://mre.zcu.cz/ontology/dasta.owl#>
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
SELECT ?pacient
WHERE {
?pacient rdf:type ds:Patient .
}
Vlastnost rdf:type lze zkracovat na prosté písmeno „a“.
# existuje zkratka, nevyžadující prefix rdf - a
# 16 b
PREFIX ds: <http://mre.zcu.cz/ontology/dasta.owl#>
SELECT ?pacient
WHERE {
?pacient a ds:Patient .
}
ds:lastName.ds:Patient.
# ověřme, do jakých tříd (typů) nám spadají všechny uzly (URI), které mají jméno
# 17
PREFIX ds: <http://mre.zcu.cz/ontology/dasta.owl#>
SELECT ?uzel ?trida
WHERE {
?uzel a ?trida ;
ds:lastName ?jmeno .
}
Zjistili jsme, že každý pacient je navíc instancí třídy ds:Male nebo ds:Female, což odpovídá pohlaví pacienta.
Zjistěme, který pacient ma jaké pohlaví prostřednictvím dané třídy.
# získat identifikátory uzlů, které mají jméno a spadají do třídy ds:Male nebo ds:Female
# 18a
PREFIX ds: <http://mre.zcu.cz/ontology/dasta.owl#>
SELECT ?uzel ?jmeno ?trida
WHERE {
?uzel a ?trida ;
ds:lastName ?jmeno .
FILTER (?trida = ds:Female || ?trida = ds:Male)
}
Konstrukcí VALUES lze naplnit proměnnou nebo n-tici proměnných s přesně definovanou sadou hodnot.
Pokud je takto zavedená proměnná dále použita ve WHERE klauzuli, nemůže nabývat jiných hodnot.
# to samé pomocí VALUES
# 18b
PREFIX ds: <http://mre.zcu.cz/ontology/dasta.owl#>
SELECT ?uzel ?jmeno ?pohlavi
WHERE {
VALUES ?pohlavi { ds:Female ds:Male }
?uzel a ?pohlavi ;
ds:lastName ?jmeno .
}
# místo názvu třídy vypíšeme statický řetězec
# 18c
PREFIX ds: <http://mre.zcu.cz/ontology/dasta.owl#>
SELECT ?uzel ?jmeno ?pohlavi
WHERE {
VALUES (?typ ?pohlavi) { (ds:Female "Žena") (ds:Male "Muž") }
?uzel a ?typ ;
ds:lastName ?jmeno .
}
Na základě znalosti, jaké pohlaví má který pacient, můžeme s využitím agregačních funkcí spočítat, kolik pacientů je žen a kolik je mužů.
# následně si uděláme malou statistiku pacientů podle jejich pohlaví - GROUP BY
# 19
PREFIX ds: <http://mre.zcu.cz/ontology/dasta.owl#>
SELECT ?pohlavi
(COUNT (?uzel) AS ?pocet)
WHERE {
VALUES (?typ ?pohlavi) { (ds:Female "Žena") (ds:Male "Muž") }
?uzel a ?typ ;
ds:lastName ?jmeno .
}
GROUP BY ?pohlavi
Použijte zejména data patient7-medical a ověřte, že vaše dotazy vrací správné výsledky pro níže uvedené příklady.
dc:title a současně i rdfs:label,ds:Male (20-2b).
Doporučená data: patient7-medical.
# jméno a rč pacienta včetně data jeho lékařských vyšetření
# 21a
PREFIX ds: <http://mre.zcu.cz/ontology/dasta.owl#>
SELECT ?rc
?jmeno
?datum
WHERE {
?pacient a ds:Patient ;
ds:patientID ?rc ;
ds:lastName ?jmeno ;
ds:clinicalEvent ?vysetreni .
?vysetreni ds:datetimeEvent ?datum .
}
Dotaz vrací 1 013 výsledků. Je to správně? Není, opakují se nám stejné kombinace hodnot.
# ve výsledku se nám opakují hodnoty - použijeme DISTINCT
# 21a
PREFIX ds: <http://mre.zcu.cz/ontology/dasta.owl#>
SELECT DISTINCT ?rc
?jmeno
?datum
WHERE {
?pacient a ds:Patient ;
ds:patientID ?rc ;
ds:lastName ?jmeno ;
ds:clinicalEvent ?vysetreni .
?vysetreni ds:datetimeEvent ?datum .
}
Přidáním DISTINCT za SELECT omezíme počet duplicitních výsledků a dostaneme 47 výsledků. Je to už správně?
Nevíme, uděláme si kontrolu. Nejdříve zjistíme, kolik existuje instancí třídy ds:MedicalExamination:
# 22a
PREFIX ds: <http://mre.zcu.cz/ontology/dasta.owl#>
SELECT ?uri
WHERE {
?uri a ds:MedicalExamination .
}
nebo prostřednictvím agregační funkce COUNT():
# 22b
PREFIX ds: <http://mre.zcu.cz/ontology/dasta.owl#>
SELECT ( COUNT(?uri) as ?pocet )
WHERE {
?uri a ds:MedicalExamination .
}
V obou případech je celkový počet 10 lékařských zpráv. Ve skutečnosti může být v ds:clinicalEvent:
ds:MedicalExamination),ds:LaboratoryReport).Má-li být výstupem pouze datum a čas lékařských zpráv, musíme doplnit trojici, která specifikuje typ (třídu).
# problém je jinde, poskytuje to jak lékařská vyšetření, tak laboratorní vyšetření
# 21b
PREFIX ds: <http://mre.zcu.cz/ontology/dasta.owl#>
SELECT ?rc
?jmeno
?datum
WHERE {
?pacient a ds:Patient ;
ds:patientID ?rc ;
ds:lastName ?jmeno ;
ds:clinicalEvent ?vysetreni .
?vysetreni ds:datetimeEvent ?datum ;
a ds:MedicalExamination .
}
Nyní dotaz vrací správně 10 výsledků (stejně to bude v tomto případě s DISTINCT pro uvedená data).
Přidání trojice s určením typu třídy odstraní všechny ostatní instance ds:LaboratoryReport, které nás zrovna nezajímají.
Diagnóza je vždy uvedena u lékařské zprávy pacienta:
# zjistíme, jaké diagnózy byly určeny během lékařských vyšetření
# diagnózy jsou dostupné jen přes lékařská vyšetření
# 23a
PREFIX ds: <http://mre.zcu.cz/ontology/dasta.owl#>
SELECT ?rc
?jmeno
?popis
WHERE {
?pacient a ds:Patient ;
ds:patientID ?rc ;
ds:lastName ?jmeno ;
ds:clinicalEvent ?vysetreni .
?vysetreni ds:diagnosis ?diagnoza .
?diagnoza ds:diagDetail ?popis .
}
ORDER BY ?jmeno ?popis
Ve výsledku je patrné, že se nám některé diagnózy u stejného pacieta opakují. To je dáno skutečností, že stejná diagnóza byla u pacienta znova detekována na dalším vyšetření. Zjistíme, kolikrát byla každá diagnóza detekována u každého pacienta:
# kolikrát byla která diagnóza určena během lékařského vyšetření
# 23b
PREFIX ds: <http://mre.zcu.cz/ontology/dasta.owl#>
SELECT ?rc
?jmeno
(COUNT (?diagnoza) AS ?pocet)
?popis
WHERE {
?pacient a ds:Patient ;
ds:patientID ?rc ;
ds:lastName ?jmeno ;
ds:clinicalEvent ?vysetreni .
?vysetreni ds:diagnosis ?diagnoza .
?diagnoza ds:diagDetail ?popis .
}
GROUP BY ?rc ?jmeno ?popis
ORDER BY ?jmeno ?popis
Výsledek dotazu je 34 diagnóz pro data z patient7-medical. V dotazu musíme projít od pacienta ?patient
(získáme jeho rodné číslo a jméno) na klinickou událost (?vysetreni), z klinické události ?vysetreni projdeme
k diagnóze ?diagnoza a z diagnózy nás zajímá její popis.
Ke stejnému výsledku dojdeme také při využití Property Path ve SPARQL 1.1:
# získání popisu diagnózy pomocí Property Path
# 23c
PREFIX ds: <http://mre.zcu.cz/ontology/dasta.owl#>
SELECT ?rc
?jmeno
(COUNT (?diagnoza) AS ?pocet)
?popis
WHERE {
?pacient a ds:Patient ;
ds:patientID ?rc ;
ds:lastName ?jmeno ;
ds:clinicalEvent/ds:diagnosis ?diagnoza .
?diagnoza ds:diagDetail ?popis .
}
GROUP BY ?rc ?jmeno ?popis
ORDER BY ?jmeno ?popis
ds:MedicalExamination)?patient7-medical instancí ds:LaboratoryReport?ds:diagOrder).FILTER?
Dotazovací jazyk SPARQL nabízí kromě příkazu SELECT ještě další dva příkazy pro dotazování: DESCRIBE a ASK.
Příkazy SELECT a ASK vyžadují konstrukci FROM, u příkazu DESCRIBE je tato konstrukce nepovinná.
Příkaz DESCRIBE pro všechny proměnné mající tvar URI vrací všechny RDF troji těchto identifikátorů.
Chceme-li znát všechny vlastnosti a jejich hodnoty daného identifikároru, použijeme k tomu tento příkaz.
Naproti tomu příkaz ASK poskytuje jen odpovědi True nebo False na otázku, zda dané trojice jsou v datové sadě dostupné či nikoliv.
V uvedených trojicích se mohou vyskytovat proměnné, pak probíhá dosazování různých hodnot za tyto proměnné.
Porovnejte výsledky níže uvedených dotazů, které mají shodnou konstrukci WHERE:
# příkaz SELECT
PREFIX ds: <http://mre.zcu.cz/ontology/dasta.owl#>
SELECT *
WHERE {
?pacient a ds:Patient .
?pacient ds:patientID "666" .
?pacient ds:clinicalEvent ?vysetreni .
}
# příkaz DESCRIBE
PREFIX ds: <http://mre.zcu.cz/ontology/dasta.owl#>
DESCRIBE *
WHERE {
?pacient a ds:Patient .
?pacient ds:patientID "666" .
?pacient ds:clinicalEvent ?vysetreni .
}
# příkaz ASK
PREFIX ds: <http://mre.zcu.cz/ontology/dasta.owl#>
ASK
WHERE {
?pacient a ds:Patient .
?pacient ds:patientID "666" .
?pacient ds:clinicalEvent ?vysetreni .
}
DISTINCT,FILTER,VALUES,GROUP BY,HAVING,
Pro následující úlohy použijte současně data patient7-medical a patient7-imaging.
Můžete je nahrát:
FROM.Úlohy
ds:DiagCode) a abecedně je seřaďte.
dcm:Study)?
ds:patientID a číslo studie dcm:Study_ID.
dcm:Series)?
dcm:CT_Image)?
dcm:CT_Image) se každá ze sérií skládá, a seřaďte je od největší k nejmenší?
Vypište číslo série (dcm:Series_Number), popis (dcm:Series_Description) a počet souborů v sérii.
dcm:CT_Image) se každá ze sérií skládá, a jaký objem dat na disku zabírá?
Vypište číslo série (dcm:Series_Number), popis (dcm:Series_Description), počet souborů v sérii, celkovou velikost série v B i současně v MB.
Hodnotu velikosti v MB zaokrouhlete. Série seřaďte dle velikosti v Bytech.
ds:labNumberValue) jsou mimo stanovené referenční meze pro daného pacienta:
ds:labScale4 je referenční mez dolní,ds:labScale5 je referenční mez horní,ds:labLocalName), hodnotu, a obě referenční meze.
Data budou řazena vzestupně dle názvu, dolní meze, hodnoty a horní meze.
ds:labLocalName) mimo stanovené meze.
Zobrazit pouze ty, které se opakují (alespoň 2x) a seřadit dle počtu (sestupně) a názvů (vzestupně).
Copyright © 2023 Petr Včelák a Martin Zíma