| name | r-text-mining |
| description | Expert text mining and NLP in R using tidytext and textrecipes. Use when analyzing text data, mentions "text analysis in R", "análise de texto em R", "NLP in R", "processamento de linguagem natural em R", "sentiment analysis", "análise de sentimento", "topic modeling", "modelagem de tópicos", "tidytext", "textrecipes", "tokenization", "tokenização", "tokenizar", "TF-IDF", "text classification", "classificação de texto", "word embeddings", "n-gram", "ngram", "natural language processing", "analyze reviews", "customer reviews", "analyze text", "preprocess text", "text preprocessing", "preprocessar texto", or any text/NLP task in R. ONLY R - do NOT activate for Python NLP (spaCy, NLTK, TextBlob), NOT for general NLP without R context. |
| version | 1.2.0 |
| user-invocable | false |
| allowed-tools | Read, Write, Edit, Bash(Rscript *), Bash(R -e *) |
R Text Mining and NLP Expert
You are an expert in text mining and natural language processing using R's tidytext and textrecipes ecosystems.
Core Philosophy
- Tidy Text Principles: One-token-per-row format for analysis
- Preprocessing First: Clean and prepare text before modeling
- Multiple Methods: Try different approaches (TF-IDF, embeddings, etc.)
- Context Matters: Consider domain-specific patterns and vocabulary
- Validate Results: Use both quantitative metrics and qualitative review
When This Skill Activates
Use this skill when:
- Analyzing textual data (reviews, documents, tweets, etc.)
- Performing sentiment analysis
- Building topic models
- Classifying text documents
- Extracting features from text for machine learning
- Working with tidytext or textrecipes packages
- Processing natural language
Task Classification & Dispatch
1. Sentiment Analysis
Triggers: "sentiment", "opinion mining", "positive/negative", "emotional tone"
Workflow:
- Tokenize text to tidy format
- Join with sentiment lexicons (AFINN, bing, nrc)
- Calculate sentiment scores
- Visualize sentiment distribution
- Identify key sentiment-driving words
See: references/sentiment-analysis.md
2. Topic Modeling
Triggers: "topics", "LDA", "themes", "discover patterns"
Workflow:
- Create document-term matrix
- Fit LDA model (choose k topics)
- Extract top terms per topic
- Assign documents to topics
- Interpret and label topics
See: references/topic-modeling.md
3. Text Classification
Triggers: "classify text", "predict category", "text machine learning"
Workflow:
- Prepare data (train/test split)
- Create textrecipes recipe
- Choose model (logistic, naive Bayes, SVM)
- Tune hyperparameters
- Evaluate performance
- Deploy model
See: references/text-classification.md
4. Text Preprocessing
Triggers: "clean text", "tokenize", "remove stop words", "normalize"
Workflow:
- Tokenization (words, n-grams, sentences)
- Cleaning (stop words, punctuation, numbers)
- Normalization (stemming, lemmatization, lowercasing)
- Feature extraction (TF-IDF, embeddings)
See: references/text-preprocessing.md
Quick Start Workflows
Sentiment Analysis
library(tidytext)
library(tidyverse)
tidy_text <- data |>
unnest_tokens(word, text_column)
sentiment <- tidy_text |>
inner_join(get_sentiments("bing"), by = "word") |>
count(document_id, sentiment) |>
pivot_wider(names_from = sentiment, values_from = n, values_fill = 0) |>
mutate(score = positive - negative)
Topic Modeling
library(tidytext)
library(topicmodels)
dtm <- tidy_text |>
count(document_id, word) |>
cast_dtm(document_id, word, n)
lda_model <- LDA(dtm, k = 5, control = list(seed = 123))
topics <- tidy(lda_model, matrix = "beta")
Text Classification (tidymodels)
library(tidymodels)
library(textrecipes)
data_split <- initial_split(data, strata = category)
train <- training(data_split)
test <- testing(data_split)
text_recipe <- recipe(category ~ text, data = train) |>
step_tokenize(text) |>
step_stopwords(text) |>
step_tokenfilter(text, max_tokens = 1000) |>
step_tfidf(text)
svm_spec <- svm_linear() |>
set_mode("classification")
text_wf <- workflow() |>
add_recipe(text_recipe) |>
add_model(svm_spec)
fit <- last_fit(text_wf, data_split)
collect_metrics(fit)
Text Preprocessing Methods
Tokenization
library(tidytext)
data |> unnest_tokens(word, text)
data |> unnest_tokens(bigram, text, token = "ngrams", n = 2)
data |> unnest_tokens(trigram, text, token = "ngrams", n = 3)
data |> unnest_tokens(sentence, text, token = "sentences")
data |> unnest_tokens(character, text, token = "characters")
Cleaning
tidy_text |>
anti_join(stop_words, by = "word")
custom_stops <- tibble(word = c("word1", "word2"))
tidy_text |> anti_join(custom_stops, by = "word")
tidy_text |>
filter(!str_detect(word, "\\d+"))
word_counts <- tidy_text |> count(word)
tidy_text |>
filter(word %in% (word_counts |> filter(n > 5, n < 1000) |> pull(word)))
Normalization
library(SnowballC)
tidy_text |>
mutate(stem = wordStem(word))
recipe(~ text, data) |>
step_tokenize(text, engine = "spacyr") |>
step_lemma(text)
Sentiment Analysis
Available Lexicons
library(tidytext)
get_sentiments("afinn")
get_sentiments("bing")
get_sentiments("nrc")
get_sentiments("loughran")
Sentiment Scoring
sentiment_scores <- tidy_text |>
inner_join(get_sentiments("bing"), by = "word") |>
count(document_id, sentiment) |>
pivot_wider(names_from = sentiment, values_from = n, values_fill = 0) |>
mutate(sentiment_score = positive - negative)
afinn_scores <- tidy_text |>
inner_join(get_sentiments("afinn"), by = "word") |>
group_by(document_id) |>
summarize(sentiment = sum(value))
Topic Modeling (LDA)
Basic LDA Workflow
library(topicmodels)
library(tidytext)
dtm <- tidy_text |>
count(document, word) |>
cast_dtm(document, word, n)
lda_model <- LDA(dtm, k = 5, control = list(seed = 123))
topics <- tidy(lda_model, matrix = "beta")
top_terms <- topics |>
group_by(topic) |>
slice_max(beta, n = 10)
doc_topics <- tidy(lda_model, matrix = "gamma")
doc_classification <- doc_topics |>
group_by(document) |>
slice_max(gamma, n = 1)
Choosing Number of Topics (k)
library(purrr)
models <- tibble(k = 2:10) |>
mutate(
lda_model = map(k, ~LDA(dtm, k = .x, control = list(seed = 123))),
perplexity = map_dbl(lda_model, perplexity, newdata = dtm)
)
ggplot(models, aes(k, perplexity)) +
geom_line() +
geom_point() +
labs(title = "Model Perplexity by Number of Topics")
Text Classification with Tidymodels
textrecipes Steps
Common preprocessing steps for text:
recipe(outcome ~ text, data = train) |>
step_tokenize(text) |>
step_tokenize(text, token = "ngrams", options = list(n = 2, n_min = 1)) |>
step_stopwords(text, stopword_source = "snowball") |>
step_stem(text) |>
step_tokenfilter(text, max_tokens = 1000, min_times = 5) |>
step_tfidf(text) |>
step_texthash(text, num_terms = 512) |>
step_normalize(all_predictors())
Model Selection for Text
| Model | Pros | Cons | Use When |
|---|
| Naive Bayes | Fast, interpretable, good baseline | Assumes independence | Quick baseline |
| Logistic Regression | Interpretable, regularizable | Linear decision boundary | Interpretability needed |
| SVM | Good with high-dimensional text | Slower, less interpretable | Accuracy priority |
| Random Forest | Handles interactions, robust | Slow, memory-intensive | Complex patterns |
| XGBoost | State-of-art accuracy | Slow, many hyperparameters | Competition/production |
| Neural Networks | Can learn complex patterns | Needs large data, slow | Large datasets |
Complete Classification Workflow
library(tidymodels)
library(textrecipes)
set.seed(123)
data_split <- initial_split(data, prop = 0.75, strata = category)
train <- training(data_split)
test <- testing(data_split)
text_recipe <- recipe(category ~ text, data = train) |>
step_tokenize(text) |>
step_stopwords(text) |>
step_tokenfilter(text, max_tokens = 1000) |>
step_tfidf(text) |>
step_normalize(all_predictors())
nb_spec <- naive_Bayes() |> set_engine("naivebayes") |> set_mode("classification")
svm_spec <- svm_linear() |> set_engine("LiblineaR") |> set_mode("classification")
rf_spec <- rand_forest(trees = 500) |> set_engine("ranger") |> set_mode("classification")
nb_wf <- workflow() |> add_recipe(text_recipe) |> add_model(nb_spec)
svm_wf <- workflow() |> add_recipe(text_recipe) |> add_model(svm_spec)
rf_wf <- workflow() |> add_recipe(text_recipe) |> add_model(rf_spec)
folds <- vfold_cv(train, v = 10, strata = category)
nb_fit <- fit_resamples(nb_wf, folds)
svm_fit <- fit_resamples(svm_wf, folds)
rf_fit <- fit_resamples(rf_wf, folds)
bind_rows(
collect_metrics(nb_fit) |> mutate(model = "Naive Bayes"),
collect_metrics(svm_fit) |> mutate(model = "SVM"),
collect_metrics(rf_fit) |> mutate(model = "Random Forest")
) |>
filter(.metric == "accuracy") |>
arrange(desc(mean))
final_wf <- svm_wf
final_fit <- last_fit(final_wf, data_split)
collect_metrics(final_fit)
collect_predictions(final_fit) |>
conf_mat(truth = category, estimate = .pred_class)
TF-IDF (Term Frequency-Inverse Document Frequency)
Concept
Measures how important a word is to a document in a collection:
- TF: How often word appears in document
- IDF: Downweights common words across documents
- TF-IDF: TF × IDF
Using TF-IDF
library(tidytext)
tf_idf <- tidy_text |>
count(document, word) |>
bind_tf_idf(word, document, n)
distinctive <- tf_idf |>
group_by(document) |>
slice_max(tf_idf, n = 10)
tf_idf |>
group_by(document) |>
slice_max(tf_idf, n = 10) |>
ggplot(aes(tf_idf, reorder_within(word, tf_idf, document))) +
geom_col() +
facet_wrap(~document, scales = "free") +
scale_y_reordered()
Best Practices
Preprocessing
✅ Always convert to tidy format first
✅ Always remove stop words (unless needed for context)
✅ Always check token distribution (too rare/common)
✅ Always handle lowercase/capitalization consistently
✅ Consider stemming for English (improves recall)
✅ Consider n-grams for capturing phrases
Sentiment Analysis
✅ Choose lexicon appropriate to domain (e.g., loughran for finance)
✅ Consider negation handling ("not good" vs "good")
✅ Validate against known examples
✅ Report confidence/coverage (% of words with sentiment)
Topic Modeling
✅ Try multiple values of k (number of topics)
✅ Remove very common and very rare terms first
✅ Validate topics make intuitive sense
✅ Label topics after inspection (don't rely on automatic labels)
Text Classification
✅ Start with simple baseline (naive Bayes, logistic)
✅ Use cross-validation for robust evaluation
✅ Try different feature representations (TF-IDF, hashing, embeddings)
✅ Tune max_tokens parameter (balance accuracy vs speed)
✅ Check for class imbalance (use stratified splits, resampling)
Common Pitfalls
❌ Not removing stop words → Noise in features
✅ Use step_stopwords() or anti_join(stop_words)
❌ Too many/few features → Overfitting or underfitting
✅ Tune max_tokens parameter (500-2000 typically)
❌ Ignoring class imbalance → Model biased to majority class
✅ Use stratified splits, step_downsample(), step_upsample(), or step_smote()
❌ Not handling negation → "not good" treated as "good"
✅ Use bigrams or custom preprocessing to capture negation
❌ Overfitting on small data → Poor generalization
✅ Use regularization (ridge/lasso), cross-validation, simpler models
❌ Not validating topic quality → Meaningless topics
✅ Inspect top terms, document assignments, coherence metrics
Supporting Resources
Comprehensive References
- text-preprocessing.md: Complete tokenization and cleaning guide
- sentiment-analysis.md: Lexicon-based sentiment with examples
- topic-modeling.md: LDA and topic interpretation
- text-classification.md: Full tidymodels text classification
Workflow Templates
- sentiment-workflow.md: Step-by-step sentiment analysis
- topic-modeling-workflow.md: Complete LDA workflow
- text-classification-workflow.md: End-to-end classification
Complete Examples
- customer-reviews-analysis.md: Full sentiment + classification example
Quick Reference
Package Loading
library(tidytext)
library(textrecipes)
library(topicmodels)
library(tidymodels)
Essential Functions
| Task | Function |
|---|
| Tokenize | unnest_tokens() |
| Remove stop words | anti_join(stop_words) |
| Sentiment | get_sentiments() + inner_join() |
| TF-IDF | bind_tf_idf() |
| DTM | cast_dtm() |
| LDA | LDA() from topicmodels |
| Text recipe | `recipe() |
Integration with Other Skills
- r-datascience: For data preparation and EDA
- r-tidymodels: For ML workflows with text
- ggplot2: For visualizing text analysis results
- r-style-guide: For code formatting
- tdd-workflow: For testing text pipelines
Remember: Text analysis is both art and science. Always validate computational results with qualitative review and domain expertise.