Borderlands Sentiment analysis

Author

Ben H

Borderlands 2 vs Borderlands the Pre-sequel

For this post I wanted to do a sentiment analysis on two of my most played games during my youth. Borderlands 2, which released in 2012 and won game of the year. And the following installment Borderlands the Pre-sequel which released in 2014.

These two games are first person shooters that popularized the “Shoot N Loot” game type. Similar games being the Fallout series and Destiny. They feature colorful character, a wide variety of creative and unique weaponry, and intricate stories that take place on alien planets.

I wanted to do a sentiment analysis on these topics because of the disparity between the two games. One of these games is known as the shining gem of the category. While the other is known to be a bit underdeveloped, too aspiration, and is accused of riding of its predecessor’s coat tails.

Where to scrape

I will be scraping from the Steam Community page found here: Steam Community. We set out filters to “Most Helpful (ALL TIME)” and alternated between positive and negative. This may cause some discrepancies with the sentiment analysis later on. As many of our visualizations can be skewed by just the length of each review. The longer the review, the more words pulled, and that can lead to our pulls lending to improper results. So here’s me, essentially just repeating the known issues with sentiment analysis pulls.

Loading packages and set Configuration

Lets load some of the necessary packages and some extras because I am a hypochondriac.

Additionally lets set our configuration

 #| echo: false

library(tidyverse)
library(rvest)
Warning: package 'rvest' was built under R version 4.3.3
library(lubridate)
library(httr)
library(dplyr)
library(readr)
library(stringr)
library(tidytext)
Warning: package 'tidytext' was built under R version 4.3.3
library(textdata)
Warning: package 'textdata' was built under R version 4.3.3
set_config(user_agent("Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; Googlebot/2.1; +http://www.google.com/bot.html) Chrome/W.X.Y.Z Safari/537.36"))

Data frame, function, and Loop

Now lets do our scrape and load it into our pre-set empty data frame

BorderFull <- data.frame()

ben_fun <- function(url) {
  
  link <-
    read_html(url)
  
Sys.sleep(2)
  
review <- 
  link %>%
  html_elements("div.apphub_CardTextContent") %>%
  html_text()

opinion <-
  link %>%
  html_elements("div.title") %>%
  html_text()

hoursplayed <- 
  link %>%
  html_elements("div.hours") %>%
  html_text()

helpful <- 
  link %>%
  html_elements("div.found_helpful") %>%
  html_text()

date <- 
  link %>%
  html_elements("div.date_posted") %>%
  html_text()

game <-
  link %>% 
  html_elements("div.apphub_AppName.ellipsis") %>% 
  html_text()


### Name combine
name1 <-
  link %>% 
  html_elements("div.apphub_CardContentAuthorName.online.ellipsis") %>% 
  html_text()

name2<-
  link %>% 
  html_elements("div.apphub_CardContentAuthorName.offline.ellipsis") %>%
  html_text()

name3 <-
  link %>% 
  html_elements("div.apphub_CardContentAuthorName.in-game.ellipsis") %>% 
  html_text()


BL_df <- data.frame("Posted" = date, "Review" = review, "Opinion" = opinion, 
                    "Hours Played" = hoursplayed, "Number of helpful vote" = helpful, 
                    "Title" = game, "Names" = c(name1,name2,name3))



return(BL_df)
}
#LOOP FUNCTION
for (i in seq_along(link)){
  BorderFull <- 
    ben_fun(link[i]) %>% 
    bind_rows(BorderFull)}

Convert to Tidy Text

Lets separate the words from each review so we can conduct the sentiment analysis

Joining with `by = join_by(word)`
`summarise()` has grouped output by 'Names'. You can override using the
`.groups` argument.
# A tibble: 3,783 × 3
# Groups:   Names [38]
   Names          word            n
   <chr>          <chr>       <int>
 1 Tongues Akimbo borderlands    22
 2 Vinyl Synth    borderlands    19
 3 SomaWolf       game           18
 4 caubert        game           15
 5 Vinyl Synth    game           14
 6 Wolf Echo      borderlands    14
 7 Aeylaris       game           13
 8 Tongues Akimbo pre            11
 9 Tongues Akimbo sequel         11
10 derkregin      game           11
# ℹ 3,773 more rows

Now to make some observations

Which game has more positive comments towards it? And does that make it a better game?

bing <- 
  get_sentiments("bing")

Bandit_counts <- 
  tidy_Pandora %>% 
  group_by(Title, word) %>% 
  summarize(n = n()) %>% 
  inner_join(bing)
`summarise()` has grouped output by 'Title'. You can override using the
`.groups` argument.
Joining with `by = join_by(word)`
Bandit_counts %>%
  group_by(Title,sentiment) %>% 
  summarize(n = n()) %>% 
  arrange(-n)
`summarise()` has grouped output by 'Title'. You can override using the
`.groups` argument.
# A tibble: 4 × 3
# Groups:   Title [2]
  Title                       sentiment     n
  <chr>                       <chr>     <int>
1 Borderlands: The Pre-Sequel negative    152
2 Borderlands 2               negative     92
3 Borderlands: The Pre-Sequel positive     88
4 Borderlands 2               positive     70
Bandit_counts %>% 
  group_by(Title) %>% 
  filter(n>3) %>% 
  mutate(n = ifelse(sentiment == "negative", -n, n)) %>% 
  mutate(word = reorder(word, n)) %>% 
  ggplot(aes(word, n)) +
  geom_col() +
  coord_flip() +
  facet_wrap(~Title, ncol = 2) +
  geom_text(aes(label = signif(n, digits = 3)), nudge_y = 8) +
  labs(title = "Positive and Negative Words for Borderlands 2 and Borderlands 1.5",
       subtitle = "Only words appearing at least 3 times are shown")

