rlabuonora.com

Personas y lugares en las novelas de Mario Vargas Llosa

spacyR es una interfaz para usar una librería de NLP de python -spacy- desde R. En este post exploro un poco como detectar nombres de personas y de lugares usando esta librería para analizar cuáles son los personajes principales y los lugares más mencionados en cada capítulo de Las Travesuras de la Niña Mala.

Corpus

A partir del texto de las novelas (en inglés), creo un data frame con una línea por fila y columnas con el texto y el capítulo al que corresponde cada línea.

bad_girl_url <- "https://raw.githubusercontent.com/rlabuonora/mvll_nlp/master/txt/bad_girl.txt"
bad_girl_raw <- readLines(bad_girl_url, encoding = "UTF-8")

bad_girl_lines <- tibble(text = bad_girl_raw) %>%
  mutate(chapter = str_detect(text, "\\d{1,2}.?$"), # Detect 1, 2, 3 ..7
         chapter = cumsum(chapter)) %>%  # cumsum o fill
  tibble::rowid_to_column(var="doc_id")

Spacy

Una vez que importé el texto de la novela, uso spacy_parse para detectar las named entities. Una named entity es una entidad que aparece mencionada en el texto.

Spacy usa un algoritmo para detectar cuáles tokens en el texto pertenecen a una entidad y de que tipo de entidad se trata.

bad_girl_parsed <- spacy_parse(bad_girl_lines$text, lemma = FALSE, entity = TRUE, nounphrase = TRUE)

ents <- entity_extract(bad_girl_parsed, type = "all") %>% 
  mutate(doc_id = as.numeric(str_extract(doc_id, "\\d{1,4}"))) %>% 
  left_join(select(bad_girl_lines, chapter, doc_id))

head(ents)
##   doc_id sentence_id                  entity entity_type chapter
## 1      1           1                       1    CARDINAL       1
## 2     10           1       a_fabulous_summer        DATE       1
## 3     10           2             Pérez_Prado      PERSON       1
## 4     10           2                  twelve    CARDINAL       1
## 5     10           2                Carnival      PERSON       1
## 6     10           2 the_Lawn_Tennis_of_Lima     PRODUCT       1

El data frame ents tiene un doc_id que nos permite vincular estos resultados con el input original (donde tenemos a què capítulo pertence cada frase, el nombre de la entidad detectada, y el tipo de entidad detectada.)

Visualización

Para la visualizión del resultado, veamos cuáles son las entidades de tipo persona y lugar más nombradas en cada capítulo:

personas_por_capitulo <- ents %>% 
  filter(entity_type  == "PERSON") %>% 
  filter(!entity %in% c("’s")) %>% # sacar entidades mal clasificadas
  group_by(chapter, entity) %>% 
  summarize(mentions = n()) %>% 
  arrange(chapter, -mentions) %>% 
  top_n(5, mentions) # Los 5 personajes más mencionados

personas_por_capitulo %>% 
  ggplot(aes(entity, mentions, fill = entity)) +
  geom_col() +
  coord_flip() +
  facet_wrap(~chapter, scales="free_y") + 
  guides(fill=FALSE) +
  labs(y="Menciones", x="Personajes", 
       title = "Travesuras de la Niña Mala",
       subtitle="Personajes más mencionados por capítulo")

Ahora hacemos lo mismo pero nos quedamos con las entidades de tipo GPE: (Geopolitical Entity).

lugares_por_capitulo <- ents %>% 
  filter(entity_type  == "GPE") %>% # GPE => Geopolitical Entity
  filter(!entity %in% c("Salomón", "Alberta", "’s", 
                        "Mitsuko", "Elena")) %>% # Sacar ents mal clasificadas
  group_by(chapter, entity) %>% 
  summarize(mentions = n()) %>% 
  arrange(chapter, -mentions) %>% 
  top_n(5, mentions)

lugares_por_capitulo %>% 
  ggplot(aes(entity, mentions, fill = entity)) +
  geom_col() +
  coord_flip() +
  facet_wrap(~chapter, scales="free_y") + 
  guides(fill=FALSE) +
  labs(y="Menciones", x="Lugares", 
       title = "Travesuras de la Niña Mala",
       subtitle="Lugares más mencionados por capítulo")