Methods Hub (beta)

rwhatsapp

Abstract:

R package designed to work with WhatsApp text data

Type: Method
Topics: Data analysis
Tasks: Text mining
License: GNU General Public License v3.0 only
Programming Language: R
Code Repository: https://github.com/JBGruber/rwhatsapp
Code Repository Git Reference: 13e556c
Download URL: https://github.com/JBGruber/rwhatsapp/archive/refs/heads/master.zip
Published: July 28, 2026
Cite Feedback Try it out

Description

rwhatsapp is a straightforward, robust R package for importing and parsing exported WhatsApp chat logs (Android/iOS) into tidy tabular data. It ingests .txt and .zip history files across locales/devices and exposes message text, timestamps, authors, and emojis for downstream analysis.

Use Cases

  • Import exported WhatsApp chats to study interpersonal communication
  • Describe conversations (e.g., messages per day; messages per author) to study activity and participation.
  • Explore emoji usage patterns and look up emoji names to understand affective dynamics.
  • Conduct token-based analyses (e.g., most-used words, tf–idf per author) with standard text-mining workflows.
  • Teaching: demonstrate tidy text pipelines on personal communication data.

Input Data

  • Accepted inputs: WhatsApp chat exports in plain text (.txt) or compressed archives (.zip) created via the app’s export function.
  • Locales/devices: Designed to handle multiple locales and both Android/iOS.
  • Example files that come with the package: sample.txt[system.file("extdata", "sample.txt", package = "rwhatsapp")]

Typical Structure

Lines with timestamps, author handles, message text; media as placeholders. If you extract it and open the .txt file it should look like this:

[06/03/25, 2:27:42] Markus: I just need a quick coffee, and then I’ll be back to normal. Hahaha [06/03/25, 2:27:47] Angela: My day has been totally random, but I’ll be right back as well!

Output Data

rwa_read() returns a tibble: - time, author, text, source, emoji, emoji_name

library("rwhatsapp")
history <- system.file("extdata", "sample.txt", package = "rwhatsapp")
chat <- rwa_read(history)
head(chat)
# A tibble: 6 × 6
  time                author          text              source emoji  emoji_name
  <dttm>              <fct>           <chr>             <chr>  <list> <list>    
1 2017-07-12 22:35:00 <NA>            "Messages to thi… /srv/… <NULL> <NULL>    
2 2017-07-12 22:35:00 <NA>            "You created gro… /srv/… <NULL> <NULL>    
3 2017-07-12 22:35:00 Johannes Gruber "<Media omitted>" /srv/… <NULL> <NULL>    
4 2017-07-12 22:35:00 Johannes Gruber "Fruit bread wit… /srv/… <chr>  <chr [2]> 
5 2017-07-13 09:12:00 Test            "It's fun doing … /srv/… <NULL> <NULL>    
6 2017-07-13 09:16:00 Johannes Gruber "Haha it sure is… /srv/… <chr>  <chr [1]> 

Hardware Requirements

  • R: ≥ 3.5.0
  • Resources: Small-to-moderate for typical chats.

Environment Setup

With R installed run:

install.packages("rwhatsapp")
library("rwhatsapp")

OR

remotes::install_github("JBGruber/rwhatsapp")
library("rwhatsapp")

Loading Dependencies:

RWhatsApp needs a number of other packages to be installed to ensure functionality. Always make sure to load these common packages into your environment when using RWhatsapp:

library("dplyr")
library("ggplot2")
library("lubridate")
library("tidyr")
library("tidytext")
library("stopwords")
library("magick")
library("ggimage")

How to Use

In the following example we will show you how to use the main features of the package. You may use the example data or are welcome to load your own chatlogs into the environment. In order to upload your chatlogs, you can use the “Upload File”-Function of R-Studio in this environment.

Load sample data and read into R

chat <- rwa_read("ExampleChat.txt")
chat
# A tibble: 4,547 × 6
   time                author      text                 source emoji  emoji_name
   <dttm>              <fct>       <chr>                <chr>  <list> <list>    
 1 2025-06-02 14:13:50 JSE Esports "‎Messages and calls… Examp… <NULL> <NULL>    
 2 2025-06-02 14:13:50 JSE Esports "‎You created the gr… Examp… <NULL> <NULL>    
 3 2025-06-02 14:13:54 Markus      "Morning everyone"   Examp… <NULL> <NULL>    
 4 2025-06-02 14:14:17 Markus      "Today it would be … Examp… <NULL> <NULL>    
 5 2025-06-02 14:14:29 Angela      "hii"                Examp… <NULL> <NULL>    
 6 2025-06-02 14:14:32 Angela      "I'll be at my PC f… Examp… <NULL> <NULL>    
 7 2025-06-02 14:14:39 Julian      "hey"                Examp… <NULL> <NULL>    
 8 2025-06-02 14:15:01 Markus      "I'll be there from… Examp… <NULL> <NULL>    
 9 2025-06-02 14:15:09 Julian      "that is cursed"     Examp… <NULL> <NULL>    
