Building Wordclouds in R

In this article, I will show you how to use text data to build word clouds in R. We will use a dataset containing around 200k Jeopardy questions. The dataset can be downloaded here (thanks to reddit user trexmatt for providing the dataset).

We will require three packages for this: tm, SnowballC, and wordcloud.

First, let’s load the required libraries and read in the data.

library(tm)
library(SnowballC)
library(wordcloud)

jeopQ <- read.csv('JEOPARDY_CSV.csv', stringsAsFactors = FALSE)

The actual questions are available in the Question column.

Now, we will perform a series of operations on the text data to simplify it.
First, we need to create a corpus.

jeopCorpus <- Corpus(VectorSource(jeopQ$Question))

Next, we will convert the corpus to a lowercase.

jeopCorpus <- tm_map(jeopCorpus, content_transformer(tolower))

Then, we will remove all punctuation and stopwords, and convert it to a plain text document.. Stopwords are commonly used words in the English language such as I, me, my, etc. You can see the full list of stopwords using stopwords('english').

jeopCorpus <- tm_map(jeopCorpus, removePunctuation)
jeopCorpus <- tm_map(jeopCorpus, PlainTextDocument)
jeopCorpus <- tm_map(jeopCorpus, removeWords, stopwords('english'))

Next, we will perform stemming. This means that all the words are converted to their stem (Ex: learning -> learn, walked -> walk, etc.). This will ensure that different forms of the word are converted to the same form and plotted only once in the wordcloud.

jeopCorpus <- tm_map(jeopCorpus, stemDocument)

Now, we will plot the wordcloud.

wordcloud(jeopCorpus, max.words = 100, random.order = FALSE)

This will produce the following wordcloud:
Screen Shot 2015-09-04 at 11.24.58 AM

There are a few ways to customize it.

  • scale: This is used to indicate the range of sizes of the words.
  • max.words and min.freq: These parameters are used to limit the number of words plotted. max.words will plot the specified number of words and discard least frequent terms, whereas, min.freq will discard all terms whose frequency is below the specified value.
  • random.order: By setting this to FALSE, we make it so that the words with the highest frequency are plotted first. If we don’t set this, it will plot the words in a random order, and the highest frequency words may not necessarily appear in the center.
  • rot.per: This value determines the fraction of words that are plotted vertically.
  • colors: The default value is black. If you want to use different colors based on frequency, you can specify a vector of colors, or use one of the pre-defined color palettes. You can find a list here.

That brings us to the end of this article. I hope you enjoyed it! As always, if you have questions, feel free to leave a comment or reach out to me on Twitter.

Edit: After struggling a lot, I resorted to StackOverflow for a fix. I forgot to convert the document to lower case. As explained in the StackOverflow thread, a lot of the words start with “The”, with an uppercase “T”, whereas stopwords has “the” with a lowercase “t”. This is what was causing the words “the” and “this” to appear in the wordcloud.

Note: I learnt this technique in The Analytics Edge course offered by MIT on edX. It is a great course and I highly recommend that you take it if you are interested in Data Science!

G
Author
ginobili0

Teja is a writer for DataScience+, working as a Data Analyst in Chicago. He is a recent graduate of the University of Rochester and enjoys working with data and building visualisations. In his spare …

18 articles on DataScience+
View all posts

