Who is on the severed floor: Helly or Helena?

In the second season of Severance, there is speculation about who is actually on the severed floor: is it the Helly we got to know during season one or her “outie”, Helena? I decided to try to take a look at the language we’ve seen Helly and Helena use and see if it can help us figure out who this character actually is (SPOILER: I’m basically positive it’s Helena and have been so since the first episode of the second season, no amount of text analysis will change my mind because it’s all in her mannerisms! And her voice! And the weird way she points out the lack of cameras and microphones…). I started by tagging every line of dialogue spoken by this actress in season 1 as Helly or Helena. I then went through the season 2 transcript and tagged Helly, Helena, as well as any dialogue on the severed floor as “Unknown”. This analysis was made possible by the mdr R package, which used data originally compiled by the Severance wiki.

Term frequency, document frequency

In the text analysis space, a common way to examine how words are used by different individuals can be to look at the tf-idf statistic - this statistic looks at the term frequency (in our case how many times the character says a particular word) and the inverse document frequency (how many time the word is used across all three characters: Helly, Helena, and Unknown). Basically, this statistic will help us understand which words are unique to each of our three characters: Helly, Helena, and the third Unknown (which we hope to classify as one of the others). Let’s look at which words are ranked at the top for our three characters below (I’ll show the top 5, with ties, so there may be more).

Code
library(mdr)
library(tidyverse)
library(tidytext)
library(textclean)
library(widyr)
library(ggiraph)
library(quanteda)
library(quanteda.textstats)

df <- transcripts |>
  filter(str_detect(speaker, "Hel")) |>
  mutate(speaker = case_when(
    season == 1 & episode == 1 &
      minute(timestamp) > 34 ~ "Helena",
    season == 1 & episode == 2 &
      minute(timestamp) < 5 ~ "Helena",
    season == 1 & episode == 4 & 
      minute(timestamp) > 21.7 & 
      minute(timestamp) < 23 ~ "Helena",
    season == 1 & episode == 9 & 
      minute(timestamp) > 18 & minute(timestamp) < 20 ~ "Helena",
    episode == 9 & speaker == "Helly (on video)" ~ "Helena",
    season == 2 & episode == 1 & minute(timestamp) < 1 ~ "Helly",
    season == 2 & episode == 2 & minute(timestamp) < 2 ~ "Helly",
    season == 2 & episode == 2 & minute(timestamp) > 21 & minute(timestamp) < 23 ~ "Helly",
    season == 2 & episode == 2 ~ "Helena",
    season == 2 & episode == 3 & minute(timestamp) > 40 ~ "Helena",
    season == 2 ~ "Unknown",
    .default = speaker)
  ) 

tidy_df <- df |>
  mutate(
    dialogue = str_replace_all(dialogue, "’", "'"),
    dialogue = replace_contraction(dialogue)) |>
  unnest_tokens(word, dialogue) |>
  anti_join(stop_words, by = "word") 

tf_idf <- tidy_df |>
  count(speaker, word, sort = TRUE) |>
  bind_tf_idf(word, speaker, n)

similarity <- tf_idf |>
  pairwise_similarity(item = speaker, feature = word, value = tf_idf)

p <- tf_idf |>
  group_by(speaker) |>
  slice_max(tf_idf, n = 5) |>
  ungroup() |>
  ggplot(aes(tf_idf, fct_reorder(word, tf_idf), fill = speaker)) +
  geom_col_interactive(show.legend = FALSE, alpha = 0.9) +
  scale_fill_manual(values = c("#C15C58", "#5BA9D0", "#898699")) +
  facet_wrap(~speaker, ncol = 3, scales = "free_y") +
  labs(x = "tf-idf", y = NULL) + 
  theme_mdr() + 
  theme(panel.grid.minor = element_blank(),
        strip.background = element_rect(fill = "#CFE0E1")) 

p

Interesting, a few things jump out. First, we see that both dad and father show up on Helena’s; this makes sense, she mentions (or speaks with) her father a few times, and this wouldn’t come up on the severed floor. Another interesting one is “Harmony”, this also makes sense since Ms. Cobel would not be reffered to by her first name downstairs. Now, innie Helly, uses the words “scary”, “live”, and “life”, compared to our unknown friend, who uses the words “mushy” and “yeah” more often. Both mention goats (this shows up on each because of the plural in Helly’s and the singular in our Unknown one). Alright, let’s turn these pictures into numbers. We can use cosine similarity to compare our tf-idf vectors for each of our three friends (well, two friends, our third is just one of them, presumably!). Higher numbers would imply that they are more similar, lower less.

  • Helena and Helly have a similarity score of 0.037.
    Helena and Unknown have a similarity score of 0.001.
    Helly and Unknown have a similarity score of 0.092.

Interesting! So at least in terms of the distinguishing words they use, our unknown friend is closer to Helly than Helena (and in fact closer to Helly than Helly is to her outie-self, Helena!)

But alas, this is just one way to look at the data! What about readability?

Readability

Let’s look at four measures:

  1. The average number of syllables per word
  2. The average sentence length
  3. The Flesch’s Reading Ease Score
  4. Scrabble score

Examining the plots below, Helena seems to use higher syllabic words and longer sentences; her dialogue has a lower readability score compared to the other two. They look similar in terms of how their vocabulary would score in Scrabble.

Code
readability_df <- df |>
  mutate(dialogue = str_replace_all(dialogue, "’", "'")) |>
  group_by(speaker) |>
  summarise(text = paste(dialogue, collapse = ". "))

corpus <- corpus(readability_df, text_field = "text")
# text1: Helena, text2: Helly, text3: Unknown

readability_scores <- textstat_readability(corpus,
                                           measure = c("meanWordSyllables",
                                                       "meanSentenceLength",
                                                       "Flesch",
                                                       "Scrabble"))

readability_scores$speaker <- c("Helena", "Helly", "Unknown")

readability_scores |>
  select(-document) |>
  rename(`Avg Syllables` = meanWordSyllables,
         `Avg Sentence Length` = meanSentenceLength) |> 
  pivot_longer(cols = `Avg Syllables`:Scrabble) |>
  ggplot(aes(x = speaker, y = value, fill = speaker)) +
  geom_col() +
  scale_fill_manual(values = c("#C15C58", "#5BA9D0", "#898699")) +
  facet_wrap(~name, scales = "free") + 
  labs(x = "") +
  theme_mdr() + 
  theme(legend.position = "none",
        panel.grid.minor = element_blank(),
        strip.background = element_rect(fill = "#CFE0E1"))

Code
sentiment_df <- tidy_df |>
  inner_join(get_sentiments("afinn"), by = "word")

sentiment_summary <- sentiment_df |>
  group_by(speaker) |>
  summarise(sentiment = mean(value))

Sentiment

Ok, let’s try looking at the sentiment of their words. We can use the AFINN sentiment lexicon to classify the words they each use and then examine the average sentiment score, do they tend to speak positively or negatively?

  • Helena’s average sentiment score is -0.06.
  • Helly’s average sentiment score is -0.79.
  • Unknown’s average sentiment score is -0.78.

So, this makes it look like Helena’s words are fairly neutral, while Helly and our Unknown friend are more negative.

Conclusions

What does this tell us? Probably nothing, Helena was seen watching tapes of the severed floor and certainly could be studying the words that Helly used, or the unknown person could actually just be Helly, like she says (I don’t think so!!). Maybe we need to curate a dataset of facial expressions…stay tuned?