10 2025-06-02 14:15:22 Angela      "I feel personally … Examp… <NULL> <NULL>    
# ℹ 4,537 more rows

Loading own data

chat <- rwa_read("/path/to/your/WhatsAppChat.txt")

Remove system messages

In this step we are removing any messages, that are not sent from one of the two human authors in the chat. You may have seen an example of this in the Output-Data-Chapter earlier.

chat <- subset(chat, !is.na(author))

Messages per day

We are now able to extract i.E. how many messages were sent per day or on a specific day by both authors:

theme_set(theme_minimal())

chat %>%
  mutate(day = date(time)) %>%
  count(day) %>%
  ggplot(aes(day, n)) +
  geom_col() +
  labs(
    title = "Messages per Day",
    x = "Date",
    y = "Number of Messages"
  ) +
  scale_x_date(date_labels = "%d.%m.%Y")

Messages per author

If we want to split the message count, we can use the following feature to check how many messages were sent by each author:

chat %>%
  count(author) %>%
  ggplot(aes(x = reorder(author, n), y = n)) +
  geom_col() +
  coord_flip() +
  labs(
    title = "Messages per Author",
    x = NULL,
    y = "Number of Messages"
  ) +
  theme_minimal()

Emoji exploration

Sometimes it is of interest, which and how often emojis are used in a given chat. By using the following code chunk we can examine this further:

chat %>%
  unnest(emoji) %>%
  count(author, emoji, sort = TRUE) %>%
  rename(
    Author = author,
    Emoji = emoji,
    Frequency = n
  ) %>%
  knitr::kable()
Author Emoji Frequency
Max 👍 23
Jules 👍 11
Angela 😭 6
Richard 👍 5
Casper 👍 3
Jason 👍 3
Jerry 👍 3
Tjark 👍 3
Michael 👍 2
Felix 👍 1

Token-based exploration

We may also try to see how often a given author used a specific word/wording. By using the following code chunk we can examine this further:

chat %>%
  tidytext::unnest_tokens(input = text, output = word) %>%
  count(author, word, sort = TRUE) %>%
  rename(
    Author = author,
    Word = word,
    Frequency = n
  ) %>%
  select(Author, Word, Frequency)
# A tibble: 1,969 × 3
   Author Word    Frequency
   <fct>  <chr>       <int>
 1 Markus the           507
 2 Markus a             371
 3 Markus then          354
 4 Markus i'm           334
 5 Max    omitted       304
 6 Max    sticker       269
 7 Markus be            259
 8 Markus and           253
 9 Markus for           248
10 Markus to            240
# ℹ 1,959 more rows

This is not very useful at this point, because the analysis is “polluted” with words like “the”, “a” or “omitted”. How to remove those words in order to make the analysis more fruitful will be shown in the next chapter.

Deep Dive

After you are familiar with the package and its basic functionalities you are able to combine the diverse feature together to undergo deeper analysis. The following example will work especially well, if you have loaded your own WhatsApp-Data:

Another way to determine favourite words is to calculate the term frequency–inverse document frequency (tf–idf). Basically, what the measure does, in this case, is to find words that are common within the messages of one author but uncommon in the rest of the messages.

# First we define stopwords to be removed from the analysis. You may need to add further words to this list while investigating the dataset, such as "omitted". "Omitted" is a placeholder WhatsApp puts into the log file instead of a picture or video. Try to run the chunk and then add more words to the list to be removed, in order to make the analysis more fruitful.

to_remove <- c(stopwords(language = "en"),
               "addmorewordstoberemovedhere")

# Then we load our own dataframe (or leave it as is to use our larger example dataset)
chat <- rwa_read("ExampleChat.txt")

#Then we remove all system messages from the dataset
chat <- subset(chat, !is.na(author))

chat %>%
  unnest_tokens(input = text, output = word) %>%
  select(word, author) %>%
  filter(!word %in% to_remove) %>%
  mutate(word = gsub(".com", "", word)) %>%
  mutate(word = gsub("^gag", "9gag", word)) %>%
  count(author, word, sort = TRUE) %>%
  bind_tf_idf(term = word, document = author, n = n) %>%
  filter(n > 10) %>%
  group_by(author) %>%
  slice_max(tf_idf, n = 6, with_ties = FALSE) %>%
  ggplot(aes(x = reorder_within(word, tf_idf, author),
             y = tf_idf,
             fill = author)) +
  geom_col(show.legend = FALSE) +
  geom_text(aes(label = n), hjust = -0.2, size = 3) +
  ylab("") +
  xlab("") +
  coord_flip() +
  facet_wrap(~author, ncol = 2, scales = "free_y") +
  scale_x_reordered() +
  ggtitle("Frequency of each author's most distinctive words (selected by tf–idf)")

For further deep dives and more in depth tutorials visit the official documentation page.

Technical Details

See the CRAN REPOSITORY for technical information.

References

Disclaimer

This method is intended for academic research; ensure legality and ethics when processing chat data.

Contact Details