18 Comments

  1. BL
    Bernardo Lares July 17, 2018

    textCloud <- function(text, lang = "english", exclude = c(), seed = 0, print = T, keep_spaces = FALSE) {

    require("tm")
    require("SnowballC")
    require("wordcloud")
    require("RColorBrewer")

    set.seed(seed)
    options(warn=-1)

    text <- as.character(text)

    if (keep_spaces == TRUE) {
    text <- gsub(" ", "_", text) # '_' deleted later on
    }

    ## Load the data as a corpus
    docs <- Corpus(VectorSource(text))

    ## Text transformation
    toSpace <- content_transformer(function (x , pattern) gsub(pattern, " ", x))
    docs <- tm_map(docs, toSpace, "/")
    docs <- tm_map(docs, toSpace, "@")
    docs <- tm_map(docs, toSpace, "\|")

    ## Cleaning the text
    # Convert the text to lower case
    docs <- tm_map(docs, content_transformer(tolower))
    # Remove numbers
    docs <- tm_map(docs, removeNumbers)
    # Remove english common stopwords
    docs <- tm_map(docs, removeWords, stopwords(lang))
    # Remove your own stop word (specify your stopwords as a character vector)
    docs <- tm_map(docs, removeWords, rbind("https", exclude))
    # Remove punctuations
    docs <- tm_map(docs, removePunctuation)
    # Eliminate extra white spaces
    docs <- tm_map(docs, stripWhitespace)

    ## Build a term-document matrix
    dtm <- TermDocumentMatrix(docs)
    m <- as.matrix(dtm)
    v <- sort(rowSums(m), decreasing=TRUE)
    d <- data.frame(word = names(v), freq=v)

    if (print == TRUE) {
    message(paste0(capture.output(head(d, 10)), collapse = "n"))
    }

    wordcloud(words = d$word, freq = d$freq,
    scale = c(3.5, .7),
    min.freq = 1,
    max.words = 200,
    random.order = FALSE,
    rot.per = 0.2,
    colors = brewer.pal(8, "Paired"))

    }

    Reply
    1. BL
      Bernardo Lares July 17, 2018

      Hope you find this useful 😉

      Reply
  2. HN
    Hariram N November 22, 2017

    Hi, I am unable to set a title to the worcloud.For example, I want to set the title as “Word Topic probabilities” and plot a wordcloud followed. How do I do this?

    Reply
  3. NM
    Nicole Miller October 13, 2017

    Not sure if this will still be checked, but I am using RStudio Version 1.0.153 is there a way to do this on that? I can’t figure out how to download R 3.4.1

    Reply
  4. SI
    Seyed Ibrahim July 4, 2017

    R version 3.4.0 (2017-04-21)

    Got two warnings:
    package ‘tm’ was built under R version 3.4.1
    package ‘wordcloud’ was built under R version 3.4.1

    No other warnings while running the code as given by you. When it comes to the last line, wordcloud(jeopCorpus, max.words = 100, random.order = FALSE)

    Error in simple_triplet_matrix(i, j, v, nrow = length(terms), ncol = length(corpus), :
    ‘i, j’ invalid

    Reply
  5. G
    gravedigger February 14, 2017

    What’s with these words in the cloud:

    countri, citi, includ, mani, lill, hous, becam, charact, compani, titl, centuri,featur, etc.

    Reply
  6. R
    Ryan September 2, 2015

    Also- why is “the” showing up in the first place? Isn’t it one of the contained stopwords in stopwords(‘english’)?

    Reply
    1. TK
      Teja K September 4, 2015

      I was just informed that I forgot to convert it to lower case. A lot of the questions start with “The”, with an uppercase T, and “the” in stopwords has lowercase t, which is why it wasn’t removed. I have updated the code to reflect this.

      Reply
  7. R
    Ryan September 2, 2015

    Hey Teja,

    Is there a way to specify which column you want to perform the word cloud on? For example, if you only wanted to create a wordcloud on the “Answer” column and not the whole dataframe?

    Thanks.

    Reply
    1. TK
      Teja K September 4, 2015

      Hello Ryan, if you look at the first line of code where we create the corpus, you can see that we created it using the question column. I’m afraid that the only way to do it with the answer column is just to use jeopQ$Answer instead of jeopQ$Question and repeat the rest of the code.

      Reply
  8. 1
    141xgc August 30, 2015

    Nice post…I ran into what seems to be a problem reported by others too though. Running tm under R 3.2.2 the removeWords step results in…

    jeopCorpus inspect(jeepCorpus[11:15])
    Error in inspect(jeepCorpus[11:15]) : object ‘jeepCorpus’ not found
    > inspect(jeopCorpus[11:15])

    The preceding steps executed correctly (I used inspect to look at the output).

    > jeopQ jeopCorpus jeopCorpus jeopCorpus <- tm_map(jeopCorpus, PlainTextDocument)

    Any thoughts on how this could be fixed? Thanks GC

    Reply
    1. 1
      141xgc October 26, 2015

      It’s really interesting. The various OS and R updates I ran since I posted this question seem to have taken care of the problem. Teja’s suggestion was to try adding lazy=true to the tm_map statements but that wasn’t necessary in the end. Thanks!!!

      Reply
  9. WG
    Wayne Gray August 30, 2015

    Nice. But no matter what I do, I cannot get rid of the words “this” and “the”. I have gone so far as to quit and restart R (so I would have nothing extra loaded in my environment) as well as to break your: line: jeopCorpus <- tm_map(jeopCorpus, removeWords, c('the', 'this', stopwords('english')))

    into two separate commands:

    jeopCorpus <- tm_map(jeopCorpus, removeWords, stopwords('english'))
    jeopCorpus <- tm_map(jeopCorpus, removeWords, c('the', 'this'))

    but when I plot the wordcloud, I still see "the" and "this"

    ?? Thanks!

    Reply
    1. TK
      Teja K August 31, 2015

      Hello Wayne, thank you for bringing this to my attention. It seems that running the commands in the wrong order is causing this to happen. I just ran the Corpus and the removeWords command (with only the word ‘the’) and I managed to make the word ‘the’ go away. I need to do some more tests, and I will update the article with a more concrete solution tomorrow.

      Reply
      1. R
        Ryan September 2, 2015

        I’m confused. Why is “the” showing up in the first place? Isn’t it one of the contained stopwords in stopwords(‘english’)?

        Reply
  10. SA
    Syed Ali August 29, 2015

    Nice article

    Reply
  11. SA
    Syed Ali August 29, 2015

    Did you learn how to use the term_score function in the course? Would you recommend it?

    Reply
    1. TK
      Teja K August 30, 2015

      Hello Syed, I am unaware of that function, sorry about that.

      Reply

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.