Spookify: Halloween Name Generation in R

It’s October, time for spooky Twitter names! If you’re on this social media platform, you might have noticed some of your friends switching their names to something spooky and punny. Last year I was “Maelstrom Salmon”, which I find scary but is arguably not that funny. Anyhow, what if you want to switch your name but have no inspiration? In this post, we shall explore R’s abilities to help us with that with the help of webscraping, phonetic spelling and string distance algorithms, and the magic of randomness!

My strategy for spooky name generation will be to replace each part of a name (e.g. first and last names) with the phonetically closest Halloween-related words. Therefore, the technical challenges here were to find a list of Halloween-related words, and to measure phonetical string distance.

Getting spooky words

The otherwise very useful package rcorpora doesn’t have a “spooky” category, so I decided to scrape this webpage of Halloween words for kids. It was one of the first search results, and being for kids it couldn’t be offensive. Like the last two times I webscraped, I used the very cool polite package!

There were two less useful sections on the page, with French and Spanish words, that I very inelegantly removed after looking up their div ID in the page source.

# introduce myself
session <- polite::bow("https://holidappy.com/holidays/halloween-words-and-halloween-vocabulary-lists",
                       user_agent = "Maëlle Salmon https://masalmon.eu/")

# scrape
page <- polite::scrape(session)
## No encoding supplied: defaulting to UTF-8.
# remove the two sections I don't want
French <- xml2::xml_find_all(page,
                            xpath = "//div[@id='mod_37589248']")

xml2::xml_remove(French)

Spanish <- xml2::xml_find_all(page,
                              xpath = "//div[@id='mod_37589143']")

xml2::xml_remove(Spanish)

# extract the words
words <- xml2::xml_find_all(page,
                            xpath = "//td[contains(@class, 'tableCell')]")

words <- xml2::xml_text(words)
head(words)
## [1] "All Hallows' Eve\n\t" "lantern\n\t"          "prank\n\t"           
## [4] "costume\n\t"          "make-believe\n\t"     "sweets\n\t"

I cleaned the words a bit. Some of them were not words so I split them. I hesitated between this solution and keeping only one-word phrases… I’m still not sure it was the best decision!

words <- stringr::str_remove_all(words, "\\\n\\\t")
words <- trimws(words)
words <- tokenizers::tokenize_words(words)
words <- unlist(words[lengths(words) > 0])
words <- words[!stringr::str_detect(words, "[0-9]")]
head(words)
## [1] "all"     "hallows" "eve"     "lantern" "prank"   "costume"

I obtained 258 words.

Matching names and spooky words

Once I had this vector of Halloween-related words, all I needed was a way to compute a phonetical distance between each of them and a name. It turned out more complicated than I thought! The soundex method of the stringdist package returned 1 or 0 only, so I set out to search another package supporting phonetical comparison. I found the phonics package that has a paper in JOSS, that implements more algorithms translating strings to phonetic “codes”. I ended up using the phonics::nysiis() function, corresponding to the New York State Identification and Intelligence System phonetic algorithm. That package also has an algorithm suitable for German, via phonics::cologne(), and one for French, via phonics::statcan(), but I haven’t explored that further.

phonics::nysiis("pain")
## [1] "PAN"
phonics::nysiis("pane")
## [1] "PAN"
phonics::nysiis("train")
## [1] "TRAN"

I was at a loss as to how best compare output from phonics::nysiis() and very hackily used… stringdist::stringdist() with a default method! Below is the function finding the closest match for any name part.

spookify_word <- function(word, seed = 42, words){

  phon <- phonics::nysiis(words)
  dist <- stringdist::stringdist(phonics::nysiis(word),
                                 phon)
  set.seed(seed)
  sample(words[dist == min(dist)], 1)
}
spookify_word("Jane", words = words)
## [1] "bones"

So spookify_word() samples a word among the closest matches… that aren’t necessarily very close to the original name. But that’s fine!

Mixing it all in a chauldron function

I finally created a function taking a whole name, say “Maëlle Salmon” or “Ada Colau Ballano”, tokenizes it into words thanks to the tokenizers package, gets the closest match for each part and then collapses everything together, capitalizing first letters thanks to snakecase… before announcing the result with the cool cowsay package! I made sure to only use animals that are associated with Halloween.

spookify <- function(name, seed,
                     words){
  name_parts <- unlist(tokenizers::tokenize_words(name))
  spook_names <- purrr::map_chr(name_parts,
                                spookify_word,
                                seed, words)
  spook_name <- paste(spook_names, collapse = " ")
  spook_name <- snakecase::to_upper_camel_case(spook_name,
                                               sep_out =  " ")
  
  set.seed(seed)
  animal <- sample(c("owl", "spider",
                     "pumpkin", "ghost",
                     "bat"), 1)

  cowsay::say(what = paste0("Byebye ", name, "\n",
                           "You are now ", spook_name),
              by = animal)

}

