Я хотел определить возраст на момент смерти альпинистов, используя даты, указанные в DBpedia. Я быстро столкнулся с проблемой, когда в качестве даты указывалось 0, например, «1834-9-0». Это вызвало ошибку о том, что он не может быть преобразован в дату и время. Чтобы избежать этой ошибки, я хотел добавить фильтр с помощью функции DAY (), но сначала тип данных должен быть в datetime. Мой начальный код здесь:
#Problem code:
PREFIX d: <http://dbpedia.org/ontology/>
SELECT ?firstAscentPerson ?died ?diedDatetime ?born
( bif:datediff( 'year', xsd:dateTime( str(?born) ), xsd:dateTime( str(?died) ) ) ) AS ?age
WHERE
{
{
SELECT DISTINCT ?firstAscentPerson ?mountainLabel ?climberName ?died ?diedDatetime ?born
FROM <http://dbpedia.org>
WHERE
{
?country rdf:type d:Country.
?country dct:subject dbc:Countries_in_Europe.
?mountain d:locatedInArea ?country.
?mountain d:firstAscentYear ?firstAscent.
?mountain d:firstAscentPerson ?firstAscentPerson.
?firstAscentPerson d:deathDate ?died.
?firstAscentPerson d:birthDate ?born.
BIND(xsd:dateTime(str(?died)) as ?diedDatetime)
FILTER (DAY(?diedDatetime)>0)
}
LIMIT 30
}
}
Чтобы фильтр работал для этой проблемы, я преобразовал дату в строку, разделил ее на день, месяц и год, а затем добавил фильтр, чтобы значения былибольше нуля:
# Verbose working code
PREFIX d: <http://dbpedia.org/ontology/>
SELECT ?firstAscentPerson ?bornCleaned ?diedCleaned
( bif:datediff( 'year', xsd:dateTime( ?bornCleaned ), xsd:dateTime( ?diedCleaned ) ) ) AS ?age
WHERE
{
{
SELECT DISTINCT ?firstAscentPerson ?bornCleaned ?diedCleaned
FROM <http://dbpedia.org>
WHERE
{
?country rdf:type d:Country.
?country dct:subject dbc:Countries_in_Europe.
?mountain d:locatedInArea ?country.
?mountain d:firstAscentYear ?firstAscent.
?mountain d:firstAscentPerson ?firstAscentPerson.
?firstAscentPerson d:deathDate ?died.
?firstAscentPerson d:birthDate ?born.
# 1. Convert the date to string
BIND(str(?died) as ?diedString )
BIND(str(?born) as ?bornString )
# 2. Extract the date elements from the string
BIND(xsd:integer(strbefore(?diedString,"-")) as ?deathYear)
BIND(strafter(?diedString,"-") as ?deathMonthDay)
BIND(xsd:integer(strafter(?deathMonthDay,"-")) as ?deathDay)
BIND(xsd:integer(strbefore(?deathMonthDay,"-")) as ?deathMonth)
BIND(xsd:integer(strbefore(?bornString,"-")) as ?bornYear)
BIND(strafter(?bornString,"-") as ?bornMonthDay)
BIND(xsd:integer(strafter(?bornMonthDay,"-")) as ?bornDay)
BIND(xsd:integer(strbefore(?bornMonthDay,"-")) as ?bornMonth)
# 3. Concatenate the strings to provide a consistent date to prevent duplicates
BIND(concat(str(?bornYear),"-",str(?bornMonth),"-",str(?bornDay)) as ?bornCleaned)
BIND(concat(str(?deathYear),"-",str(?deathMonth),"-",str(?deathDay)) as ?diedCleaned)
# 4. Filter the results to make sure the date is in the correct format
FILTER(?deathDay > 0 && ?deathMonth > 0 && ?deathYear > 0 )
FILTER(?bornDay > 0 && ?bornMonth > 0 && ?bornYear > 0 )
}
LIMIT 100
}
}
Это выглядит как неэффективный запрос, который отлавливает только ту особую ошибку, по которой я фильтрую, например, если дата содержит неизвестный символ или текст, тогда этот запрос не будет выполнен. С более сложными запросами было бы нецелесообразно указывать все возможные условия, которые могли бы потерпеть неудачу. Было бы более эффективно, если бы существовала функция, которая улавливала любую ошибку в предоставленных входных данных и просто пропускала эту запись, если есть проблема.
Существует ли что-то подобное в SPARQL, если да, то как это можно реализовать для этой проблемы? Или лучший способ запросить данные как можно лучше, а затем выполнить дополнительные преобразования и очистку в чем-то вроде Pandas?