This Computational Provenance Record documents the computing environment, software dependencies, computational provenance, and bibliographic references associated with the RAISINS Qualitative Data Analysis module. It is intended to support computational reproducibility and software transparency. Detailed methodology and user guidance are provided separately in the official module documentation.
Any issues or updates required please comment here
Text is split into lowercased unigrams with tidytext::unnest_tokens(); a token is kept only if it contains an alphabetic character and its length is ≥ 3
Stopword removal
On by default: removes English stopwords (stopwords::stopwords("en") , and user can give custom stopwords through tidytext::stop_words
Lemmatisation
On by default: tokens reduced to dictionary lemma via textstem::lemmatize_words()
Frequency metric
Raw term frequency = token count per word (dplyr::count()).
Sentiment
Analysis
(lexicon / valence)
Unit of analysis
Sentences are split on . ! ? boundaries via stringr::str_split(text, "(?<=[.!?])\\s+")
Bing
Binary positive/negative lexicon summed per unit via syuzhet::get_sentiment(method = "bing")
AFINN
Integer-valence lexicon (−5 to +5) summed per unit via syuzhet::get_sentiment(method = "afinn")
NRC
Eight emotions plus positive/negative polarity via syuzhet::get_nrc_sentiment()
VADER
Sentence-level valence with negation and valence-shifter handling via sentimentr::sentiment_by() (ave_sentiment)
5 R Code for Key Analytical Steps
The blocks below reproduce the exact computation behind each reported result on a small self-contained example corpus.
5.1 Word Frequency
library(dplyr)library(tidytext)library(textstem)library(stringr)# Model sentencestext_data <- tibble::tibble(text =c("The maize yield was really good this season.","Farmers reported good rainfall and healthy maize crops.","The delayed monsoon affected sowing in several regions.","Despite the challenges, farmers adapted their farming practices." ))# Settings (default values)min_len <-3L # Minimum token lengthremove_stop <-TRUE# Remove stopwordslemmatise <-TRUE# Lemmatise wordscustom_stop <-character(0) # Additional user-defined stopwordstop_n <-40L # Number of most frequent words to display# Default stopword listqda_stopwords <-unique(c( stopwords::stopwords("en"), tidytext::stop_words$word,c("yes", "no", "na", "n/a", "etc", "also", "like", "really", "thing", "things")))# 1. Split the text into lowercase words and keep only alphabetic# words with at least the specified minimum length.tokens <- text_data %>%unnest_tokens(word, text) %>%filter(str_detect(word, "[a-z]")) %>%filter(nchar(word) >= min_len)# 2. Remove stopwords (default enabled)if (remove_stop) tokens <- tokens %>%filter(!word %in%c(qda_stopwords, custom_stop))# 3. Convert words to their base (lemma) form (default enabled)if (lemmatise) tokens <- tokens %>%mutate(word =lemmatize_words(word))# 4. Count the frequency of each word and compute its relative frequency.total_tokens <-nrow(tokens)word_frequency <- tokens %>%count(word, sort =TRUE, name ="Count") %>%mutate(`Relative frequency (%)`=round(100* Count / total_tokens, 2))head(word_frequency, top_n)
library(syuzhet)library(sentimentr)library(stringr)library(dplyr)# Unit of analysis: "doc", "sentence" or "segment"unit <-"sentence"text <-c("The maize yield was really good this season.","Unfortunately the late rains ruined part of the harvest.")# --- Build units ---# "sentence": split on . ! ? ; "doc": whole text; "segment": the coded span textif (unit =="sentence") { units <-unlist(stringr::str_split(text, "(?<=[.!?])\\s+")) units <- units[nzchar(trimws(units))]} else { units <- text}# Whole-document scores are divided by word count; sentence/segment left as-isavg_if_doc <-function(score, txt) {if (unit !="doc") return(score) nw <- stringr::str_count(txt, "\\S+"); nw[is.na(nw) | nw ==0] <-1L score / nw}# --- Bing: summed +/-1 lexicon ---bing <-avg_if_doc(suppressWarnings(syuzhet::get_sentiment(units, method ="bing")), units)# --- AFINN: summed -5..+5 lexicon ---afinn <-avg_if_doc(suppressWarnings(syuzhet::get_sentiment(units, method ="afinn")), units)# --- NRC: 8 emotions + polarity; score = positive - negative ---nrc_mat <-suppressWarnings(syuzhet::get_nrc_sentiment(units))nrc <-avg_if_doc(nrc_mat$positive - nrc_mat$negative, units)# --- VADER: sentence-level valence via sentimentr (negation / valence-shifter# aware; replaces the archived 'vader' package). Already length-normalised ---vader <- sentimentr::sentiment_by(units, by =NULL)$ave_sentiment# --- Polarity label (lexicons use 0; VADER uses a +/-0.05 neutral dead-band) ---polarity <-function(score, deadband =0) dplyr::case_when( score > deadband ~"Positive", score <-deadband ~"Negative",TRUE~"Neutral")data.frame(unit = units, bing, afinn, nrc, vader,bing_polarity =polarity(bing),vader_polarity =polarity(vader, 0.05))
Explore the entire Qualitative Analysis module in preview mode using the demo project. To submit suggestions or report a workflow issue, please use the discussion section below, or visit the official RAISINS website.
6 RAISINS Native Analytical Framework
RAISINS uses R for all its computations. Every package used to generate major results is listed and demonstrated with examples, so results can be reproduced independently. These results are then organized and formatted on the RAISINS website along with visualisation to make them easier to use and interpret. RAISINS also has its own custom-built tools for managing workflows, validating results, and generating reports. Details of these are not fully covered here, they’re shared with outside researchers only on request, and are subject to licensing terms.
7 Package References
R Core Team. (2025). R: A language and environment for statistical computing. R Foundation for Statistical Computing, Vienna, Austria. https://www.R-project.org/
Silge, J., & Robinson, D. (2016). tidytext: Text Mining and Analysis Using Tidy Data Principles in R. Journal of Open Source Software, 1(3), 37. https://doi.org/10.21105/joss.00037
Rinker, T. W. (2018). textstem: Tools for stemming and lemmatizing text (R package version 0.1.4). https://github.com/trinker/textstem
Benoit, K., Muhr, D., & Watanabe, K. (2021). stopwords: Multilingual Stopword Lists (R package version 2.3). https://doi.org/10.32614/CRAN.package.stopwords
Jockers, M. L. (2015). syuzhet: Extract Sentiment and Plot Arcs from Text (R package version 1.0.7). https://github.com/mjockers/syuzhet
Rinker, T. W. (2021). sentimentr: Calculate Text Polarity Sentiment (R package version 2.9.0). https://github.com/trinker/sentimentr
Wickham, H. (2019). stringr: Simple, Consistent Wrappers for Common String Operations (R package version 1.6.0). https://doi.org/10.32614/CRAN.package.stringr
Wickham, H., François, R., Henry, L., & Müller, K. (2023). dplyr: A Grammar of Data Manipulation (R package version 1.2.1). https://doi.org/10.32614/CRAN.package.dplyr
Le Pennec, E., & Slowikowski, K. (2023). ggwordcloud: A Word Cloud Geom for ggplot2 (R package version 0.6.2). https://doi.org/10.32614/CRAN.package.ggwordcloud
Wickham, H. (2016). ggplot2: Elegant Graphics for Data Analysis. Springer-Verlag New York. https://ggplot2.tidyverse.org
Allaire, J. J., Xie, Y., Dervieux, C., McPherson, J., Luraschi, J., Ushey, K., Atkins, A., Wickham, H., Cheng, J., Chang, W., & Iannone, R. (2026). rmarkdown: Dynamic Documents for R (R package version 2.31). https://github.com/rstudio/rmarkdown