У меня есть первый абзац статей Википедии из пакета wikifacts
(только для людей). Мне нравится извлекать год рождения и год смерти.
library(wikifacts)
library(tidyverse)
politicians <- data.frame(
Name = c("Barack Obama", "Angela Merkel", "Nelson Mandela", "Margaret Thatcher", "Mahatma Gandhi"),
stringsAsFactors = FALSE
)
politicians <- politicians %>%
mutate(First_Paragraph = substr(wiki_define(Name), 1, 200))
head(politicians)
> head(politicians)
Name
1 Barack Obama
2 Angela Merkel
3 Nelson Mandela
4 Margaret Thatcher
5 Mahatma Gandhi
First_Paragraph
1 Barack Hussein Obama II (born August 4, 1961) is an American politician who served as the 44th president of the United States from 2009 to 2017. As a member of the Democratic Party, he was the first A
2 Angela Dorothea Merkel (German: [aŋˈɡɪːla doʁoˈteːa ˈmɛʁkl̩] ; née Kasner; born 17 July 1954) is a retired German politician who served as Chancellor of Germany from 2005 to 2021 and was the first wom
3 Nelson Rolihlahla Mandela ( man-DEH-lə; Xhosa: [xolíɬaɬa mandɛ̂ːla]; born Rolihlahla Mandela; 18 July 1918 – 5 December 2013) was a South African anti-apartheid activist, politician, and statesman who
4 Margaret Hilda Thatcher, Baroness Thatcher, (née Roberts; 13 October 1925 – 8 April 2013) was a British stateswoman and Conservative politician who was Prime Minister of the United Kingdom from 1979
5 Mohandas Karamchand Gandhi (ISO: Mōhanadāsa Karamacaṁda Gāṁdhī; 2 October 1869 – 30 January 1948) was an Indian lawyer, anti-colonial nationalist and political ethicist who employed nonviolent resista
Мне нравится извлекать год рождения и, если возможно, год смерти. Обычно это первые две 4-значные цифры или первые две 4-значные цифры, находящиеся в первой паре круглых скобок. Я попробовал несколько способов регулярных выражений извлечения строк. Какой был бы хороший и простой способ, желательно в tidyverse
логике, узнать год рождения и смерти?
Вот довольно грубый способ извлечения дат рождения и, если существует, смерти.
Создайте регулярное выражение для каждого из возможных форматов даты, вставьте их вместе с "|"
в качестве разделителя и извлеките эти строки. Затем свяжите все в data.frame.
library(wikifacts)
#> Warning: package 'wikifacts' was built under R version 4.4.1
library(tidyverse)
politicians <- data.frame(
Name = c("Barack Obama", "Angela Merkel", "Nelson Mandela", "Margaret Thatcher", "Mahatma Gandhi"),
stringsAsFactors = FALSE
)
politicians <- politicians %>%
mutate(First_Paragraph = substr(wiki_define(Name), 1, 200))
# regex for dates "day month.name year"
pat1 <- sprintf("\\d+ %s \\d+", month.name) |> paste(collapse = "|")
# regex for dates "month.name day, year"
pat2 <- sprintf("%s \\d+, \\d+", month.name) |> paste(collapse = "|")
# combine the two patterns above
pat <- paste(pat1, pat2, collapse = "|")
dat <- str_extract_all(politicians$First_Paragraph, pat)
# do we have two dates?
Max <- dat |> lengths() |> max()
lapply(dat, \(x) if (length(x) == Max) x else c(x, NA_character_)) |>
do.call(rbind.data.frame, args = _) |>
setNames(c("Birth", "Death"))
#> Birth Death
#> 1 August 4, 1961 <NA>
#> 2 17 July 1954 <NA>
#> 3 18 July 1918 <NA>
#> 4 13 October 1925 8 April 2013
#> 5 2 October 1869 30 January 1948
Created on 2024-06-25 with reprex v2.1.0
Хорошее начало. Но применив это ко всем моим данным, я заметил, что есть больше закономерностей, которые иногда не фиксируются, например (; January 28, 1912 – August 11, 1956)
или 25 February 1841 – 3 December 1919)
.
Самый простой способ сделать это вместо использования wikifacts
— просто использовать rvest
для получения каждой страницы Википедии и поиска <span>
по классу bday
. Он содержит дату в формате ГГГГ-ММ-ДД, поэтому ее легко преобразовать в объект даты:
library(tidyverse)
library(rvest)
politicians %>%
rowwise() %>%
mutate(doc = list("https://en.wikipedia.org/wiki/" %>%
paste0(Name %>% stringr::str_replace_all(' ', '_')) %>%
read_html())) %>%
ungroup() %>%
mutate(DOB = map(doc, ~ .x %>%
html_element('.bday') %>%
html_text()) %>% unlist() %>% as.Date(),
DOD = map(doc, function(x) {
vals <- x %>%
html_elements(xpath = paste0("//td[@class='infobox-data']/",
"span[@style='display:none']")) %>%
html_text()
if (length(vals) == 2) substr(vals[2], 2, 11) else NA_character_
}) %>% unlist() %>% as.Date()) %>%
select(-doc)
#> # A tibble: 5 x 3
#> Name DOB DOD
#> <chr> <date> <date>
#> 1 Barack Obama 1961-08-04 NA
#> 2 Angela Merkel 1954-07-17 NA
#> 3 Nelson Mandela 1918-07-18 2013-12-05
#> 4 Margaret Thatcher 1925-10-13 2013-04-08
#> 5 Mahatma Gandhi 1869-10-02 1948-01-30
Хороший подход, отлично работает с минимальными данными. Выкидываю ошибки по моему образцу. Пытаюсь разобраться в проблеме.
@Марко, в некоторых случаях могут быть случаи, когда страница Википедии не представляет собой простую замену пробелов подчеркиванием в имени человека, или есть страницы устранения неоднозначности, или страница не настроена с этим конкретным макетом. Возможно, лучше протестировать определенные подмножества вашего фрейма данных, чтобы выяснить, какие имена не подходят, и сузить круг.
Есть ли у вас также предложения по поводу года смерти? Кажется, это только класс deathplace
.
Рассмотрите возможность использования URLencode
, чтобы убедиться, что у вас действительный URL-адрес. Также это работает только для дат рождения, но можно использовать регулярное выражение для перемещения по странице для случаев смерти.
@Onyambu URLencode
было бы разумным дополнением, но оно не гарантирует существование URL-адреса. Это решение намеренно избегает регулярных выражений, предпочитая считывать предварительно помеченные поля данных, что обычно безопаснее. Я добавил этот метод и для смертей.
создайте URL, используя url <- paste0("https://en.wikipedia.org/w/index.php?search = ", URLencode(name))
. Это обеспечит существование URL-адреса. И будет давать результаты независимо от того, как указано имя, например, будет работать, если переданное имя будет только obama
или mandela
и т. д.
Вот функция, которую можно использовать для получения нужной информации:
library(xml2)
dates <- function(name){
url <- paste0("https://en.wikipedia.org/w/index.php?search = ", URLencode(name))
html <- read_html(url)
bdate <- xml_text(xml_find_first(html, "//span[@class='bday']"))
name2 <- sub(" - Wikipedia", "", xml_text(xml_find_first(html, "//head/title")))
response <- read_xml(paste0("https://en.wikipedia.org/w/api.php?action=query&prop=extracts&exsentences = ",1,
"&exlimit=1&titles = ", URLencode(name2),
"&explaintext=1&format=xml"))
string <- xml_text(xml_find_first(response, "query/pages/page/extract"))
pat <- sprintf("(?<=%s)[^)]+",substr(bdate, 1, 4))
ddate <- sub("\\W+", "", regmatches(string, regexpr(pat, string, perl = TRUE)))
c(Birth = format(as.Date(bdate), "%d %B %Y"),
Death = if (length(ddate)) ddate else NA)
}
dates('mandela')
Birth Death
1 1918-07-18 2013-12-05
dates('obama')
Birth Death
1 1961-08-04 <NA>
Теперь пробежимся по политикам:
data.frame(t(sapply(politicians$Name, dates)))
Birth Death
Barack Obama 04 August 1961 <NA>
Angela Merkel 17 July 1954 <NA>
Nelson Mandela 18 July 1918 5 December 2013
Margaret Thatcher 13 October 1925 8 April 2013
Mahatma Gandhi 02 October 1869 30 January 1948
Может быть, попробуйте query.wikidata.org построить запрос и использовать
wiki_query
?