Как извлечь год рождения и смерти из строки в R?

У меня есть первый абзац статей Википедии из пакета 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 логике, узнать год рождения и смерти?

Может быть, попробуйте query.wikidata.org построить запрос и использовать wiki_query ?

zx8754 25.06.2024 13:20
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
4
1
82
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Вот довольно грубый способ извлечения дат рождения и, если существует, смерти.
Создайте регулярное выражение для каждого из возможных форматов даты, вставьте их вместе с "|" в качестве разделителя и извлеките эти строки. Затем свяжите все в 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).

Marco 25.06.2024 14:37
Ответ принят как подходящий

Самый простой способ сделать это вместо использования 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

Хороший подход, отлично работает с минимальными данными. Выкидываю ошибки по моему образцу. Пытаюсь разобраться в проблеме.

Marco 25.06.2024 14:47

@Марко, в некоторых случаях могут быть случаи, когда страница Википедии не представляет собой простую замену пробелов подчеркиванием в имени человека, или есть страницы устранения неоднозначности, или страница не настроена с этим конкретным макетом. Возможно, лучше протестировать определенные подмножества вашего фрейма данных, чтобы выяснить, какие имена не подходят, и сузить круг.

Allan Cameron 25.06.2024 14:57

Есть ли у вас также предложения по поводу года смерти? Кажется, это только класс deathplace.

Marco 25.06.2024 17:18

Рассмотрите возможность использования URLencode, чтобы убедиться, что у вас действительный URL-адрес. Также это работает только для дат рождения, но можно использовать регулярное выражение для перемещения по странице для случаев смерти.

Onyambu 25.06.2024 17:27

@Onyambu URLencode было бы разумным дополнением, но оно не гарантирует существование URL-адреса. Это решение намеренно избегает регулярных выражений, предпочитая считывать предварительно помеченные поля данных, что обычно безопаснее. Я добавил этот метод и для смертей.

Allan Cameron 25.06.2024 19:08

создайте URL, используя url <- paste0("https://en.wikipedia.org/w/index.php?search = ", URLencode(name)). Это обеспечит существование URL-адреса. И будет давать результаты независимо от того, как указано имя, например, будет работать, если переданное имя будет только obama или mandela и т. д.

Onyambu 25.06.2024 21:36

Вот функция, которую можно использовать для получения нужной информации:

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

Другие вопросы по теме