And because I didn’t want to make fun of anyone else than me, I created 7 fake names using the charlatan package and ran spookify on them!

set.seed(42)
names <- charlatan::ch_name(n = 7)
purrr::walk2(names, 1:length(names),
             spookify, words = words)
## Colors cannot be applied in this environment :( Try using a terminal or RStudio.

## 
## 
##  ----- 
## Byebye Tyrik Graham PhD
## You are now Trick Grinning Giant 
##  ------ 
##     \   
##      \
##               |
##               |
##               |
##              __
##           | /  \ |
##          \_\\  //_/
##           .'/()\'.
##            \\  //  [nosig]
## 

## Colors cannot be applied in this environment :( Try using a terminal or RStudio.

## 
##  ----- 
## Byebye Jeremy Rippin
## You are now Horrify Rotten 
##  ------ 
##     \   
##      \  
##       \
##        /\___/\
##        {o}{o}|
##        \ v  /|
##        |    \ \
##         \___/_/       [ab] 
##           | |

## Colors cannot be applied in this environment :( Try using a terminal or RStudio.

## 
##  ----- 
## Byebye Margarett Purdy
## You are now Boogers Party 
##  ------ 
##     \   
##      \  
##       \
##        /\___/\
##        {o}{o}|
##        \ v  /|
##        |    \ \
##         \___/_/       [ab] 
##           | |

## Colors cannot be applied in this environment :( Try using a terminal or RStudio.

## 
## 
##  ----- 
## Byebye Miss Tilla Funk DDS
## You are now Ooze Vil Fangs Dead 
##  ------ 
##     \   
##      \
##                   ___
##                ___)__|_
##           .-*'          '*-,
##          /      /|   |\     \
##         ;      /_|   |_\     ;
##         ;   |\           /|  ;
##         ;   | ''--...--'' |  ;
##          \  ''---.....--''  /
##           ''*-.,_______,.-*'  [nosig]
## 

## Colors cannot be applied in this environment :( Try using a terminal or RStudio.

## 
## 
##  ----- 
## Byebye Wilton Carroll IV
## You are now Cartoon Cruella Eve 
##  ------ 
##     \   
##      \
##               |
##               |
##               |
##              __
##           | /  \ |
##          \_\\  //_/
##           .'/()\'.
##            \\  //  [nosig]
## 

## Colors cannot be applied in this environment :( Try using a terminal or RStudio.

## 
## 
##  ----- 
## Byebye Tevin Conn
## You are now Coffin Bones 
##  ------ 
##     \   
##      \
##      .-.
##     (o o)
##     | O \
##      \   \
##       `~~~' [nosig]
## 

## Colors cannot be applied in this environment :( Try using a terminal or RStudio.

## 
## 
##  ------------- 
## Byebye Dr. Ballard Langworth III
## You are now Dish Blood Cemetery Dish 
##  -------------- 
##               \   
##                \  
##                 \
##         __.--'\     \.__./     /'--.__
##     _.-'       '.__.'    '.__.'       '-._
##   .'                                      '.
##  /                                          \
## |                                            |
## |                                            |
##  \         .---.              .---.         /
##   '._    .'     '.''.    .''.'     '.    _.'
##      '-./            \  /           \.-'
##                       ''mrf

Note that I reported the message I got from cowsay. It’s not too bad, cowsay is not designed for use in R Markdown I think.

I found the results not fantastic, but not too bad either! I especially liked “Boogers Party”!

Mucus, anyone?

In conclusion, in this post I showed how to build a corpus of Halloween-related words by responsibly webscraping, and how to more or less hackily compare strings based on their pronunciation. I obviously tried the function on my own name (without the accent, useless in this case) and wasn’t too happy…

spookify("Maelle Salmon", 42, words)
## Colors cannot be applied in this environment :( Try using a terminal or RStudio.

## 
## 
##  ------------- 
## Byebye Maelle Salmon
## You are now Mucus Bulging 
##  -------------- 
##               \   
##                \  
##                 \
##         __.--'\     \.__./     /'--.__
##     _.-'       '.__.'    '.__.'       '-._
##   .'                                      '.
##  /                                          \
## |                                            |
## |                                            |
##  \         .---.              .---.         /
##   '._    .'     '.''.    .''.'     '.    _.'
##      '-./            \  /           \.-'
##                       ''mrf

But then, at least, it is scary.

 
comments powered by Disqus