Here we can see some of the sentiments from each game. This is the most crucial observation we will see in this post as this seems to be the most accurate. Something interesting to look at is the negative words. This being a shooter, and a game known for its characters, words like enemy, loot, and damage have completely different conotations. Enemy and enemies could go either way. As the reviewer could be talking about the wide variety of enemies or about how the enemies are uninspired and tedious. Damage and loot are more than likely to be positive comments in this context. As damage is important in a shoot and loot because a main part of the combat is “Shoot cool gun at bad guy, big number shows up and health bar goes down”, its a satisfying thing to watch seeing as you level up in game you do more damage. Loot is CERTAINLY positive. The guns are what make these games fun. However, take it with a pinch of salt. There’s a chance that the context is the lack of loot or even that loot is hard to obtain. for this context lets assume its a positive.

In the visualization itself we can see a few things. Whats important here is seeing the negative words that appear for the Pre-Sequel that don’t appear for Borderlands 2. While there are positive terms that appear more frequently, we are at the mercy of the reviewers rhetoric. So seeing that there are more net negative words appearing for th Pre-Sequel is a strong indicator of which game is favored. Additionaly, negative comments tend to be written with strong emotion than positive ones. It is a fair assumption, and is accurate reading the full reviews, to say that negative reviews will have more content(words) than positive ones. This means with this basic bing dictionary, we will get skewed data. And with this in mind we will view the other visualizations.

What game has stronger emotions towards it?

Using the NRC sentiment dictionary lets look at the emotional values of the game sentiments. And to be cute with it, lets use the colors from their weapon rarities. Orange for legendary will represent Borderlands 2 while Purple for super rare will represent the Pre-Sequel.

nrc <- get_sentiments("nrc")

tidy_Pandora %>% 
  inner_join(nrc, by = "word", relationship = "many-to-many") %>% 
  group_by(sentiment, Title) %>% 
  summarize(n = n()) %>% 
  ggplot(aes(x = sentiment, y = n, fill = Title)) +
  geom_bar(stat = "identity", position = "dodge") +
  scale_fill_manual(values = c("#Ffa500","#8019C8")) +
  labs(title = "Game Sentiment Scores",
       subtitle = "Total number of emotive words scored ",
       y = "Total Number of Words",
       x = "Emotional Sentiment",
       fill = "Title")
`summarise()` has grouped output by 'sentiment'. You can override using the
`.groups` argument.

tidy_Pandora %>% 
  inner_join(nrc, by = "word", relationship = "many-to-many") %>% 
  group_by(sentiment, Title) %>% 
  summarize(n = n()) %>% 
  filter(sentiment %in% c("anticipation", "fear", "negative")) %>% 
  ggplot(aes(x = sentiment, y = n, fill = Title)) +
  geom_bar(stat = "identity", position = "dodge") +
  scale_fill_manual(values = c("#Ffa500","#8019C8")) +
  labs(title = "Game Sentiment Scores",
       subtitle = "Total number of emotive words scored ",
       y = "Total Number of Words",
       x = "Emotional Sentiment",
       fill = "Title")
`summarise()` has grouped output by 'sentiment'. You can override using the
`.groups` argument.

Again we can see the same issue where the less popular game seems to be outpacing the more beloved. This can still be attributed to the length of negative comments and the inaccuracy of simple dictionary based sentiment analysis when reviewing violent video games. But there is still something to be learned here.

Lets look at the size of the columns. Specifically Anticipation, Fear, negative, and positive. The sizes of these columns tell a story. We can see that the biggest difference in height is in negativity. Showing us that despite outclassing in every other category, the Pre-sequel is still valued less than BL2. Fear and Anticipation can be attributed to release dates, expectations, and in game combat.

The positive column illustrates how BL2 is the more beloved game despite being outpaced in the sentiment analysis. When you read these comments and take into account their length we find that the positive comments to the Pre-Sequel are more frequently being miss attributed to the game. While BL2 is still able to keep within a small margin with the Pre-Sequel, despite have much shorter reviews.

Now lets create a final data set that makes analysis simpler

sentiments <- 
  tidy_Pandora %>% 
  inner_join(nrc, by = "word", relationship = "many-to-many") %>% 
  group_by(sentiment, Names) %>% 
  summarize(n = n()) %>%
  pivot_wider(names_from = sentiment, values_from = n, values_fill = 0) %>%
  mutate(positivity = positive-negative)

Pandora_sents <- 
  left_join(BorderFull, sentiments, by = "Names") %>%
  replace(is.na(.),0)

This data set shows the sentiments per review from the initial scrapes. And can come in handy when comparing how negative posters comment vs positive posters.

How many positive and negative sentiments do we have for each game

Let’s give ourselves a little cheat sheet that better explains our visuals

entiment_counts <- Bandit_counts %>%
  count(sentiment)

Conclusion

When reading many of this comments we actually see that the negatives attributed to Borderlands 2 frequently feature complaint to 2K, the distributor, as opposed to game play. They have been putting pop-up adds on the home screen that have been crashing individuals games.

Meanwhile the Pre-Sequel feature far more genuine complaints about game play. It suffers multiple problems like slow initial game play to lengthen play times. As well as only one additional DLC ever being made.