| Title: | Transformer Emotion Analysis for Text, Image, Video, and Speech |
|---|---|
| Description: | Zero-shot emotion classification for text, images, video and speech, and tools to find faces and code them by hand. Text is scored with 'Hugging Face' transformer pipelines (default 'cross-encoder/nli-distilroberta-base'). Images and video are scored with 'OpenAI CLIP'; faces can be limited to those found by 'OpenCV YuNet'. Speech segments are scored with 'LAION CLAP'. Frame scores can be calibrated and combined into posteriors, and detected faces or other items can be turned into a local coding task served from R. Any other zero-shot model can be selected by its name on <https://huggingface.co/models>. For details see Tomašević, Golino, and Christensen (2026) <doi:10.5117/CCR2026.2.2.TOMA>. |
| Authors: | Alexander Christensen [aut]
|
| Maintainer: | Aleksandar Tomašević <[email protected]> |
| License: | GPL (>= 3.0) |
| Version: | 0.1.9 |
| Built: | 2026-10-29 22:05:17 UTC |
| Source: | https://github.com/atomashevic/transforemotion |
Zero-shot emotion classification for text, images, video and speech, and tools to find faces and code them by hand. Text is scored with Hugging Face transformer pipelines (default cross-encoder/nli-distilroberta-base). Images and video are scored with OpenAI CLIP; faces can be limited to those found by OpenCV YuNet. Speech segments are scored with LAION CLAP. Frame scores can be calibrated and combined into posteriors, and detected faces or other items can be turned into a local coding task served from R. Any other zero-shot model can be selected by its name on Hugging Face.
Alexander P. Christensen <[email protected]>, Hudson Golino <[email protected]> and Aleksandar Tomasevic <[email protected]>
Tomašević, A., Golino, H., & Christensen, A. P. (2026). transforEmotion: An open-source R package for emotion analysis using transformer-based generative AI models. Computational Communication Research, 8(2). doi:10.5117/CCR2026.2.2.TOMA
Yin, W., Hay, J., & Roth, D. (2019). Benchmarking zero-shot text classification: Datasets, evaluation and entailment approach. arXiv preprint arXiv:1909.00161.
citation("transforEmotion")
Register the default/built-in vision models that come with transforEmotion. This function is automatically called when the package is loaded.
.init_builtin_models().init_builtin_models()
Invisibly returns TRUE
Central registry system for managing vision models in transforEmotion. Provides extensible architecture allowing users to register custom vision models beyond the default CLIP-based models.
.vision_model_registry.vision_model_registry
The registry maintains a list of available vision models with their configurations. Each model entry includes the HuggingFace model ID, architecture type, and metadata needed for proper initialization and processing.
Aleksandar Tomasevic <[email protected]>
High-level functions for managing vision models in transforEmotion, providing an easy interface for extending the package with custom models.
User-friendly wrapper for registering custom vision models with automatic validation and helpful error messages.
add_vision_model( name, model_id, description = NULL, architecture = "clip", test_labels = NULL, force = FALSE )add_vision_model( name, model_id, description = NULL, architecture = "clip", test_labels = NULL, force = FALSE )
name |
A short, memorable name for your model (e.g., "my-emotion-model") |
model_id |
HuggingFace model identifier or path to local model directory |
description |
Optional description of the model and its purpose |
architecture |
Model architecture type. Currently supported:
|
test_labels |
Optional character vector to test the model immediately |
force |
Logical indicating whether to overwrite existing model with same name |
Invisibly returns TRUE if successful
Aleksandar Tomasevic <[email protected]> Add a Custom Vision Model
## Not run: # Add a fine-tuned CLIP model for emotion recognition add_vision_model( name = "emotion-clip", model_id = "openai/clip-vit-large-patch14", description = "Large CLIP model for better emotion recognition", test_labels = c("happy", "sad", "angry"), force = TRUE ) # Add a local model add_vision_model( name = "my-local-model", model_id = "/path/to/my/model", description = "My custom fine-tuned model" ) # Add experimental BLIP model add_vision_model( name = "blip-base", model_id = "Salesforce/blip-image-captioning-base", architecture = "blip", description = "BLIP model for image captioning" ) # Now use any of them in analysis result <- image_scores("photo.jpg", c("happy", "sad"), model = "emotion-clip") batch_results <- image_scores_dir("photos/", c("positive", "negative"), model = "my-local-model") ## End(Not run)## Not run: # Add a fine-tuned CLIP model for emotion recognition add_vision_model( name = "emotion-clip", model_id = "openai/clip-vit-large-patch14", description = "Large CLIP model for better emotion recognition", test_labels = c("happy", "sad", "angry"), force = TRUE ) # Add a local model add_vision_model( name = "my-local-model", model_id = "/path/to/my/model", description = "My custom fine-tuned model" ) # Add experimental BLIP model add_vision_model( name = "blip-base", model_id = "Salesforce/blip-image-captioning-base", architecture = "blip", description = "BLIP model for image captioning" ) # Now use any of them in analysis result <- image_scores("photo.jpg", c("happy", "sad"), model = "emotion-clip") batch_results <- image_scores_dir("photos/", c("positive", "negative"), model = "my-local-model") ## End(Not run)
Starts the coding page of an annotation_task from R and
opens it in the web browser. Each answer is saved in the task's
results folder as soon as the coder gives it, so closing the
browser or R loses nothing, and a coder who comes back continues where
they stopped.
annotate_faces( task, coder = NULL, open = interactive(), port = NULL, inline = .te_in_notebook(), max_side = 640 )annotate_faces( task, coder = NULL, open = interactive(), port = NULL, inline = .te_in_notebook(), max_side = 640 )
task |
An |
coder |
The coder id to start with. Default: the page asks. |
open |
Logical. Open the page in the web browser. Default is
|
port |
Port of the local server. Default is a free port chosen at random. |
inline |
Logical. Show the coding page inside the notebook, below the
cell. Default is |
max_side |
Longest side, in pixels, of the pictures in a notebook page, which stores them in the notebook. Default 640. |
The page is served by R (with httpuv) on your own computer only
(127.0.0.1), at an address with a random part, and keeps running
while you use R. Coders press the number keys to answer, Enter for the
next item, and Backspace to undo.
Coders on other computers can open page/index.html of the task
from a copy of the folder instead. Their page keeps answers in that
browser and ends with Export annotations; put the exported files
in the task's results folder.
In a Jupyter notebook, including Google Colab, the coding page is shown
below the cell instead (inline = TRUE, the default there), because
a page served from R cannot be reached from a notebook running on another
computer. Its Copy R code button gives a line such as
answers <- read_annotations(task, "te-annotations:...") to run in
a new cell. The line holds every answer given so far, so copy it again
after more coding; keep the last one, as the answers are not saved in the
task's results folder.
Invisibly, the page: a list with its url and
stop(), which stops the server. With inline = TRUE,
invisibly, the path of the page as one self-contained file.
read_annotations, annotation_progress
## Not run: coding <- annotate_faces("annotation-round-1", coder = "C01") annotation_progress("annotation-round-1") coding$stop() ## End(Not run)## Not run: coding <- annotate_faces("annotation-round-1", coder = "C01") annotation_progress("annotation-round-1") coding$stop() ## End(Not run)
Krippendorff's alpha, Fleiss' kappa and percent agreement for each question of a face annotation task.
annotation_agreement(annotations, questions = NULL, level = NULL)annotation_agreement(annotations, questions = NULL, level = NULL)
annotations |
The result of |
questions |
Questions to compute agreement for. Default: all. |
level |
Level of measurement for alpha, as a named character vector
(for example |
Only items with answers from at least two coders count; a question that was not asked (after "Not a face", say) is missing, not an answer.
alphaKrippendorff's alpha, from the coincidence matrix. It allows for missing answers and for items coded by different numbers of coders.
fleiss_kappaFleiss' kappa, when every counted item has
answers from the same number of coders; NA otherwise. Treats
answers as categories.
percent_agreementThe share of pairs of coders who gave the same answer to an item, averaged over items.
A data frame with one row per question: question,
level, n_items, n_coders, n_answers,
percent_agreement, alpha and fleiss_kappa.
Krippendorff, K. (2011). Computing Krippendorff's alpha-reliability. https://repository.upenn.edu/items/034a6030-c584-4d14-9d3d-7b7e8d16df20
Fleiss, J. L. (1971). Measuring nominal scale agreement among many raters. Psychological Bulletin, 76(5), 378-382.
Progress of a face annotation task
annotation_progress(task)annotation_progress(task)
task |
An |
A data frame with one row per coder: items assigned, answered, skipped and left, and the median seconds per answered item.
Ready-made questions for annotation_task. Each question is a
list, so you can change them or add your own (see Details).
annotation_questions(which = c("face_check", "emotion", "intensity"))annotation_questions(which = c("face_check", "emotion", "intensity"))
which |
Names of the ready-made questions, in the order they are asked:
|
A question is a list with
idA name in lower case: letters, digits and underscores.
type"single" (one of a few options) or
"likert" (a whole number from min to max).
promptThe question as coders read it.
optionsFor "single": the answers, or
options_from = "classes" for the task's classes. Optional
labels: a named list of the text shown for each answer.
min, max
For "likert", with optional
anchors = list(min = "...", max = "...").
show_ifOptional: ask only after another question got one
of these answers, as in list(face_check = "ok").
revealOptional, for items with a reference label and a
model prediction: TRUE asks the question only once they are
shown, which with blind = TRUE is after the other questions are
answered.
requiredFALSE lets coders leave it unanswered.
A list of questions.
annotation_questions(c("face_check", "emotion")) # Add a question of your own gaze <- list(id = "gaze", type = "single", prompt = "Where does the person look?", options = c("camera", "away", "closed"), show_if = list(face_check = "ok")) questions <- c(annotation_questions(), list(gaze))annotation_questions(c("face_check", "emotion")) # Add a question of your own gaze <- list(id = "gaze", type = "single", prompt = "Where does the person look?", options = c("camera", "away", "closed"), show_if = list(face_check = "ok")) questions <- c(annotation_questions(), list(gaze))
Turns detected faces into a coding task: each face is an item that coders
see outlined in its picture, with a close-up, and answer questions about,
such as whether it is a face at all and which emotion it shows. Start the
coding page with annotate_faces, then read the answers with
read_annotations.
annotation_task( faces = NULL, classes = NULL, questions = NULL, coders = "C01", overlap = 1, dir, seed = 1, title = NULL, instructions = NULL, max_side = 1280, task_id = NULL, items = NULL, frames = 4, play = TRUE, blind = TRUE )annotation_task( faces = NULL, classes = NULL, questions = NULL, coders = "C01", overlap = 1, dir, seed = 1, title = NULL, instructions = NULL, max_side = 1280, task_id = NULL, items = NULL, frames = 4, play = TRUE, blind = TRUE )
faces |
A data frame returned by |
classes |
The emotions coders choose from, for example the classes
passed to |
questions |
The questions, from |
coders |
Coder ids: letters, digits, dots, dashes or underscores. |
overlap |
Share of the items that every coder codes, for agreement. The rest are split between the coders. Default is 1: every coder codes every item. |
dir |
A new or empty folder for the task. |
seed |
Seed for the order of the items. Each coder gets their own order. Your own random numbers are left as they were. |
title |
Title shown on the page. |
instructions |
Text shown to coders before the first item. |
max_side |
Longest side, in pixels, of the picture copies. |
task_id |
An id for the task. Coders' progress in the browser is kept under it. Default is made from the time. |
items |
Instead of |
frames |
For items: the number of frames shown per video clip, spread
over the clip as |
play |
For items: put the clips themselves in the page, with their sound, when a browser can play them (MP4, WebM, MOV). |
blind |
For items: show the label, prediction and scores only after
the questions without |
With items instead of faces, each item is a video clip, an
image or a text, for example the clips a model got wrong. Coders see
sampled frames of the clip with its face boxes (the largest face, the one
video_scores scores by default, drawn solid), the clip itself
with its sound, and the item's text. When the items have a reference
label and a model prediction, the page shows them with the
model's scores once the coder has answered (blind = TRUE), then
asks what explains the result (annotation_questions("why")). Unlike
a face task, such a page holds the texts, labels and scores.
The task is a folder:
page/The coding page: small copies of the images or video frames, under names that say nothing about the files, and the questions. It holds no file names, scores, or other coders' answers.
items.csvWhich face each item is: its file, frame, face number, track and person. For you, not for coders.
task.jsonThe questions, items and each coder's list.
results/The answers, one file per coder.
The task, invisibly: a list with its folder (dir),
task_id, questions, coders and items (the
contents of items.csv).
annotate_faces, read_annotations,
annotation_agreement, consensus_labels
## Not run: faces <- detect_faces(system.file("extdata", "two-people.mp4", package = "transforEmotion"), nframes = 20) task <- annotation_task(faces, classes = c("happy", "sad", "angry", "neutral"), coders = c("C01", "C02"), dir = "annotation-round-1") annotate_faces(task) # A model's errors on video clips, with their descriptions emotions <- c("angry", "fearful", "happy", "sad") items <- data.frame(id = errors$clip, media = errors$path, text = errors$description, label = errors$label, prediction = errors$predicted, errors[, emotions]) review <- annotation_task(items = items, classes = emotions, dir = "error-review") annotate_faces(review) answers <- read_annotations(review) table(answers$value[answers$question == "why"]) ## End(Not run)## Not run: faces <- detect_faces(system.file("extdata", "two-people.mp4", package = "transforEmotion"), nframes = 20) task <- annotation_task(faces, classes = c("happy", "sad", "angry", "neutral"), coders = c("C01", "C02"), dir = "annotation-round-1") annotate_faces(task) # A model's errors on video clips, with their descriptions emotions <- c("angry", "fearful", "happy", "sad") items <- data.frame(id = errors$clip, media = errors$path, text = errors$description, label = errors$label, prediction = errors$predicted, errors[, emotions]) review <- annotation_task(items = items, classes = emotions, dir = "error-review") annotate_faces(review) answers <- read_annotations(review) table(answers$value[answers$question == "why"]) ## End(Not run)
Produces a long-form data.frame with columns: 'label', 'confidence', 'intensity', 'doc_id', 'span', 'score'.
as_rag_table(x, validate = TRUE)as_rag_table(x, validate = TRUE)
x |
JSON string or parsed list. |
validate |
Logical; validate structure first. |
A data.frame suitable for statistical analysis.
j <- '{"labels":["joy","surprise"],"confidences":[0.8,0.5], "intensity":0.7,"evidence_chunks":[]}' as_rag_table(j)j <- '{"labels":["joy","surprise"],"confidences":[0.8,0.5], "intensity":0.7,"evidence_chunks":[]}' as_rag_table(j)
This function calculates the moving average for a time series.
calculate_moving_average(data, window_size)calculate_moving_average(data, window_size)
data |
Matrix or Data frame. The time series data |
window_size |
Numeric integer. The size of the moving average window. |
Matrix or Data frame containing the moving average values.
Learns how far to trust a model's frame scores, so that
posterior_scores can treat them as noisy evidence instead of
taking them at face value. Without labels it learns the model's average
score for each class, its lean towards some labels before it has seen a
face. With the true emotion of some videos it also learns the model's
confusions: how often it gives each top label when the true emotion is a
given one.
calibrate_scores(scores, labels = NULL, classes = NULL, alpha = NULL)calibrate_scores(scores, labels = NULL, classes = NULL, alpha = NULL)
scores |
A data frame of scores for one video, one row per frame, as
returned by |
labels |
Optional. A character vector with the true emotion of each
video, one entry per data frame in |
classes |
Character vector. The score columns. Default: every numeric
column except the columns of a faces table ( |
alpha |
Number between 0 and 1: how much one frame's evidence counts.
|
A model's scores are not probabilities to be read as they are. CLIP's scores have a lean of their own: averaged over the frames of CREMA-D clips with six bare labels, standard CLIP gives "neutral" 37% of its probability, whichever emotion the actor shows. They make the same mistakes again and again, such as reading disgust as sadness. And frames a fraction of a second apart are nearly copies of each other, so 80 frames are not 80 independent looks at a face.
The calibration turns the score vector of frame into
evidence for each emotion :
without labels, , where
is the model's mean score for (prior);
with labels, , where
is the share of frames with true emotion whose top
label is (confusion).
discounts frames that repeat each other: with , ten frames count as one independent look. Without labels it is
, where is the correlation of the
evidence between neighbouring frames, the share of independent
observations in a series with that correlation. Videos whose frames all
have the same scores are left out of the correlation; if every video is
like that, is 0.01.
Scores are renormalised over classes in every row, which is the
same as scoring with only those labels.
An object of class te_calibration, a list with
The score columns, in order.
The model's mean score of each class over all frames.
With labels: a matrix whose row c gives the share
of frames of videos with true emotion c that have each top label
(add-one smoothed). NULL without labels.
"prior" or "confusion": how
posterior_scores turns scores into evidence.
The weight of one frame's evidence.
The videos, frames with scores and labelled videos it was fitted on.
# Frame scores of nine short clips (in practice from video_scores()) set.seed(1) clip <- function(n, emotion) { s <- matrix(runif(n * 3), n, 3, dimnames = list(NULL, c("neutral", "happy", "sad"))) s[, "neutral"] <- s[, "neutral"] + 0.6 # the model leans to neutral s[, emotion] <- s[, emotion] + 0.5 as.data.frame(s / rowSums(s)) } emotions <- rep(c("neutral", "happy", "sad"), each = 3) scores <- lapply(emotions, function(e) clip(40, e)) # Without labels: correct for the model's lean calibrate_scores(scores) # With labels: also learn its confusions calibration <- calibrate_scores(scores, labels = emotions) calibration# Frame scores of nine short clips (in practice from video_scores()) set.seed(1) clip <- function(n, emotion) { s <- matrix(runif(n * 3), n, 3, dimnames = list(NULL, c("neutral", "happy", "sad"))) s[, "neutral"] <- s[, "neutral"] + 0.6 # the model leans to neutral s[, emotion] <- s[, emotion] + 0.5 as.data.frame(s / rowSums(s)) } emotions <- rep(c("neutral", "happy", "sad"), each = 3) scores <- lapply(emotions, function(e) clip(40, e)) # Without labels: correct for the model's lean calibrate_scores(scores) # With labels: also learn its confusions calibration <- calibrate_scores(scores, labels = emotions) calibration
Checks the quality and completeness of a downloaded FindingEmo dataset, reporting on file availability, image accessibility, and potential issues.
check_findingemo_quality(data_dir, check_images = FALSE, sample_size = 10)check_findingemo_quality(data_dir, check_images = FALSE, sample_size = 10)
data_dir |
Character. Directory containing the FindingEmo dataset. |
check_images |
Logical. Whether to verify image file accessibility (default: FALSE, as this can be slow). |
sample_size |
Integer. If check_images is TRUE, number of images to sample for verification (default: 10). |
A list containing:
structure: Dataset structure type ("standard" or "flat")
files_found: List of available files
annotations_count: Number of annotations
urls_count: Number of image URLs
images_count: Number of downloaded images
completeness: Percentage of images successfully downloaded
image_check: Results of image accessibility check (if performed)
## Not run: # Check dataset quality quality_report <- check_findingemo_quality("./findingemo_data") print(quality_report) # Check with image verification quality_report <- check_findingemo_quality( data_dir = "./findingemo_data", check_images = TRUE, sample_size = 5 ) ## End(Not run)## Not run: # Check dataset quality quality_report <- check_findingemo_quality("./findingemo_data") print(quality_report) # Check with image verification quality_report <- check_findingemo_quality( data_dir = "./findingemo_data", check_images = TRUE, sample_size = 5 ) ## End(Not run)
Combines the coders' answers to one question into one value per item: the
most common answer, or the mean or median of Likert answers. Use it as
the truth for evaluate_emotions.
consensus_labels( annotations, question = "emotion", method = c("majority", "mean", "median"), min_coders = 1 )consensus_labels( annotations, question = "emotion", method = c("majority", "mean", "median"), min_coders = 1 )
annotations |
The result of |
question |
The question to combine. Default is |
method |
|
min_coders |
Items with fewer answers than this get |
A data frame with one row per item: item_id, label
(the combined answer, NA for a tie or too few answers),
n_coders, agreement (the share of coders who gave the
majority answer; NA for the mean or median), tie, and the
item's face (source, frame, frame_index,
time, face, track_id, person_id), or, for
a task made from items, its id, reference,
prediction and scores: compare the coders with the reference by
evaluate_emotions(consensus, truth_col = "reference", pred_col = "label").
Large language models can be quite large and, when stored locally, can take up a lot of space on your computer. The direct paths to where the models are on your computer is not necessarily intuitive.
This function quickly identifies the models on your computer and informs you which ones can be deleted from it to open up storage space
delete_transformer(model_name, delete = FALSE)delete_transformer(model_name, delete = FALSE)
model_name |
Character vector. If no model is provided, then a list of models that are locally stored on the computer are printed |
delete |
Boolean (length = 1).
Should model skip delete question?
Defaults to |
Returns list of models or confirmed deletion
Alexander P. Christensen <[email protected]>
if(interactive()){ delete_transformer() }if(interactive()){ delete_transformer() }
Finds every face in a set of images or in frames sampled from a video, so
you can see which faces the package found, pick the ones to analyse (with
select_faces or ordinary subsetting) and score only those with
face_scores.
detect_faces( x, face_detector = c("yunet", "haar"), min_confidence = 0.6, identify = TRUE, identity_threshold = 0.363, nframes = 100, start = 0, end = -1, uniform = FALSE, ffreq = 15, save_frames = FALSE, save_video = FALSE, save_dir = tempdir(), video_name = "temp", pattern = "\\.(jpg|jpeg|png|bmp)$", recursive = FALSE )detect_faces( x, face_detector = c("yunet", "haar"), min_confidence = 0.6, identify = TRUE, identity_threshold = 0.363, nframes = 100, start = 0, end = -1, uniform = FALSE, ffreq = 15, save_frames = FALSE, save_video = FALSE, save_dir = tempdir(), video_name = "temp", pattern = "\\.(jpg|jpeg|png|bmp)$", recursive = FALSE )
x |
Image paths or URLs, a directory of images, or one video (a file or a YouTube URL). |
face_detector |
|
min_confidence |
YuNet's confidence threshold, between 0 and 1. Ignored for Haar. |
identify |
Logical. Give faces of the same person the same
|
identity_threshold |
Cosine similarity between faces (or video tracks)
above which they count as the same person. The default, 0.363, is the
threshold SFace's authors recommend; raise it if different people share
a |
nframes, start, end, uniform, ffreq
|
How frames are sampled from a video;
see |
save_frames |
Logical. Keep the extracted video frames in
|
save_video |
Logical. Keep a downloaded YouTube video in
|
save_dir |
Directory for video frames. Default is a temporary directory. |
video_name |
Name used for the frame files
( |
pattern, recursive
|
When |
Faces are numbered from left to right within each image or frame. In a
video, a face is followed from frame to frame and keeps its
track_id. With identify = TRUE, faces of the same person get
the same person_id: across all images, or across all tracks of the
video, so a person who leaves the picture and comes back, or who appears
in several images, is one person.
Face recognition compares faces through SFace embeddings (128 numbers per
face). Faces are grouped by average-linkage clustering on cosine
similarity; a video track counts as one face, the average of its frames.
Two faces in the same image can be the same person (mirrors, screens,
posters). The embeddings stay in memory and are never returned or written
to disk, and person_ids only label faces within one call: they do
not name anyone and do not match ids from other calls.
Grouping faces by person is processing of biometric data under the EU
General Data Protection Regulation (Article 9); make sure your ethics
approval covers it, or use identify = FALSE.
The YuNet model ships with the package. The SFace model
(face_recognition_sface_2021dec_int8.onnx, from OpenCV's model zoo,
Apache 2.0 licence) is downloaded into the package cache on first use, or
by setup_modules. To use a copy elsewhere, for example on a
computer without internet access, set the environment variable
TRANSFOREMOTION_SFACE_MODEL to its path. Without the model,
person_id is NA.
A data frame with one row per face and the columns:
The image path or URL, or the video.
For a video: the sample number (as in
the frame file name), the frame's index in the video and its time in
seconds. NA for images.
Face number within the image or frame, from left to right.
For a video: the face's track. NA for images.
"p1", "p2", ... numbered by first
appearance; NA without recognition.
The face box in pixels, from the top-left corner of the image.
YuNet's score; NA for Haar.
"yunet" or "haar".
Size of the image or frame in pixels.
The five YuNet landmarks
(NA for Haar).
The attribute "media" holds one row per image or frame, with the
number of faces found (n_faces), so images without faces are not
lost. The attribute "settings" records the detector and, for a
video, how frames were sampled, which face_scores uses to
find the same frames again.
All processing is done locally; images and frames are never sent to a remote server.
select_faces, face_scores,
draw_faces
## Not run: image <- system.file("extdata", "two-people.jpg", package = "transforEmotion") faces <- detect_faces(image) faces[, c("face", "person_id", "x", "y", "width", "height", "confidence")] draw_faces(faces) # Every face of one person in a video, scored once per sampled frame video <- system.file("extdata", "two-people.mp4", package = "transforEmotion") faces <- detect_faces(video, nframes = 20) table(faces$track_id, faces$person_id) scores <- face_scores(select_faces(faces, persons = "p1"), classes = c("happy", "sad", "angry", "neutral")) ## End(Not run)## Not run: image <- system.file("extdata", "two-people.jpg", package = "transforEmotion") faces <- detect_faces(image) faces[, c("face", "person_id", "x", "y", "width", "height", "confidence")] draw_faces(faces) # Every face of one person in a video, scored once per sampled frame video <- system.file("extdata", "two-people.mp4", package = "transforEmotion") faces <- detect_faces(video, nframes = 20) table(faces$track_id, faces$person_id) scores <- face_scores(select_faces(faces, persons = "p1"), classes = c("happy", "sad", "angry", "neutral")) ## End(Not run)
This function calculates the dynamics of a system using the DLO (Damped Linear Oscillator) model based on Equation 1 (Ollero et al., 2023). The DLO model is a second-order differential equation that describes the behavior of a damped harmonic oscillator. The function takes in the current state of the system, the derivative of the state, the damping coefficient, the time step, and the values of the eta and zeta parameters. It returns the updated derivative of the state.
dlo_dynamics(x, dxdt, q, dt, eta, zeta)dlo_dynamics(x, dxdt, q, dt, eta, zeta)
x |
Numeric. The current state of the system (value of the latent score). |
dxdt |
Numeric. The derivative of the state (rate of change of the latent score). |
q |
Numeric. The damping coefficient. |
dt |
Numeric. The time step. |
eta |
Numeric. The eta parameter of the DLO model. |
zeta |
Numeric. The zeta parameter of the DLO model. |
A numeric vector containing the updated derivative of the state.
Ollero, M. J. F., Estrada, E., Hunter, M. D., & Cancer, P. F. (2023). Characterizing affect dynamics with a damped linear oscillator model: Theoretical considerations and recommendations for individual-level applications. Psychological Methods. doi:10.1037/met0000615
Downloads the FindingEmo-Light dataset using the official PyPI package. This dataset contains 25k images with emotion annotations including valence, arousal, and discrete emotion labels, focusing on complex naturalistic scenes with multiple people in social settings.
download_findingemo_data( target_dir, max_images = NULL, randomize = FALSE, skip_existing = TRUE, force = FALSE )download_findingemo_data( target_dir, max_images = NULL, randomize = FALSE, skip_existing = TRUE, force = FALSE )
target_dir |
Character. Directory to download the dataset to. |
max_images |
Integer. Maximum number of images to download (optional). |
randomize |
Logical. If TRUE and max_images is specified, randomly select images for download. Useful for creating test/benchmark subsets (default: FALSE). |
skip_existing |
Logical. Whether to skip download if dataset already exists (default: TRUE). |
force |
Logical. Force download even if dataset exists (default: FALSE). |
This function uses the findingemo-light Python package, which is
installed automatically on first use (or ahead of time with
setup_modules(extras = "findingemo")).
The FindingEmo dataset is described in: Mertens, L. et al. (2024). "FindingEmo: An Image Dataset for Emotion Recognition in the Wild". NeurIPS 2024 Datasets and Benchmarks Track.
The dataset uses a flat directory structure with all images stored directly in the images/ subdirectory, annotations.csv and urls.json at the root level.
**Note**: For copyright reasons, the dataset provides URLs and annotations only. Images are downloaded on-demand from their original sources.
A list containing:
success: Logical indicating if download was successful
message: Character string with status message
target_dir: Path to downloaded data
annotation_file: Path to annotation file (if successful)
urls_file: Path to URLs file (if successful)
image_count: Number of images downloaded (if any)
annotations: Full annotations data.frame (raw)
evaluation_data: Data.frame filtered to downloaded images
with columns suitable for evaluation workflows (id, truth, image_file,
image_path, valence, arousal, emo8_label, emotion)
evaluation_csv: Path to saved CSV of evaluation_data
matched_count: Number of annotations matched to downloaded images
load_findingemo_annotations, setup_modules
## Not run: # First install required modules setup_modules(extras = "findingemo") # Download dataset to local directory result <- download_findingemo_data("./findingemo_data") if (result$success) { cat("Dataset downloaded to:", result$target_dir) cat("Images downloaded:", result$image_count) } # Download random subset for testing/benchmarking result <- download_findingemo_data( target_dir = "./findingemo_test", max_images = 100, randomize = TRUE ) # Download subset with flat directory structure (always used) result <- download_findingemo_data( target_dir = "./findingemo_subset", max_images = 50 ) # Force re-download result <- download_findingemo_data( target_dir = "./findingemo_data", force = TRUE ) ## End(Not run)## Not run: # First install required modules setup_modules(extras = "findingemo") # Download dataset to local directory result <- download_findingemo_data("./findingemo_data") if (result$success) { cat("Dataset downloaded to:", result$target_dir) cat("Images downloaded:", result$image_count) } # Download random subset for testing/benchmarking result <- download_findingemo_data( target_dir = "./findingemo_test", max_images = 100, randomize = TRUE ) # Download subset with flat directory structure (always used) result <- download_findingemo_data( target_dir = "./findingemo_subset", max_images = 50 ) # Force re-download result <- download_findingemo_data( target_dir = "./findingemo_data", force = TRUE ) ## End(Not run)
Draws the box and a label for each face on its image or video frame, so you
can check what detect_faces found: which faces were
detected, how they are numbered, and which faces were recognised as the
same person. Faces of the same person share a colour. plot() on a
detect_faces result does the same.
draw_faces(faces, labels = NULL, max_images = 20, dir = tempdir(), plot = TRUE) ## S3 method for class 'te_faces' plot(x, ...)draw_faces(faces, labels = NULL, max_images = 20, dir = tempdir(), plot = TRUE) ## S3 method for class 'te_faces' plot(x, ...)
faces |
A data frame returned by |
labels |
Text drawn above each box: one per row of |
max_images |
Most images or video frames to draw. When there are more, they are picked evenly from the first to the last. |
dir |
Directory for the drawn images (PNG files). Default is a temporary directory. |
plot |
Logical. Show the drawn images in the plot window, or below
the cell in a notebook. Default is |
x |
A data frame returned by |
... |
Passed to |
Only images and frames with a face in faces are drawn. The
"media" attribute of a detect_faces result lists every
image or frame with its number of faces, including those without any.
For a video, the frames are extracted again (as in
face_scores), unless detect_faces kept them
with save_frames = TRUE.
The paths of the drawn images, invisibly.
## Not run: image <- system.file("extdata", "two-people.jpg", package = "transforEmotion") faces <- detect_faces(image) draw_faces(faces) # A video: a few frames, with each face's track and person video <- system.file("extdata", "two-people.mp4", package = "transforEmotion") plot(detect_faces(video, nframes = 20), max_images = 4) ## End(Not run)## Not run: image <- system.file("extdata", "two-people.jpg", package = "transforEmotion") faces <- detect_faces(image) draw_faces(faces) # A video: a few frames, with each face's track and person video <- system.file("extdata", "two-people.mp4", package = "transforEmotion") plot(detect_faces(video, nframes = 20), max_images = 4) ## End(Not run)
A matrix containing words (n = 175,592) and the emotion category most frequently associated with each word. This dataset is a modified version of the 'DepecheMood++' lexicon developed by Araque, Gatti, Staiano, and Guerini (2018). For proper scoring, text should not be stemmed prior to using this lexicon. This version of the lexicon does not rely on part of speech tagging.
data(emotions)data(emotions)
A data frame with 175,592 rows and 9 columns.
An entry in the lexicon, in English
The emotional category. All emotions contain either a 0 or 1. If the category is most likely to be associated with the word, it recieves a 1, otherwise, 0. Words are only associated with one category.
Araque, O., Gatti, L., Staiano, J., and Guerini, M. (2018). DepecheMood++: A bilingual emotion lexicon built through simple yet powerful techniques. ArXiv
data("emotions")data("emotions")
A bag-of-words approach for computing emotions in text data using the lexicon compiled by Araque, Gatti, Staiano, and Guerini (2018).
emoxicon_scores(text, lexicon, exclude)emoxicon_scores(text, lexicon, exclude)
text |
Matrix or data frame. A data frame containing texts to be scored (one text per row) |
lexicon |
The lexicon used to score the words. The default is the |
exclude |
A vector listing terms that should be excluded from the lexicon.
Words specified in |
Tara Valladares <tls8vx at virginia.edu> and Hudson F. Golino <hfg9s at virginia.edu>
Araque, O., Gatti, L., Staiano, J., and Guerini, M. (2018). DepecheMood++: A bilingual emotion lexicon built through simple yet powerful techniques. ArXiv
emotions, where we describe how we modified the original DepecheMood++ lexicon.
# Obtain "emotions" data data("emotions") # Obtain "tinytrolls" data data("tinytrolls") ## Not run: # Obtain emoxicon scores for first 10 tweets emotions_tinytrolls <- emoxicon_scores(text = tinytrolls$content, lexicon = emotions) ## End(Not run)# Obtain "emotions" data data("emotions") # Obtain "tinytrolls" data data("tinytrolls") ## Not run: # Obtain emoxicon scores for first 10 tweets emotions_tinytrolls <- emoxicon_scores(text = tinytrolls$content, lexicon = emotions) ## End(Not run)
This function generates and emphasizes the effect of strong emotions expressions during the period where the derivative of the latent variable is high. The observable value of the strongest emotion from the positive or negative group will spike in the next k time steps. The probability of this happening is p at each time step in which the derivative of the latent variable is greater than 0.2. The jump is proportionate to the derivative of the latent variable and the sum of the observable values of the other emotions.
emphasize(data, num_observables, num_steps, k = 10, p = 0.5)emphasize(data, num_observables, num_steps, k = 10, p = 0.5)
data |
Data frame.
The data frame containing the latent and observable variables created by the |
num_observables |
Numeric integer. The number of observable variables per latent factor. |
num_steps |
Numeric integer. The number of time steps used in the simulation. |
k |
Numeric integer. The mumber of time steps to emphasize the effect of strong emotions on future emotions (default is 10). Alternatively: the length of a strong emotional episode. |
p |
Numeric. The probability of the strongest emotion being emphasized in the next k time steps (default is 0.5). |
A data frame containing the updated observable variables.
Comprehensive evaluation function for discrete emotion classification tasks. Computes standard classification metrics including accuracy, F1-scores, AUROC, calibration metrics, and inter-rater reliability measures.
evaluate_emotions( data, id_col = "id", truth_col = "truth", pred_col = "pred", probs_cols = NULL, classes = NULL, metrics = c("accuracy", "precision", "recall", "f1_macro", "f1_micro", "auroc", "ece", "krippendorff", "confusion_matrix"), return_plot = FALSE, na_rm = TRUE )evaluate_emotions( data, id_col = "id", truth_col = "truth", pred_col = "pred", probs_cols = NULL, classes = NULL, metrics = c("accuracy", "precision", "recall", "f1_macro", "f1_micro", "auroc", "ece", "krippendorff", "confusion_matrix"), return_plot = FALSE, na_rm = TRUE )
data |
A data frame or file path to CSV containing evaluation data. Must include columns for identifiers, ground truth, predictions, and optionally class probabilities. |
id_col |
Character. Name of column containing unique identifiers (default: "id"). |
truth_col |
Character. Name of column containing ground truth labels (default: "truth"). |
pred_col |
Character. Name of column containing predicted labels (default: "pred"). |
probs_cols |
Character vector. Names of columns containing class
probabilities. If NULL, probabilistic metrics will be skipped.
AUROC pairs |
classes |
Character vector. Emotion classes to evaluate. If NULL,
will be inferred from the data. With |
metrics |
Character vector. Metrics to compute. Options include: "accuracy", "precision", "recall", "f1_macro", "f1_micro", "auroc", "ece", "krippendorff", "confusion_matrix" (default: all metrics). |
return_plot |
Logical. Whether to return plotting helpers (default: FALSE). |
na_rm |
Logical. Whether to remove missing values (default: TRUE). |
This function implements a comprehensive evaluation pipeline for discrete emotion classification following best practices from the literature.
**Metrics computed:**
**Accuracy**: Overall classification accuracy
**Precision/Recall/F1**: Per-class and macro/micro averages
**AUROC**: Area under ROC curve (requires probability scores)
**ECE**: Expected Calibration Error for probability calibration
**Krippendorff's alpha**: Inter-rater reliability between human and model
**Input format:** The input data should contain at minimum:
ID column: Unique identifier for each instance
Truth column: Ground truth emotion labels
Prediction column: Model predicted emotion labels
Probability columns (optional): Class probabilities for each emotion
A list containing:
metrics: Data frame with computed evaluation metrics
confusion_matrix: Confusion matrix (if requested)
per_class: Per-class metrics breakdown
summary: Overall performance summary
plot_data: Data prepared for plotting (if return_plot = TRUE)
Grandini, M., Bagli, E., & Visani, G. (2020). Metrics for multi-class classification: an overview. arXiv preprint arXiv:2008.05756.
Krippendorff, K. (2011). Computing Krippendorff's alpha-reliability. Scholarly commons, 25.
Naeini, M. P., Cooper, G., & Hauskrecht, M. (2015). Obtaining well calibrated probabilities using bayesian binning. In AAAI (pp. 2901-2907).
transformer_scores, nlp_scores,
emoxicon_scores for emotion prediction functions.
## Not run: # Basic evaluation with predicted labels only results <- evaluate_emotions( data = evaluation_data, truth_col = "human_label", pred_col = "model_prediction" ) # Full evaluation with probabilities results <- evaluate_emotions( data = evaluation_data, truth_col = "ground_truth", pred_col = "predicted_class", probs_cols = c("prob_anger", "prob_joy", "prob_sadness"), return_plot = TRUE ) # Custom metrics selection results <- evaluate_emotions( data = evaluation_data, metrics = c("accuracy", "f1_macro", "confusion_matrix") ) ## End(Not run)## Not run: # Basic evaluation with predicted labels only results <- evaluate_emotions( data = evaluation_data, truth_col = "human_label", pred_col = "model_prediction" ) # Full evaluation with probabilities results <- evaluate_emotions( data = evaluation_data, truth_col = "ground_truth", pred_col = "predicted_class", probs_cols = c("prob_anger", "prob_joy", "prob_sadness"), return_plot = TRUE ) # Custom metrics selection results <- evaluate_emotions( data = evaluation_data, metrics = c("accuracy", "f1_macro", "confusion_matrix") ) ## End(Not run)
Scores each face of a detect_faces result with a CLIP model,
so you can analyse exactly the faces you picked, for example one person
across a whole video.
face_scores( faces, classes, model = "oai-base", local_model_path = NULL, padding = 0.25 )face_scores( faces, classes, model = "oai-base", local_model_path = NULL, padding = 0.25 )
faces |
A data frame returned by |
classes |
A character vector of classes to score. |
model, local_model_path
|
The vision model; see
|
padding |
Share of the face box's width and height added on each side before scoring. Default is 0.25. |
For a video, the frames are extracted again with the sampling settings
detect_faces used (and the video downloaded again from
YouTube), unless the frames were kept with save_frames = TRUE.
faces without the landmark columns, plus one column of
scores per class.
All processing is done locally with the downloaded model.
## Not run: image <- system.file("extdata", "boris-1.png", package = "transforEmotion") faces <- detect_faces(image) face_scores(faces, classes = c("happy", "sad", "angry", "neutral")) ## End(Not run)## Not run: image <- system.file("extdata", "boris-1.png", package = "transforEmotion") faces <- detect_faces(image) face_scores(faces, classes = c("happy", "sad", "angry", "neutral")) ## End(Not run)
Function to generate observable data from 2 latent variables (negative and positive affect). The function takes in the latent variable scores, the number of time steps, the number of observable variables per latent factor, and the measurement error variance. It returns a matrix of observable data. The factor loadings are not the same for all observable variables. They have uniform random noise added to them (between -0.15 and 0.15). The loadings are scaled so that the sum of the loadings for each latent factor is 2, to introduce a ceiling effect and to differentiate the dynamics of specific emotions. This is further empahsized by adding small noise to the measurement error variance for each observed variable (between -0.01 and 0.01).
generate_observables(X, num_steps, num_obs, error, loadings = 0.8)generate_observables(X, num_steps, num_obs, error, loadings = 0.8)
X |
Matrix or Data frame. The (num_steps X 2) matrix of latent variable scores. |
num_steps |
Numeric integer. Number of time steps. |
num_obs |
Numeric integer. The number of observable variables per latent factor. |
error |
Numeric. Measurement error variance. |
loadings |
Numeric (default = 0.8). The default initial loading of the latent variable on the observable variable. |
A (num_steps X num_obs) Matrix or Data frame containing the observable variables.
This function generates a matrix of Dynamic Error values (q) for the DLO simulation.
generate_q(num_steps, sigma_q)generate_q(num_steps, sigma_q)
num_steps |
Numeric integer. The number of time steps used in the simulation. |
sigma_q |
Numeric. Standard deviation of the Dynamic Error/ |
A (num_steps X 3) matrix of Dynamic Error values for neutral, negative and positive emotion latent score.
Retrieve the configuration for a specific vision model from the registry.
get_vision_model_config(name)get_vision_model_config(name)
name |
The name/alias of the registered model |
A list with model configuration, or NULL if model not found
This function takes an image file and a vector of classes as input and calculates the scores for each class using a specified Hugging Face CLIP model. Primary use of the function is to calculate FER scores - Facial Expression Detection of emotions based on detected facial expression in images. In case there are more than one face in the image, the function will return the scores of the face selected using the face_selection parameter. If there is no face in the image, the function will return NA for all classes. Function uses reticulate to call the Python functions in the image.py file. If you run this package/function for the first time it will take some time for the package to setup a functioning Python virtual environment in the background. This includes installing Python libraries for facial recognition and emotion detection in text, images and video. Please be patient.
image_scores( image, classes, face_selection = "largest", model = "oai-base", local_model_path = NULL, face_detector = c("yunet", "haar") )image_scores( image, classes, face_selection = "largest", model = "oai-base", local_model_path = NULL, face_detector = c("yunet", "haar") )
image |
The path to the image file or URL of the image. |
classes |
A character vector of classes to classify the image into. |
face_selection |
The face(s) to score. Can be "largest", "left", "right",
"none", "all", face numbers, or a data frame of faces. Default is "largest"
and will select the largest face in the image. "left" and "right" will select
the face on the far left or the far right side of the image. "none" will use
the whole image without cropping. These four return one row, as before.
"all" scores every face, face numbers (such as |
model |
A string specifying the vision model to use. Options include:
Use |
local_model_path |
Optional. Path to a local directory containing a pre-downloaded HuggingFace model. If provided, the model will be loaded from this directory instead of being downloaded from HuggingFace. This is useful for offline usage or for using custom fine-tuned models. On Linux/Mac, look in ~/.cache/huggingface/hub/ folder for downloaded models. Navigate to the snapshots folder for the relevant model and point to the directory which contains the config.json file. For example: "/home/username/.cache/huggingface/hub/models–cross-encoder–nli-distilroberta-base/snapshots/b5b020e8117e1ddc6a0c7ed0fd22c0e679edf0fa/" On Windows, the base path is C:\Users\USERNAME\.cache\huggingface\transformers\ Warning: Using very large models from local paths may cause memory issues or crashes depending on your system's resources. |
face_detector |
|
Data Privacy: All processing is done locally with the downloaded model, and your images are never sent to any remote server or third-party.
A data frame containing the scores for each class.
Aleksandar Tomasevic <[email protected]>
This function scans a directory for image files and computes scores for each image using a Hugging Face CLIP model. It loads the model once and reuses text embeddings for speed, returning one row per image with the filename as image_id and probability columns for each class.
image_scores_dir( dir, classes, face_selection = "largest", pattern = "\\.(jpg|jpeg|png|bmp)$", recursive = FALSE, model = "oai-base", local_model_path = NULL, face_detector = c("yunet", "haar") )image_scores_dir( dir, classes, face_selection = "largest", pattern = "\\.(jpg|jpeg|png|bmp)$", recursive = FALSE, model = "oai-base", local_model_path = NULL, face_detector = c("yunet", "haar") )
dir |
Path to a directory containing images. |
classes |
Character vector of labels/classes (length >= 2). |
face_selection |
Face selection strategy: "largest", "left", "right", or
"none" (one row per image), "all" (one row per face, with faces of the same
person sharing a |
pattern |
Optional regex to filter images (default supports common formats). |
recursive |
Whether to search subdirectories (default FALSE). |
model |
CLIP model alias or HuggingFace model id (see image_scores()). |
local_model_path |
Optional local path to a pre-downloaded model. |
face_detector |
|
A data.frame with columns: image_id and one column per class. With
"all" or a data frame of faces, one row per face, with the columns of
face_scores after image_id.
Check if a vision model is available in the registry.
is_vision_model_registered(name)is_vision_model_registered(name)
name |
The name/alias of the model to check |
Logical indicating if the model is registered
List all vision models currently available in the transforEmotion registry.
list_vision_models( include_builtin = TRUE, architecture_filter = NULL, verbose = FALSE )list_vision_models( include_builtin = TRUE, architecture_filter = NULL, verbose = FALSE )
include_builtin |
Logical indicating whether to include built-in models (default: TRUE) |
architecture_filter |
Optional character vector to filter by architecture type |
verbose |
Logical indicating whether to show detailed information (default: FALSE) |
A data.frame with model information, or detailed list if verbose=TRUE
# List all available models list_vision_models() # List only CLIP models list_vision_models(architecture_filter = "clip") # Get detailed information list_vision_models(verbose = TRUE) # See what models are available for image analysis models <- list_vision_models() print(paste("Available models:", paste(models$name, collapse = ", ")))# List all available models list_vision_models() # List only CLIP models list_vision_models(architecture_filter = "clip") # Get detailed information list_vision_models(verbose = TRUE) # See what models are available for image analysis models <- list_vision_models() print(paste("Available models:", paste(models$name, collapse = ", ")))
Loads and preprocesses annotations from a downloaded FindingEmo-Light dataset. Returns a clean R data.frame with emotion annotations, valence/arousal scores, and associated metadata.
load_findingemo_annotations( data_dir, output_format = c("dataframe", "list"), python_path = NULL )load_findingemo_annotations( data_dir, output_format = c("dataframe", "list"), python_path = NULL )
data_dir |
Character. Directory containing the downloaded FindingEmo data. |
output_format |
Character. Format for processed data: "dataframe" returns R data.frame, "list" returns full processed data (default: "dataframe"). |
python_path |
Character. Path to Python executable (optional). |
This function loads the CSV annotation file and JSON URLs file from a downloaded FindingEmo dataset, performs basic validation and preprocessing, and returns the data in a format suitable for emotion analysis.
The function handles missing values, validates valence/arousal ranges, and provides summary statistics for the loaded data.
If output_format = "dataframe": A data.frame with columns:
image_id: Unique image identifier
valence: Valence score (emotion positivity)
arousal: Arousal score (emotion intensity)
Additional columns as present in the dataset
If output_format = "list": A list containing:
annotations: Data.frame with annotation data
urls: List with image URL information
metadata: List with dataset metadata
download_findingemo_data, prepare_findingemo_evaluation
## Not run: # Download dataset first download_result <- download_findingemo_data("./findingemo_data") if (download_result$success) { # Load annotations as data.frame annotations <- load_findingemo_annotations("./findingemo_data") # Examine the data head(annotations) summary(annotations) # Get full processed data including metadata full_data <- load_findingemo_annotations( data_dir = "./findingemo_data", output_format = "list" ) print(full_data$metadata) } ## End(Not run)## Not run: # Download dataset first download_result <- download_findingemo_data("./findingemo_data") if (download_result$success) { # Load annotations as data.frame annotations <- load_findingemo_annotations("./findingemo_data") # Examine the data head(annotations) summary(annotations) # Get full processed data including metadata full_data <- load_findingemo_annotations( data_dir = "./findingemo_data", output_format = "list" ) print(full_data$metadata) } ## End(Not run)
Maps discrete emotion classifications from image_scores(), transformer_scores(), or video_scores() functions to the Valence-Arousal-Dominance (VAD) framework using published lexicons. Automatically downloads the NRC VAD lexicon via textdata package on first use.
map_discrete_to_vad( results, mapping = "nrc_vad", weighted = TRUE, cache_lexicon = TRUE, vad_lexicon = NULL )map_discrete_to_vad( results, mapping = "nrc_vad", weighted = TRUE, cache_lexicon = TRUE, vad_lexicon = NULL )
results |
Output from image_scores(), transformer_scores(), or video_scores(). Can be a data.frame (from image/video functions) or a list (from transformer functions). |
mapping |
Character. Which VAD mapping to use. Currently supports:
|
weighted |
Logical. Whether to compute weighted averages based on confidence scores (default: TRUE). If FALSE, performs simple lookup of the highest-scoring emotion. |
cache_lexicon |
Logical. Whether to cache the VAD lexicon for repeated use (default: TRUE). |
vad_lexicon |
Optional data.frame. Pre-loaded VAD lexicon data to use instead of loading from textdata. Must have columns for word, valence, arousal, dominance (accepts both lowercase and capitalized versions, e.g., Word/word, Valence/valence). If provided, the function will use this data directly. |
This function bridges discrete emotion classification outputs with the continuous VAD emotion framework. The VAD model represents emotions in a three-dimensional space where:
**Valence**: Pleasantness (positive/negative)
**Arousal**: Activation level (excited/calm)
**Dominance**: Control (dominant/submissive)
**Input Type Detection:** The function automatically detects the input type:
**data.frame**: Assumes output from image_scores() or video_scores()
**list**: Assumes output from transformer_scores()
**Weighting Methods:**
**weighted = TRUE**: Computes weighted average VAD scores based on classification confidence scores
**weighted = FALSE**: Uses VAD values for the highest-scoring emotion only
**VAD Mappings:** Currently supports the NRC VAD lexicon which provides VAD ratings for emotion words based on crowdsourced annotations. The lexicon must be downloaded first using 'textdata::lexicon_nrc_vad()' in an interactive session.
**Setup Required:** Before using this function, download the NRC VAD lexicon by running: 'textdata::lexicon_nrc_vad()' in an interactive R session and accepting the license.
A data.frame with columns:
valence: Valence score (positive vs negative emotion)
arousal: Arousal score (excitement vs calmness)
dominance: Dominance score (control vs submissiveness)
For transformer_scores() input, includes additional identifier columns. For image/video_scores() input, returns one row per input row.
VAD lexicon is downloaded once and cached locally. No data is sent to external servers.
Aleksandar Tomasevic <[email protected]>
Mohammad, S. M. (2018). Obtaining reliable human ratings of valence, arousal, and dominance for 20,000 English words. Proceedings of the 56th Annual Meeting of the Association for Computational Linguistics (Volume 1: Long Papers), 174-184.
## Not run: # Method 1: Auto-load from textdata (requires prior download) textdata::lexicon_nrc_vad() # Run once to download # With image scores image_path <- system.file("extdata", "boris-1.png", package = "transforEmotion") emotions <- c("joy", "sadness", "anger", "fear", "surprise", "disgust") img_results <- image_scores(image_path, emotions) vad_results <- map_discrete_to_vad(img_results) # Method 2: Download once and pass as argument (recommended) nrc_vad <- textdata::lexicon_nrc_vad() # Download once # Use with different emotion results vad_results1 <- map_discrete_to_vad(img_results, vad_lexicon = nrc_vad) text <- "I am so happy today!" trans_results <- transformer_scores(text, emotions) vad_results2 <- map_discrete_to_vad(trans_results, vad_lexicon = nrc_vad) # Simple lookup (no weighting) vad_simple <- map_discrete_to_vad(img_results, weighted = FALSE, vad_lexicon = nrc_vad) ## End(Not run)## Not run: # Method 1: Auto-load from textdata (requires prior download) textdata::lexicon_nrc_vad() # Run once to download # With image scores image_path <- system.file("extdata", "boris-1.png", package = "transforEmotion") emotions <- c("joy", "sadness", "anger", "fear", "surprise", "disgust") img_results <- image_scores(image_path, emotions) vad_results <- map_discrete_to_vad(img_results) # Method 2: Download once and pass as argument (recommended) nrc_vad <- textdata::lexicon_nrc_vad() # Download once # Use with different emotion results vad_results1 <- map_discrete_to_vad(img_results, vad_lexicon = nrc_vad) text <- "I am so happy today!" trans_results <- transformer_scores(text, emotions) vad_results2 <- map_discrete_to_vad(trans_results, vad_lexicon = nrc_vad) # Simple lookup (no weighting) vad_simple <- map_discrete_to_vad(img_results, weighted = FALSE, vad_lexicon = nrc_vad) ## End(Not run)
Maps FindingEmo dataset emotion labels to the standard 8 basic emotions (Emo8) from Plutchik's emotion wheel. This function converts complex emotion labels to the 8 fundamental emotions using intensity-based mappings from the circumplex model.
map_to_emo8(findingemo_emotions)map_to_emo8(findingemo_emotions)
findingemo_emotions |
Character vector of FindingEmo emotion labels to map. |
The mapping is based on Plutchik's circumplex model of emotions, where complex emotions are mapped to their corresponding basic emotions:
**Basic Emotions (direct mapping):** - Joy, Trust, Fear, Surprise, Sadness, Disgust, Anger, Anticipation
**Intensity Variations:** - High intensity: Ecstasy→Joy, Admiration→Trust, Terror→Fear, etc. - Low intensity: Serenity→Joy, Acceptance→Trust, Apprehension→Fear, etc.
The 8 basic emotions (Emo8) are: joy, trust, fear, surprise, sadness, disgust, anger, anticipation.
Character vector of mapped Emo8 emotion labels. Unmapped emotions return NA.
## Not run: # Map single emotions map_to_emo8("Joy") # "joy" map_to_emo8("Ecstasy") # "joy" map_to_emo8("Serenity") # "joy" # Map multiple emotions findingemo_labels <- c("Joy", "Rage", "Terror", "Interest") emo8_labels <- map_to_emo8(findingemo_labels) # Returns: c("joy", "anger", "fear", "anticipation") # Use in evaluation pipeline annotations <- load_findingemo_annotations("./data") annotations$emo8_label <- map_to_emo8(annotations$emotion) ## End(Not run)## Not run: # Map single emotions map_to_emo8("Joy") # "joy" map_to_emo8("Ecstasy") # "joy" map_to_emo8("Serenity") # "joy" # Map multiple emotions findingemo_labels <- c("Joy", "Rage", "Terror", "Interest") emo8_labels <- map_to_emo8(findingemo_labels) # Returns: c("joy", "anger", "fear", "anticipation") # Use in evaluation pipeline annotations <- load_findingemo_annotations("./data") annotations$emo8_label <- map_to_emo8(annotations$emotion) ## End(Not run)
This function generates a random sample from the multivariate normal distribution with mean mu and covariance matrix Sigma.
MASS_mvrnorm(n = 1, mu, Sigma, tol = 1e-06, empirical = FALSE, EISPACK = FALSE)MASS_mvrnorm(n = 1, mu, Sigma, tol = 1e-06, empirical = FALSE, EISPACK = FALSE)
n |
Numeric integer. The number of observations to generate. |
mu |
Numeric vector. The mean vector of the multivariate normal distribution. |
Sigma |
Numeric matrix. The covariance matrix of the multivariate normal distribution. |
tol |
Numeric. Tolerance for checking the positive definiteness of the covariance matrix. |
empirical |
Logical. Whether to return the empirical covariance matrix. |
EISPACK |
Logical. Whether to use the EISPACK routine instead of the LINPACK routine. |
A (n X p) matrix of random observations from the multivariate normal distribution. Updated: 26.10.2023.
A list (length = 6) of the NEO-PI-R IPIP item descriptions (https://ipip.ori.org/newNEOFacetsKey.htm). Each vector within the 6 list elements contains the item descriptions for the respective Extraversion facets – friendliness, gregariousness, assertiveness, activity_level, excitement_seeking, and cheerfulness
data(neo_ipip_extraversion)data(neo_ipip_extraversion)
A list (length = 6)
data("neo_ipip_extraversion")data("neo_ipip_extraversion")
Natural Language Processing using word embeddings to compute
semantic similarities (cosine; see
costring) of text and specified classes.
The suggested package LSAfun is loaded on use. It is not attached
with transforEmotion, because loading it also loads rgl, which
needs the system OpenGL and GLU libraries.
nlp_scores( text, classes, semantic_space = c("baroni", "cbow", "cbow_ukwac", "en100", "glove", "tasa"), preprocess = TRUE, remove_stop = TRUE, keep_in_env = TRUE, envir = 1 )nlp_scores( text, classes, semantic_space = c("baroni", "cbow", "cbow_ukwac", "en100", "glove", "tasa"), preprocess = TRUE, remove_stop = TRUE, keep_in_env = TRUE, envir = 1 )
text |
Character vector or list. Text in a vector or list data format |
classes |
Character vector. Classes to score the text |
semantic_space |
Character vector. The semantic space used to compute the distances between words (more than one allowed). Here's a list of the semantic spaces:
|
preprocess |
Boolean.
Should basic preprocessing be applied?
Includes making lowercase, keeping only alphanumeric characters,
removing escape characters, removing repeated characters,
and removing white space.
Defaults to |
remove_stop |
Boolean.
Should |
keep_in_env |
Boolean.
Whether the classifier should be kept in your global environment.
Defaults to |
envir |
Numeric. Environment for the classifier to be saved for repeated use. Defaults to the global environment |
Returns semantic distances for the text classes
Alexander P. Christensen <[email protected]>
Baroni, M., Dinu, G., & Kruszewski, G. (2014). Don't count, predict! a systematic comparison of context-counting vs. context-predicting semantic vectors. In Proceedings of the 52nd annual meting of the association for computational linguistics (pp. 238-247).
Landauer, T.K., & Dumais, S.T. (1997). A solution to Plato's problem: The Latent Semantic Analysis theory of acquisition, induction and representation of knowledge. Psychological Review, 104, 211-240.
Pennington, J., Socher, R., & Manning, C. D. (2014). GloVe: Global vectors for word representation. In Proceedings of the 2014 conference on empirical methods in natural language processing (pp. 1532-1543).
# Load data data(neo_ipip_extraversion) # Example text text <- neo_ipip_extraversion$friendliness[1:5] ## Not run: # GloVe nlp_scores( text = text, classes = c( "friendly", "gregarious", "assertive", "active", "excitement", "cheerful" ) ) # Baroni nlp_scores( text = text, classes = c( "friendly", "gregarious", "assertive", "active", "excitement", "cheerful" ), semantic_space = "baroni" ) # CBOW nlp_scores( text = text, classes = c( "friendly", "gregarious", "assertive", "active", "excitement", "cheerful" ), semantic_space = "cbow" ) # CBOW + ukWaC nlp_scores( text = text, classes = c( "friendly", "gregarious", "assertive", "active", "excitement", "cheerful" ), semantic_space = "cbow_ukwac" ) # en100 nlp_scores( text = text, classes = c( "friendly", "gregarious", "assertive", "active", "excitement", "cheerful" ), semantic_space = "en100" ) # tasa nlp_scores( text = text, classes = c( "friendly", "gregarious", "assertive", "active", "excitement", "cheerful" ), semantic_space = "tasa" ) ## End(Not run)# Load data data(neo_ipip_extraversion) # Example text text <- neo_ipip_extraversion$friendliness[1:5] ## Not run: # GloVe nlp_scores( text = text, classes = c( "friendly", "gregarious", "assertive", "active", "excitement", "cheerful" ) ) # Baroni nlp_scores( text = text, classes = c( "friendly", "gregarious", "assertive", "active", "excitement", "cheerful" ), semantic_space = "baroni" ) # CBOW nlp_scores( text = text, classes = c( "friendly", "gregarious", "assertive", "active", "excitement", "cheerful" ), semantic_space = "cbow" ) # CBOW + ukWaC nlp_scores( text = text, classes = c( "friendly", "gregarious", "assertive", "active", "excitement", "cheerful" ), semantic_space = "cbow_ukwac" ) # en100 nlp_scores( text = text, classes = c( "friendly", "gregarious", "assertive", "active", "excitement", "cheerful" ), semantic_space = "en100" ) # tasa nlp_scores( text = text, classes = c( "friendly", "gregarious", "assertive", "active", "excitement", "cheerful" ), semantic_space = "tasa" ) ## End(Not run)
Parses a JSON string (or list) matching the enforced RAG schema and returns a normalized list: 'list(labels=chr, confidences=num, intensity=num, evidence=data.frame(doc_id, span, score))'.
parse_rag_json(x, validate = TRUE)parse_rag_json(x, validate = TRUE)
x |
JSON string or list. |
validate |
Logical; validate structure after parse. |
A normalized list with atomic vectors and an 'evidence' data.frame.
j <- '{"labels":["joy"],"confidences":[0.9], "intensity":0.8,"evidence_chunks":[]}' parse_rag_json(j)j <- '{"labels":["joy"],"confidences":[0.9], "intensity":0.8,"evidence_chunks":[]}' parse_rag_json(j)
Function to plot the latent or the observable emotion scores.
plot_sim_emotions(df, mode = "latent", title = " ")plot_sim_emotions(df, mode = "latent", title = " ")
df |
Data frame.
The data frame containing the latent and observable variables created by the |
mode |
Character. The mode of the plot. Can be either 'latent', 'positive' or 'negative'. |
title |
Character. The title of the plot. Default is an empty title, ' '. |
A plot of the latent or the observable emotion scores.
Creates visualizations for emotion evaluation results including confusion matrix heatmaps and per-class metrics bar plots.
## S3 method for class 'emotion_evaluation' plot(x, type = "both", ...)## S3 method for class 'emotion_evaluation' plot(x, type = "both", ...)
x |
An emotion_evaluation object from evaluate_emotions() |
type |
Character. Type of plot: "confusion_matrix", "metrics", or "both" |
... |
Additional arguments passed to plotting functions |
A ggplot object or list of ggplot objects
Combines each frame's scores with what the earlier (and, with
method = "smooth", the later) frames showed, and returns the
probability of each emotion in every frame and for the whole video. The
scores are treated as noisy evidence, as learned by
calibrate_scores, and the emotion is assumed to stay the
same from one frame to the next with probability stay.
posterior_scores( scores, calibration, stay = 0.99, method = c("filter", "smooth") )posterior_scores( scores, calibration, stay = 0.99, method = c("filter", "smooth") )
scores |
A data frame of scores for one video, one row per frame, or
a list of them, one per video (see |
calibration |
The result of |
stay |
Probability that the emotion is the same in the next row (default 0.99); otherwise it changes to one of the other emotions, each equally likely. It is per row, so lower it when rows are far apart in time: 0.99 between frames 1/30 s apart means an emotion lasts about 3 s on average. Use 1 for one emotion per video. |
method |
|
The posterior of emotion in frame is
where is the frame's evidence (see calibrate_scores)
and stay. The first frame starts from equal
probabilities. This is a hidden Markov model over the emotions, with the
model's scores as noisy observations.
A list with
scores (a data frame, or a list of them) in time
order, with the class columns holding the posterior probabilities and a
label column with the most probable emotion (replacing any
label column already there). No class may be called
label, video or n_frames.
A data frame with one row per video: video (the name
in the list, or its number for a video without a name; frames
is named the same way), n_frames, the posterior of each
class and label. The clip posterior is the posterior at the last
frame, which has seen every frame; it is the same for both methods. With
stay near 1 it is the posterior of one emotion for the whole
clip. For long videos in which the emotion changes, summarise
frames instead, for example with colMeans().
calibrate_scores, video_scores
set.seed(1) clip <- function(n, emotion) { s <- matrix(runif(n * 3), n, 3, dimnames = list(NULL, c("neutral", "happy", "sad"))) s[, "neutral"] <- s[, "neutral"] + 0.6 s[, emotion] <- s[, emotion] + 0.5 as.data.frame(s / rowSums(s)) } scores <- list(a = clip(40, "happy"), b = clip(40, "sad"), c = clip(40, "neutral"), d = clip(40, "sad")) calibration <- calibrate_scores(scores) result <- posterior_scores(scores, calibration) result$clips head(result$frames$a) # Each frame using the whole clip smoothed <- posterior_scores(scores$a, calibration, method = "smooth") table(smoothed$frames$label)set.seed(1) clip <- function(n, emotion) { s <- matrix(runif(n * 3), n, 3, dimnames = list(NULL, c("neutral", "happy", "sad"))) s[, "neutral"] <- s[, "neutral"] + 0.6 s[, emotion] <- s[, emotion] + 0.5 as.data.frame(s / rowSums(s)) } scores <- list(a = clip(40, "happy"), b = clip(40, "sad"), c = clip(40, "neutral"), d = clip(40, "sad")) calibration <- calibrate_scores(scores) result <- posterior_scores(scores, calibration) result$clips head(result$frames$a) # Each frame using the whole clip smoothed <- posterior_scores(scores$a, calibration, method = "smooth") table(smoothed$frames$label)
Prepares FindingEmo dataset annotations for use with evaluate_emotions().
Converts the dataset format to match the expected input structure for
evaluation functions.
prepare_findingemo_evaluation( annotations, predictions, id_col = "image_id", truth_col = "emotion_label", pred_col = "predicted_emotion", include_va = TRUE )prepare_findingemo_evaluation( annotations, predictions, id_col = "image_id", truth_col = "emotion_label", pred_col = "predicted_emotion", include_va = TRUE )
annotations |
Data.frame. Annotations from |
predictions |
Data.frame. Model predictions with same image IDs as annotations. |
id_col |
Character. Column name for image IDs (default: "image_id"). |
truth_col |
Character. Column name for ground truth emotions (default: "emotion_label"). |
pred_col |
Character. Column name for predicted emotions (default: "predicted_emotion"). |
include_va |
Logical. Whether to include valence/arousal columns (default: TRUE). |
This function merges FindingEmo annotations with model predictions and formats
the result for evaluation. It handles missing values, validates data consistency,
and ensures the output matches the expected format for evaluate_emotions().
A data.frame formatted for use with evaluate_emotions(), containing:
id: Image identifiers
truth: Ground truth emotion labels
pred: Predicted emotion labels
valence: Valence scores (if available and include_va = TRUE)
arousal: Arousal scores (if available and include_va = TRUE)
Additional probability columns if present in predictions
load_findingemo_annotations, evaluate_emotions
## Not run: # Load annotations annotations <- load_findingemo_annotations("./findingemo_data") # Create mock predictions (replace with actual model predictions) predictions <- data.frame( image_id = annotations$image_id[1:100], predicted_emotion = sample(c("happy", "sad", "angry"), 100, replace = TRUE), prob_happy = runif(100), prob_sad = runif(100), prob_angry = runif(100) ) # Prepare for evaluation eval_data <- prepare_findingemo_evaluation( annotations = annotations, predictions = predictions ) # Evaluate model performance results <- evaluate_emotions( data = eval_data, probs_cols = c("prob_happy", "prob_sad", "prob_angry") ) print(results) ## End(Not run)## Not run: # Load annotations annotations <- load_findingemo_annotations("./findingemo_data") # Create mock predictions (replace with actual model predictions) predictions <- data.frame( image_id = annotations$image_id[1:100], predicted_emotion = sample(c("happy", "sad", "angry"), 100, replace = TRUE), prob_happy = runif(100), prob_sad = runif(100), prob_angry = runif(100) ) # Prepare for evaluation eval_data <- prepare_findingemo_evaluation( annotations = annotations, predictions = predictions ) # Evaluate model performance results <- evaluate_emotions( data = eval_data, probs_cols = c("prob_happy", "prob_sad", "prob_angry") ) print(results) ## End(Not run)
Print method for emotion evaluation results
## S3 method for class 'emotion_evaluation' print(x, ...)## S3 method for class 'emotion_evaluation' print(x, ...)
x |
An emotion_evaluation object |
... |
Additional arguments (unused) |
Keeps the punctuations you want and removes the punctuations you don't
punctuate( text, allowPunctuations = c("-", "?", "'", "\"", ";", ",", ".", "!") )punctuate( text, allowPunctuations = c("-", "?", "'", "\"", ";", ",", ".", "!") )
text |
Character vector or list. Text in a vector or list data format |
allowPunctuations |
Character vector. Punctuations that should be allowed in the text. Defaults to common punctuations in English text |
Coarsely removes punctuations from text. Keeps general punctuations that are used in most English language text. Apostrophes are much trickier. For example, not allowing "'" will remove apostrophes from contractions like "can't" becoming "cant"
Returns text with only the allowed punctuations
Alexander P. Christensen <[email protected]>
# Load data data(neo_ipip_extraversion) # Example text text <- neo_ipip_extraversion$friendliness # Keep only periods punctuate(text, allowPunctuations = c("."))# Load data data(neo_ipip_extraversion) # Example text text <- neo_ipip_extraversion$friendliness # Keep only periods punctuate(text, allowPunctuations = c("."))
Returns the Python requirements that transforEmotion installs, in
requirements.txt format. Use it to build a fixed Python environment,
for example in a container image, and point transforEmotion at it with the
TRANSFOREMOTION_PYTHON environment variable (see Details).
python_requirements( extras = character(), gpu = FALSE, sysname = Sys.info()[["sysname"]], machine = Sys.info()[["machine"]] )python_requirements( extras = character(), gpu = FALSE, sysname = Sys.info()[["sysname"]], machine = Sys.info()[["machine"]] )
extras |
Character vector of optional feature sets to include; see
|
gpu |
Logical. Use the CUDA build of PyTorch (Linux and Windows) instead of the CPU build. |
sysname, machine
|
Target operating system and architecture, as
returned by |
The PyTorch requirements are direct wheel URLs built for Python 3.12, so the environment must use Python 3.12.
When TRANSFOREMOTION_PYTHON is set to a Python executable,
transforEmotion uses that interpreter and does not install anything. This is
the route for read-only environments such as Apptainer images, where uv
cannot write to its cache.
A function that needs an optional feature set (extras), for example
rag(), stops when the fixed environment does not have the Python
modules of that set. The error names the missing modules, a command that
installs their packages, and the EXTRAS build argument of
docker/Dockerfile. Use setup_modules with the same
extras to compare the whole environment with the requirements.
A fixed environment does not change when transforEmotion is updated, and a
new version can add Python packages. After an update, rebuild the
environment or the image from python_requirements(), or install the
new packages into it. For a read-only image that you cannot rebuild,
install a missing package into a writable folder on a machine with network
access, for example
uv pip install --python <python> --target <folder> "protobuf>=3.20,<8",
and start R with the environment variable PYTHONPATH set to that
folder.
A character vector of requirement specifiers.
# Requirements for a CPU environment with the rag() packages python_requirements(extras = "rag") ## Not run: # Write a requirements file and build the environment with uv (shell): # uv venv --python 3.12 /opt/te-venv # uv pip install --python /opt/te-venv/bin/python -r requirements.txt writeLines(python_requirements(extras = "rag"), "requirements.txt") ## End(Not run)# Requirements for a CPU environment with the rag() packages python_requirements(extras = "rag") ## Not run: # Write a requirements file and build the environment with uv (shell): # uv venv --python 3.12 /opt/te-venv # uv pip install --python /opt/te-venv/bin/python -r requirements.txt writeLines(python_requirements(extras = "rag"), "requirements.txt") ## End(Not run)
Performs retrieval-augmented generation {llama-index}
Supports multiple local LLM backends via HuggingFace and llama-index.
rag( text = NULL, path = NULL, transformer = c("TinyLLAMA", "Gemma3-1B", "Gemma3-4B", "Qwen2.5-1.5B", "Qwen2.5-3B", "Ministral-3B"), prompt = "You are an expert at extracting themes across many texts", query, response_mode = c("accumulate", "compact", "no_text", "refine", "simple_summarize", "tree_summarize"), similarity_top_k = 5, retriever = c("vector", "bm25"), retriever_params = list(), output = c("text", "json", "table", "csv"), task = c("general", "emotion", "sentiment"), labels_set = NULL, max_labels = 5, global_analysis = FALSE, device = c("auto", "cpu", "cuda"), temperature = NULL, do_sample = NULL, max_new_tokens = NULL, top_p = NULL, keep_in_env = TRUE, envir = 1, progress = TRUE )rag( text = NULL, path = NULL, transformer = c("TinyLLAMA", "Gemma3-1B", "Gemma3-4B", "Qwen2.5-1.5B", "Qwen2.5-3B", "Ministral-3B"), prompt = "You are an expert at extracting themes across many texts", query, response_mode = c("accumulate", "compact", "no_text", "refine", "simple_summarize", "tree_summarize"), similarity_top_k = 5, retriever = c("vector", "bm25"), retriever_params = list(), output = c("text", "json", "table", "csv"), task = c("general", "emotion", "sentiment"), labels_set = NULL, max_labels = 5, global_analysis = FALSE, device = c("auto", "cpu", "cuda"), temperature = NULL, do_sample = NULL, max_new_tokens = NULL, top_p = NULL, keep_in_env = TRUE, envir = 1, progress = TRUE )
text |
Character vector or list.
Text in a vector or list data format.
|
path |
Character.
Path to .pdfs stored locally on your computer.
Defaults to |
transformer |
Character. Large language model to use for RAG. Available models include:
|
prompt |
Character (length = 1).
Prompt to feed into TinyLLAMA.
Defaults to |
query |
Character.
The query you'd like to know from the documents.
Defaults to |
response_mode |
Character (length = 1).
Different responses generated from the model.
See documentation
here.
Defaults to |
similarity_top_k |
Numeric (length = 1).
Retrieves most representative texts given the Values will vary based on number of texts but some suggested values might be:
These values depend on the number and quality of texts. Adjust as necessary |
retriever |
Character (length = 1).
Retrieval backend: one of |
retriever_params |
List.
Optional parameters passed to the selected retriever handler. Reserved keys
include |
output |
Character (length = 1).
Output format: one of
|
task |
Character (length = 1).
Task hint for structured extraction: one of |
labels_set |
Character vector.
Allowed labels for classification when |
max_labels |
Integer (length = 1).
Maximum number of labels to return in structured outputs;
used to guide the model instruction when
|
global_analysis |
Boolean (length = 1).
Whether to perform analysis across all documents globally
(legacy behavior) or per-document (default).
When |
device |
Character.
Whether to use CPU or GPU for inference.
Defaults to |
temperature |
Numeric or NULL. Overrides the LLM sampling temperature when using local HF models. Recommended: 0.0–0.2 for structured/classification; 0.3–0.7 for summaries. |
do_sample |
Logical or NULL.
If |
max_new_tokens |
Integer or NULL. Maximum new tokens to generate. Suggested: 64–128 for label decisions; 256–512 for summaries. |
top_p |
Numeric or NULL.
Nucleus sampling parameter. Typical: 0.7–0.95. Use with |
keep_in_env |
Boolean (length = 1).
Whether the classifier should be kept in your global environment.
Defaults to |
envir |
Numeric (length = 1). Environment for the classifier to be saved for repeated use. Defaults to the global environment |
progress |
Boolean (length = 1).
Whether progress should be displayed.
Defaults to |
For output = "text", returns an object of class
"rag" with fields:
$response (character), $content (data.frame),
and $document_embeddings (matrix).
For output = "json", returns a JSON character(1)
string matching the enforced schema.
For output = "table", returns a data.frame
suitable for statistical analysis.
All processing is done locally with the downloaded model, and your text is never sent to any remote server or third-party.
Alexander P. Christensen <[email protected]>
# Load data data(neo_ipip_extraversion) # Example text text <- neo_ipip_extraversion$friendliness[1:5] ## Not run: rag( text = text, query = "What themes are prevalent across the text?", response_mode = "tree_summarize", similarity_top_k = 5 ) # Structured outputs (Gemma 3 or Qwen 2.5) rag(text = text, query = "Extract emotions", output = "json", transformer = "Qwen2.5-3B") rag(text = text, query = "Extract emotions", output = "table", transformer = "Qwen2.5-3B") ## End(Not run)# Load data data(neo_ipip_extraversion) # Example text text <- neo_ipip_extraversion$friendliness[1:5] ## Not run: rag( text = text, query = "What themes are prevalent across the text?", response_mode = "tree_summarize", similarity_top_k = 5 ) # Structured outputs (Gemma 3 or Qwen 2.5) rag(text = text, query = "Extract emotions", output = "json", transformer = "Qwen2.5-3B") rag(text = text, query = "Extract emotions", output = "table", transformer = "Qwen2.5-3B") ## End(Not run)
Helpers for validating, parsing, and flattening structured output from 'rag(output = "json"|"table")'.
Convenience wrapper around rag() that keeps vector retrieval but
simplifies getting structured outputs for emotion or sentiment analysis
using small local LLMs (1–4B) with sensible defaults.
rag_sentemo( text = NULL, path = NULL, task = c("emotion", "sentiment"), labels_set = NULL, max_labels = 5, transformer = c("TinyLLAMA", "Gemma3-1B", "Gemma3-4B", "Qwen2.5-1.5B", "Qwen2.5-3B", "Ministral-3B"), similarity_top_k = 5, response_mode = c("compact", "refine", "simple_summarize"), output = c("table", "json", "csv"), global_analysis = FALSE, ... )rag_sentemo( text = NULL, path = NULL, task = c("emotion", "sentiment"), labels_set = NULL, max_labels = 5, transformer = c("TinyLLAMA", "Gemma3-1B", "Gemma3-4B", "Qwen2.5-1.5B", "Qwen2.5-3B", "Ministral-3B"), similarity_top_k = 5, response_mode = c("compact", "refine", "simple_summarize"), output = c("table", "json", "csv"), global_analysis = FALSE, ... )
text |
Character vector or list. Text to analyze. One entry per document. |
path |
Character. Optional directory with files to index (e.g., PDFs).
If provided, overrides |
task |
Character. One of |
labels_set |
Character vector of allowed labels.
If |
max_labels |
Integer. Max number of labels to return. |
transformer |
Character. Small local LLM to use. One of:
Gemma 3 needs transformers >= 4.50, which the Python environment that
transforEmotion installs does not have: it caps transformers below 4.47
for its llama-index 0.10 stack. Gemma 3 therefore works only with a
fixed environment that has transformers >= 4.50, set with
|
similarity_top_k |
Integer. Retrieval depth per query. Default 5. |
response_mode |
Character. LlamaIndex response mode.
Default |
output |
Character. |
global_analysis |
Logical. If TRUE, analyze all documents jointly. Default FALSE. |
... |
Additional arguments passed to |
For Gemma 3 and Qwen 2.5 and output = "table"/"csv",
a data.frame with columnsdoc_id, text, label, confidence.
For Gemma 3 and Qwen 2.5 and output = "json", a JSON array
of per-doc objects with those fields.
For other models, structured outputs are not supported;
the function falls back to output = "text" and
returns a free-text "rag" object.
## Not run: texts <- c( "I feel so happy and grateful today!", "This is frustrating and makes me angry." ) rag_sentemo(texts, task = "emotion", output = "table") rag_sentemo(texts, task = "sentiment", output = "json") rag_sentemo(texts, task = "emotion", output = "table", transformer = "Qwen2.5-3B") ## End(Not run)## Not run: texts <- c( "I feel so happy and grateful today!", "This is frustrating and makes me angry." ) rag_sentemo(texts, task = "emotion", output = "table") rag_sentemo(texts, task = "sentiment", output = "json") rag_sentemo(texts, task = "emotion", output = "table", transformer = "Qwen2.5-3B") ## End(Not run)
Reads what coders answered in annotate_faces: the files in
the task's results folder, and any exported files you add.
read_annotations(task, files = NULL, revisions = c("last", "all"))read_annotations(task, files = NULL, revisions = c("last", "all"))
task |
An |
files |
Answer files to read, or |
revisions |
|
A data frame with one row per item, coder and question:
item_id, coder_id, question, value (the
answer as text, NA when the question was not asked or the item
was skipped), value_num (the answer as a number, for Likert
questions), status ("answered" or "skipped"),
revision, rt_ms (time on the item), shown_at,
submitted_at and session_id, followed by the item's face:
source, frame, frame_index, time,
face, track_id and person_id. For a task made from
items: the item's id, kind, media,
reference (its label), prediction and score
columns.
annotation_agreement, consensus_labels
Reads the file that Export selection in
view_faces(select = TRUE) downloads, or the code in
the line that Copy R code gives in a notebook, and returns the
chosen faces.
read_face_selection(file, faces)read_face_selection(file, faces)
file |
Path of the exported |
faces |
The same data frame that was passed to
|
The chosen rows of faces, which face_scores
scores and face_selection in image_scores,
image_scores_dir and video_scores accepts.
## Not run: faces <- detect_faces(system.file("extdata", "two-people.jpg", package = "transforEmotion")) view_faces(faces, select = TRUE) chosen <- read_face_selection("~/Downloads/selection-faces-20261011-101500.json", faces) ## End(Not run)## Not run: faces <- detect_faces(system.file("extdata", "two-people.jpg", package = "transforEmotion")) view_faces(faces, select = TRUE) chosen <- read_face_selection("~/Downloads/selection-faces-20261011-101500.json", faces) ## End(Not run)
Registers a retriever under a name. The handler should construct and return a query engine compatible with llama-index or a fallback with a 'query_fn'.
register_retriever(name, handler)register_retriever(name, handler)
name |
Character scalar; retriever name (e.g., "my_retriever"). |
handler |
Function with signature: function(llama_index, documents, similarity_top_k, response_mode, params) -> engine_or_list where the return value is either a Python query engine with '$query()' or a list with element 'query_fn' taking a single 'query' argument and returning a list with 'response' and 'source_nodes'. Note: Settings are configured globally via llama_index.core.Settings. |
Register a new vision model in the transforEmotion registry, making it available for use with image_scores(), video_scores(), and related functions.
register_vision_model( name, model_id, architecture = "clip", description = NULL, preprocessing_config = NULL, requires_special_handling = FALSE )register_vision_model( name, model_id, architecture = "clip", description = NULL, preprocessing_config = NULL, requires_special_handling = FALSE )
name |
A short name/alias for the model (e.g., "my-custom-clip") |
model_id |
The HuggingFace model identifier or path to local model |
architecture |
The model architecture type. Currently supported:
|
description |
Optional description of the model |
preprocessing_config |
Optional list of preprocessing parameters |
requires_special_handling |
Logical indicating if the model needs custom processing beyond standard CLIP pipeline |
Invisibly returns TRUE if registration successful
## Not run: # Register a custom CLIP model register_vision_model( name = "my-emotion-clip", model_id = "j-hartmann/emotion-english-distilroberta-base", architecture = "clip", description = "Custom CLIP fine-tuned on emotion datasets" ) # Register a local model register_vision_model( name = "local-clip", model_id = "/path/to/local/model", architecture = "clip", description = "Locally stored fine-tuned model" ) # Register experimental BLIP model register_vision_model( name = "blip-caption", model_id = "Salesforce/blip-image-captioning-base", architecture = "blip", description = "BLIP model for image captioning" ) ## End(Not run)## Not run: # Register a custom CLIP model register_vision_model( name = "my-emotion-clip", model_id = "j-hartmann/emotion-english-distilroberta-base", architecture = "clip", description = "Custom CLIP fine-tuned on emotion datasets" ) # Register a local model register_vision_model( name = "local-clip", model_id = "/path/to/local/model", architecture = "clip", description = "Locally stored fine-tuned model" ) # Register experimental BLIP model register_vision_model( name = "blip-caption", model_id = "Salesforce/blip-image-captioning-base", architecture = "blip", description = "BLIP model for image captioning" ) ## End(Not run)
Remove a custom vision model from the registry. Built-in models cannot be removed.
Remove a vision model from the transforEmotion registry.
remove_vision_model(name, confirm = TRUE) remove_vision_model(name, confirm = TRUE)remove_vision_model(name, confirm = TRUE) remove_vision_model(name, confirm = TRUE)
name |
The name/alias of the model to remove |
confirm |
Logical indicating whether to show confirmation prompt (default: TRUE) |
Invisibly returns TRUE if successful
Invisibly returns TRUE if removal successful
## Not run: # Remove a custom model remove_vision_model("my-custom-model") # Remove without confirmation prompt remove_vision_model("my-custom-model", confirm = FALSE) ## End(Not run)## Not run: # Remove a custom model remove_vision_model("my-custom-model") # Remove without confirmation prompt remove_vision_model("my-custom-model", confirm = FALSE) ## End(Not run)
Keeps the faces of a detect_faces result that a rule, face
numbers, tracks or people pick. The result can be passed to
face_scores, or as face_selection to
image_scores or video_scores. Ordinary
subsetting (for example faces[faces$person_id == "p1", ]) works as
well.
select_faces( faces, rule = c("all", "largest", "left", "right"), ids = NULL, tracks = NULL, persons = NULL )select_faces( faces, rule = c("all", "largest", "left", "right"), ids = NULL, tracks = NULL, persons = NULL )
faces |
A data frame returned by |
rule |
|
ids |
Face numbers to keep (the |
tracks |
Video track ids to keep (the |
persons |
Person ids to keep (the |
The selected rows of faces, with its attributes.
faces <- data.frame( source = "group.jpg", frame = NA, face = 1:3, person_id = c("p1", "p2", "p3"), x = c(10, 200, 400), y = 50, width = c(80, 120, 60), height = c(80, 120, 60) ) select_faces(faces, rule = "largest") select_faces(faces, persons = c("p1", "p3"))faces <- data.frame( source = "group.jpg", frame = NA, face = 1:3, person_id = c("p1", "p2", "p3"), x = c(10, 200, 400), y = 50, width = c(80, 120, 60), height = c(80, 120, 60) ) select_faces(faces, rule = "largest") select_faces(faces, persons = c("p1", "p3"))
Uses sentence similarity models from huggingface
to compute embeddings of text and comparison_text
and returns the cosine similarity of each pair
sentence_similarity( text, comparison_text, transformer = c("all_minilm_l6"), device = c("auto", "cpu", "cuda"), preprocess = FALSE, keep_in_env = TRUE, envir = 1 )sentence_similarity( text, comparison_text, transformer = c("all_minilm_l6"), device = c("auto", "cpu", "cuda"), preprocess = FALSE, keep_in_env = TRUE, envir = 1 )
text |
Character vector or list. Text in a vector or list data format |
comparison_text |
Character vector or list. Text in a vector or list data format |
transformer |
Character.
Specific sentence similarity transformer
to be used.
Defaults to Also allows any sentence similarity models with a pipeline
from huggingface
to be used by using the specified name (e.g., |
device |
Character.
Whether to use CPU or GPU for inference.
Defaults to |
preprocess |
Boolean.
Should basic preprocessing be applied?
Includes making lowercase, keeping only alphanumeric characters,
removing escape characters, removing repeated characters,
and removing white space.
Defaults to |
keep_in_env |
Boolean.
Whether the classifier should be kept in your global environment.
Defaults to |
envir |
Numeric. Environment for the classifier to be saved for repeated use. Defaults to the global environment |
Returns a n x m similarity matrix where n is length of text and m is the length of comparison_text
Alexander P. Christensen <[email protected]>
# Load data data(neo_ipip_extraversion) # Example text text <- neo_ipip_extraversion$friendliness[1:5] ## Not run: # Example with defaults sentence_similarity( text = text, comparison_text = text ) # Example with model from 'sentence-transformers' sentence_similarity( text = text, comparison_text = text, transformer = "sentence-transformers/all-mpnet-base-v2" ) ## End(Not run)# Load data data(neo_ipip_extraversion) # Example text text <- neo_ipip_extraversion$friendliness[1:5] ## Not run: # Example with defaults sentence_similarity( text = text, comparison_text = text ) # Example with model from 'sentence-transformers' sentence_similarity( text = text, comparison_text = text, transformer = "sentence-transformers/all-mpnet-base-v2" ) ## End(Not run)
Stores the Python environment, Python itself and the Hugging Face models in one folder, and optionally runs without network access. This is the route for HPC clusters: prepare the folder on a login node with internet access, then use it offline on compute nodes.
setup_cache(path, offline = FALSE, gpu = NULL)setup_cache(path, offline = FALSE, gpu = NULL)
path |
Folder for the caches, for example on project or scratch storage shared by login and compute nodes. It needs several GB and must be writable. |
offline |
Logical. |
gpu |
Logical or |
Call setup_cache() in a new R session, after
library(transforEmotion) and before any analysis. It sets these
environment variables for the session:
UV_CACHE_DIRuv package cache and Python environments
UV_PYTHON_INSTALL_DIR, UV_PYTHON_PREFERENCE
a Python interpreter kept in the cache, so compute nodes do not need one
HF_HOMEHugging Face models
HF_HUB_CACHE, SENTENCE_TRANSFORMERS_HOME
the model
folder in HF_HOME, <path>/huggingface/hub. These variables
take precedence over HF_HOME, so setup_cache() sets them
too, for example over the values that the Docker image sets.
R_USER_CACHE_DIRreticulate's own copy of uv and the NLTK
data used by rag(). This also moves the cache of any other R
package that uses tools::R_user_dir() in this session.
It also unsets TRANSFORMERS_CACHE, PYTORCH_TRANSFORMERS_CACHE
and PYTORCH_PRETRAINED_BERT_CACHE. transformers reads these older
variables before HF_HUB_CACHE, and it warns in every session when one
of them is set.
Offline sessions can use any combination of the feature sets prepared
with setup_modules(), and any model it downloaded.
After you update transforEmotion, repeat the preparation once with network
access: setup_cache() with the same path and gpu value
but without offline = TRUE, then the same setup_modules()
call. A new version can change the Python requirements, and an offline
session cannot install them. Before Python starts, the first analysis
function of an offline session checks with uv that the cache has every
declared Python package. If one is missing, the function stops with uv's
reason, for example that a package "was not found in the cache", and the
setup_cache() and setup_modules() calls to run.
The cache must stay writable, because uv locks it even when reusing an
environment. For read-only environments such as Apptainer images, build a
fixed Python environment instead (see python_requirements).
Invisibly, a named character vector of the environment variables set.
## Not run: # On a login node, with internet access library(transforEmotion) setup_cache("/project/mylab/transforEmotion-cache", gpu = TRUE) setup_modules(extras = "rag", models = "facebook/bart-large-mnli") # In a job on a compute node, without internet access library(transforEmotion) setup_cache("/project/mylab/transforEmotion-cache", offline = TRUE, gpu = TRUE) transformer_scores(text, classes, transformer = "facebook-bart") ## End(Not run)## Not run: # On a login node, with internet access library(transforEmotion) setup_cache("/project/mylab/transforEmotion-cache", gpu = TRUE) setup_modules(extras = "rag", models = "facebook/bart-large-mnli") # In a job on a compute node, without internet access library(transforEmotion) setup_cache("/project/mylab/transforEmotion-cache", offline = TRUE, gpu = TRUE) transformer_scores(text, classes, transformer = "facebook-bart") ## End(Not run)
Adds bitsandbytes, used for 4-bit quantization of the EVA-CLIP-8B
vision model, to the Python environment. Equivalent to
setup_modules(extras = "gpu", download_models = FALSE).
setup_gpu_modules()setup_gpu_modules()
The CUDA build of PyTorch does not need this function: it is selected
automatically when an NVIDIA GPU is detected (see setup_modules).
image_scores() and video_scores() add bitsandbytes
themselves when EVA-CLIP-8B is used on a GPU.
This function requires NVIDIA GPU and drivers to be properly installed.
Alexander P. Christensen <[email protected]>
setup_miniconda() is deprecated. The transforEmotion package now uses reticulate's uv-based ephemeral environments managed via 'py_require()'. No conda or Miniconda installation is required.
setup_miniconda()setup_miniconda()
Builds the Python environment for transforEmotion ahead of first use and downloads the default models, so the first analysis call does not wait on either. Calling it is optional: every function sets up what it needs on first use.
setup_modules( extras = character(), download_models = TRUE, models = character() )setup_modules( extras = character(), download_models = TRUE, models = character() )
extras |
Character vector of optional feature sets to install together with the core packages:
|
download_models |
Logical. Download the default text, image and
sentence-similarity models into the Hugging Face cache (about 1 GB), the
face recognition model |
models |
Character vector of additional Hugging Face model IDs to
download, such as |
Python requirements are declared with py_require.
reticulate resolves them with uv into a cached environment, so after the
first run later sessions start in under a second. No conda installation or
manual uv installation is needed; reticulate downloads uv when it is not
on the PATH.
PyTorch is installed as a CPU-only build unless an NVIDIA GPU is detected.
Set TE_FORCE_CPU=1 to always use the CPU build, or
TRANSFOREMOTION_USE_GPU=1 to always use the CUDA build. The choice is
made when the package is loaded, so set either variable in
.Renviron or before library(transforEmotion), in a new R
session.
When TRANSFOREMOTION_PYTHON is set, setup_modules() installs
nothing. It compares the packages in that environment with the
requirements of the core set and of extras
(see python_requirements), and gives a warning that names
each package that is missing or has a version outside its requirement,
with a command that installs them.
Invisibly returns NULL.
Alexander P. Christensen <[email protected]>
## Not run: # Core packages and default models setup_modules() # Also install the packages rag() needs setup_modules(extras = "rag") # Prepare an additional model for offline use setup_modules(models = "facebook/bart-large-mnli") ## End(Not run)## Not run: # Core packages and default models setup_modules() # Also install the packages rag() needs setup_modules(extras = "rag") # Prepare an additional model for offline use setup_modules(models = "facebook/bart-large-mnli") ## End(Not run)
Convenience function to quickly add popular vision models with pre-configured settings.
setup_popular_models(models)setup_popular_models(models)
models |
Character vector of model shortcuts to add. Available options:
|
Invisibly returns TRUE if all models added successfully
## Not run: # Add BLIP models for image captioning setup_popular_models("blip-base") # Add multiple experimental models at once setup_popular_models(c("blip-base", "blip-large", "align-base")) # Then use them in your analysis list_vision_models() # See all available models result <- image_scores("image.jpg", c("happy", "sad"), model = "blip-base") ## End(Not run)## Not run: # Add BLIP models for image captioning setup_popular_models("blip-base") # Add multiple experimental models at once setup_popular_models(c("blip-base", "blip-large", "align-base")) # Then use them in your analysis list_vision_models() # See all available models result <- image_scores("image.jpg", c("happy", "sad"), model = "blip-base") ## End(Not run)
Display all available vision models in a user-friendly format with additional details and usage hints.
show_vision_models(show_details = FALSE, filter_by = NULL)show_vision_models(show_details = FALSE, filter_by = NULL)
show_details |
Logical indicating whether to show detailed information |
filter_by |
Optional character vector to filter by architecture type |
Invisibly returns the models data.frame
# Show all models show_vision_models() # Show only CLIP models show_vision_models(filter_by = "clip") # Show detailed information show_vision_models(show_details = TRUE)# Show all models show_vision_models() # Show only CLIP models show_vision_models(filter_by = "clip") # Show detailed information show_vision_models(show_details = TRUE)
This function simulates emotions in a video using the DLO model implemented as continuous time state space model. The function takes in several parameters, including the time step, number of steps, number of observables, and various model parameters. It returns a data frame containing the simulated emotions and their derivatives, as well as smoothed versions of the observables. The initial state of the video is always the same. Neutral score is 0.5 and both positive and negative emotion score is 0.25. To simulate more realistic time series, there is an option of including a sudden jump in the emotion scores. This is done by emphasizing the effect of the dominant emotion during the period where the derivative of the latent variable is high. The observable value of the strongest emotion from the positive or negative group will spike in the next k time step (emph.dur). The probability of this happening is p at each time step in which the derivative of the latent variable is greater than 0.2. The jump is proportionate to the derivative of the latent variable and the sum of the observable values of the other emotions.
simulate_video( dt, num_steps, num_observables, eta_n, zeta_n, eta, zeta, sigma_q, sd_observable, loadings, window_size, emph = FALSE, emph.dur = 10, emph.prob = 0.5 )simulate_video( dt, num_steps, num_observables, eta_n, zeta_n, eta, zeta, sigma_q, sd_observable, loadings, window_size, emph = FALSE, emph.dur = 10, emph.prob = 0.5 )
dt |
Numeric real. The time step for the simulation (in minutes). |
num_steps |
Numeric real. Total length of the video (in minutes). |
num_observables |
Numeric integer. The number of observables to generate per factor. Total number of observables generated is 2 x num_observables. |
eta_n |
Numeric. The eta parameter for the neutral state. |
zeta_n |
Numeric. The zeta parameter for the neutral state. |
eta |
Numeric. The eta parameter for the positive and negative emotions. |
zeta |
Numeric. The zeta parameter for the positive and negative emotions. |
sigma_q |
Numeric. The standard deviation of Dynamic Error of the q(t) function. |
sd_observable |
Numeric. The standard deviation of the measurement error. |
loadings |
Numeric (default = 0.8). The default initial loading of the latent variable on the observable variable. |
window_size |
Numeric integer. The window size for smoothing the observables. |
emph |
Logical. Whether to emphasize the effect of dominant emotion (default is FALSE). |
emph.dur |
Numeric integer. The duration of the emphasis (default is 10). |
emph.prob |
Numeric. The probability of the dominant emotion being emphasized (default is 0.5). |
A data frame (num_steps X (6 + num_observables)) containing the latent scores for neutral score, positive emotions, negative emotions and their derivatives, as well as smoothed versions of the observables.
simulate_video(dt = 0.01, num_steps = 50, num_observables = 4, eta_n = 0.5, zeta_n = 0.5, eta = 0.5, zeta = 0.5, sigma_q = 0.1, sd_observable = 0.1, loadings = 0.8, window_size = 10)simulate_video(dt = 0.01, num_steps = 50, num_observables = 4, eta_n = 0.5, zeta_n = 0.5, eta = 0.5, zeta = 0.5, sigma_q = 0.1, sd_observable = 0.1, loadings = 0.8, window_size = 10)
Splits the audio track of an audio file, a video file or a YouTube video
into segments and scores each segment against user-defined classes with a
CLAP audio-text model (zero-shot). Each segment's scores are the softmax over
the classes of the similarity between the segment's audio embedding and each
class's text embedding, as image_scores does with CLIP.
speech_scores( audio, classes, nsegments = 100, segment_length = 5, start = 0, end = -1, uniform = FALSE, model = "laion-clap", local_model_path = NULL )speech_scores( audio, classes, nsegments = 100, segment_length = 5, start = 0, end = -1, uniform = FALSE, model = "laion-clap", local_model_path = NULL )
audio |
Path to an audio or video file (any format FFmpeg reads, such as WAV, MP3, FLAC, OGG, M4A or MP4), or the URL of a YouTube video (only its audio is downloaded, to a temporary file that is removed afterwards). |
classes |
A character vector of at least two classes. CLAP compares
the audio with each class as text, so short descriptions of the voice
(for example |
nsegments |
The maximum number of segments to score. Default is 100. |
segment_length |
Segment length in seconds, at most 10 (CLAP reads 10 seconds of audio). Default is 5. |
start |
The start time, in seconds, of the audio range to analyze. Default is 0. |
end |
The end time, in seconds, of the audio range to analyze. Default is -1: the end of the audio. |
uniform |
Logical. |
model |
The CLAP model: |
local_model_path |
Optional. Path to a local directory that holds a
downloaded CLAP model (the folder with |
The audio is converted to mono at the model's sampling rate (48 kHz).
With uniform = FALSE, a last segment shorter than one second is not
scored, unless it is the only segment. A range no longer than
segment_length is scored as one segment. Segments shorter than 10
seconds are repeated to fill CLAP's 10-second input.
General-purpose CLAP models separate speech emotions much less well than
CLIP separates facial expressions. On RAVDESS (1440 acted English clips,
eight emotions), "laion-clap" had an unweighted average recall of
0.13 with one-word classes such as "angry" and 0.17 with classes
such as "a person speaking angrily", where chance is 0.125. Check
the scores against labelled examples from your own data before you use
them.
A data frame with one row per segment: segment (1, 2, ...),
start and end (the segment's times in the file, in seconds)
and one column of scores per class. The scores of a row sum to 1.
All processing is done locally with the downloaded model, and your audio is never sent to any remote server or third-party.
Aleksandar Tomasevic <[email protected]>
video_scores for the frames of the same video.
## Not run: speech_scores("interview.wav", classes = c("a person speaking angrily", "a person speaking happily", "a person speaking sadly", "a person speaking calmly"), nsegments = 20, segment_length = 5) ## End(Not run)## Not run: speech_scores("interview.wav", classes = c("a person speaking angrily", "a person speaking happily", "a person speaking sadly", "a person speaking calmly"), nsegments = 20, segment_length = 5) ## End(Not run)
174 English stop words in the tm package
data(stop_words)data(stop_words)
A vector (length = 174)
data("stop_words")data("stop_words")
Summary method for emotion evaluation results
## S3 method for class 'emotion_evaluation' summary(object, ...)## S3 method for class 'emotion_evaluation' summary(object, ...)
object |
An emotion_evaluation object |
... |
Additional arguments (unused) |
Removes the default 'reticulate' virtual environment at '~/.virtualenvs/r-reticulate' to avoid conflicts with uv-managed environments used by transforEmotion. This is optional and only needed if you want to ensure uv's environment is preferred.
te_cleanup_default_venv(confirm = TRUE)te_cleanup_default_venv(confirm = TRUE)
confirm |
Logical. Ask for confirmation before removal. Default TRUE. |
Invisibly returns TRUE on success, FALSE otherwise.
## Not run: te_cleanup_default_venv() te_cleanup_default_venv(confirm = FALSE) ## End(Not run)## Not run: te_cleanup_default_venv() te_cleanup_default_venv(confirm = FALSE) ## End(Not run)
A matrix containing a smaller subset of tweets from the trolls dataset, useful for test purposes.
There are approximately 20,000 tweets from 50 authors.
This dataset includes only authored tweets by each account; retweets, reposts, and repeated tweets have been removed.
The original data was provided by FiveThirtyEight and Clemson University researchers Darren Linvill and Patrick Warren.
For more information, visit https://github.com/fivethirtyeight/russian-troll-tweets
data(tinytrolls)data(tinytrolls)
A data frame with 22,143 rows and 6 columns.
A tweet.
The name of the handle that authored the tweet.
The date the tweet was published on.
How many followers the handle had at the time of posting.
How many interactions (including likes, tweets, retweets) the post garnered.
Left or Right
data(tinytrolls)data(tinytrolls)
Uses sentiment analysis pipelines from huggingface to compute probabilities that the text corresponds to the specified classes
transformer_scores( text, classes, multiple_classes = FALSE, transformer = c("cross-encoder-roberta", "cross-encoder-distilroberta", "facebook-bart"), device = c("auto", "cpu", "cuda"), preprocess = FALSE, keep_in_env = TRUE, envir = 1, local_model_path = NULL )transformer_scores( text, classes, multiple_classes = FALSE, transformer = c("cross-encoder-roberta", "cross-encoder-distilroberta", "facebook-bart"), device = c("auto", "cpu", "cuda"), preprocess = FALSE, keep_in_env = TRUE, envir = 1, local_model_path = NULL )
text |
Character vector or list. Text in a vector or list data format |
classes |
Character vector. Classes to score the text |
multiple_classes |
Boolean.
Whether the text can belong to multiple true classes.
Defaults to |
transformer |
Character. Specific zero-shot sentiment analysis transformer to be used. Default options:
Defaults to Also allows any other model on the Hugging Face Hub with the
zero-shot-classification
pipeline tag. Give its full model ID, The three default models are trained on English text. For text in other
languages, use a multilingual model, for example
Note: Using custom HuggingFace model IDs beyond the recommended models is done at your own risk. Large models may cause memory issues or crashes, especially on systems with limited resources. The package has been optimized and tested with the recommended models listed above. |
device |
Character.
Whether to use CPU or GPU for inference.
Defaults to |
preprocess |
Boolean.
Should basic preprocessing be applied?
Includes making lowercase, keeping only alphanumeric characters,
removing escape characters, removing repeated characters,
and removing white space.
Defaults to |
keep_in_env |
Boolean.
Whether the classifier should be kept in your global environment.
Defaults to |
envir |
Numeric. Environment for the classifier to be saved for repeated use. Defaults to the global environment |
local_model_path |
Optional. Path to a local directory containing a pre-downloaded HuggingFace model. If provided, the model will be loaded from this directory instead of being downloaded from HuggingFace. This is useful for offline usage or for using custom fine-tuned models. On Linux/Mac, look in ~/.cache/huggingface/hub/ folder for downloaded models. Navigate to the snapshots folder for the relevant model and point to the directory which contains the config.json file. For example: "/home/username/.cache/huggingface/hub/models–cross-encoder–nli-distilroberta-base/snapshots/b5b020e8117e1ddc6a0c7ed0fd22c0e679edf0fa/" On Windows, the base path is C:\Users\USERNAME\.cache\huggingface\transformers\ Warning: Using very large models from local paths may cause memory issues or crashes depending on your system's resources. |
Returns probabilities for the text classes
All processing is done locally with the downloaded model, and your text is never sent to any remote server or third-party.
Alexander P. Christensen <[email protected]>
# BART
Lewis, M., Liu, Y., Goyal, N., Ghazvininejad, M., Mohamed, A., Levy, O., ... & Zettlemoyer, L. (2019).
Bart: Denoising sequence-to-sequence pre-training for natural language generation, translation, and comprehension.
arXiv preprint arXiv:1910.13461.
# RoBERTa
Liu, Y., Ott, M., Goyal, N., Du, J., Joshi, M., Chen, D., ... & Stoyanov, V. (2019).
Roberta: A robustly optimized bert pretraining approach.
arXiv preprint arXiv:1907.11692.
# Zero-shot classification
Yin, W., Hay, J., & Roth, D. (2019).
Benchmarking zero-shot text classification: Datasets, evaluation and entailment approach.
arXiv preprint arXiv:1909.00161.
# MultiNLI dataset
Williams, A., Nangia, N., & Bowman, S. R. (2017).
A broad-coverage challenge corpus for sentence understanding through inference.
arXiv preprint arXiv:1704.05426.
# Load data data(neo_ipip_extraversion) # Example text text <- neo_ipip_extraversion$friendliness[1:5] ## Not run: # Cross-Encoder DistilRoBERTa transformer_scores( text = text, classes = c( "friendly", "gregarious", "assertive", "active", "excitement", "cheerful" ) ) # Facebook BART Large transformer_scores( text = text, classes = c( "friendly", "gregarious", "assertive", "active", "excitement", "cheerful" ), transformer = "facebook-bart" ) # Directly from huggingface: typeform/distilbert-base-uncased-mnli transformer_scores( text = text, classes = c( "friendly", "gregarious", "assertive", "active", "excitement", "cheerful" ), transformer = "typeform/distilbert-base-uncased-mnli" ) # Text in another language: a multilingual model (about 2.2 GB) transformer_scores( text = "Estoy muy contento de verte hoy", classes = c("alegría", "ira", "miedo"), transformer = "joeddav/xlm-roberta-large-xnli" ) ## End(Not run)# Load data data(neo_ipip_extraversion) # Example text text <- neo_ipip_extraversion$friendliness[1:5] ## Not run: # Cross-Encoder DistilRoBERTa transformer_scores( text = text, classes = c( "friendly", "gregarious", "assertive", "active", "excitement", "cheerful" ) ) # Facebook BART Large transformer_scores( text = text, classes = c( "friendly", "gregarious", "assertive", "active", "excitement", "cheerful" ), transformer = "facebook-bart" ) # Directly from huggingface: typeform/distilbert-base-uncased-mnli transformer_scores( text = text, classes = c( "friendly", "gregarious", "assertive", "active", "excitement", "cheerful" ), transformer = "typeform/distilbert-base-uncased-mnli" ) # Text in another language: a multilingual model (about 2.2 GB) transformer_scores( text = "Estoy muy contento de verte hoy", classes = c("alegría", "ira", "miedo"), transformer = "joeddav/xlm-roberta-large-xnli" ) ## End(Not run)
Directly predicts VAD dimensions using classification with definitional labels, bypassing the intermediate step of discrete emotion classification. This approach uses rich, educational descriptions of each VAD pole to help transformer models understand the psychological concepts and make more accurate predictions.
vad_scores( input, input_type = "auto", dimensions = c("valence", "arousal", "dominance"), label_type = "definitional", custom_labels = NULL, model = "auto", ... )vad_scores( input, input_type = "auto", dimensions = c("valence", "arousal", "dominance"), label_type = "definitional", custom_labels = NULL, model = "auto", ... )
input |
Input data. Can be:
|
input_type |
Character. Type of input data:
|
dimensions |
Character vector. Which VAD dimensions to predict:
Default: all three dimensions |
label_type |
Character. Type of labels to use:
|
custom_labels |
Optional list. Custom labels when label_type = "custom".
Must follow structure: list(valence = list(positive = "...", negative = "..."), ...).
Give an element for each dimension in |
model |
Character. The model that scores each dimension.
Give the model here, not as |
... |
Additional arguments passed to underlying classification functions
( |
This function implements direct VAD prediction using the approach: Input → VAD Classification → VAD Scores
Instead of mapping from discrete emotions, each VAD dimension is treated as a separate binary classification task using definitional labels that explain the psychological concepts.
Definitional Labels (default): The function uses rich descriptions that educate the model about each dimension:
Valence: "Positive emotional experience: pleasant, joyful, ..."
Arousal: "High energy emotional state: intense, excited, ..."
Dominance: "Feeling in control: powerful, confident, ..."
Input Type Detection: When input_type = "auto", the function detects input type based on:
URLs starting with "http": Video
File paths with image extensions: Image
Everything else: Text
Score Interpretation: Scores represent the probability that the input exhibits the "high" pole:
Valence: 1.0 = very positive, 0.0 = very negative
Arousal: 1.0 = high energy, 0.0 = very calm
Dominance: 1.0 = very controlling, 0.0 = very powerless
A data.frame with columns:
input_id: Identifier for each input (text content, filename, or index)
valence: Valence score (0-1, where 1 = positive)
arousal: Arousal score (0-1, where 1 = high arousal)
dominance: Dominance score (0-1, where 1 = high dominance)
Only requested dimensions are included in output.
All processing is done locally with downloaded models. Data is never sent to external servers.
Aleksandar Tomasevic <[email protected]>
Russell, J. A. (1980). A circumplex model of affect. Journal of Personality and Social Psychology, 39(6), 1161-1178.
Bradley, M. M., & Lang, P. J. (1994). Measuring emotion: the self-assessment manikin and the semantic differential. Journal of Behavior Therapy and Experimental Psychiatry, 25(1), 49-59.
## Not run: # Text VAD analysis texts <- c("I'm absolutely thrilled!", "I feel so helpless and sad", "This is boring") text_vad <- vad_scores(texts, input_type = "text") print(text_vad) # Image VAD analysis image_path <- system.file("extdata", "boris-1.png", package = "transforEmotion") image_vad <- vad_scores(image_path, input_type = "image") print(image_vad) # Single dimension prediction valence_only <- vad_scores(texts, dimensions = "valence") # Using simple labels for speed simple_vad <- vad_scores(texts, label_type = "simple") # Custom labels for domain-specific applications custom_labels <- list( valence = list( positive = "Customer satisfaction and positive brand sentiment", negative = "Customer complaints and negative brand sentiment" ) ) brand_vad <- vad_scores(texts, dimensions = "valence", label_type = "custom", custom_labels = custom_labels) ## End(Not run)## Not run: # Text VAD analysis texts <- c("I'm absolutely thrilled!", "I feel so helpless and sad", "This is boring") text_vad <- vad_scores(texts, input_type = "text") print(text_vad) # Image VAD analysis image_path <- system.file("extdata", "boris-1.png", package = "transforEmotion") image_vad <- vad_scores(image_path, input_type = "image") print(image_vad) # Single dimension prediction valence_only <- vad_scores(texts, dimensions = "valence") # Using simple labels for speed simple_vad <- vad_scores(texts, label_type = "simple") # Custom labels for domain-specific applications custom_labels <- list( valence = list( positive = "Customer satisfaction and positive brand sentiment", negative = "Customer complaints and negative brand sentiment" ) ) brand_vad <- vad_scores(texts, dimensions = "valence", label_type = "custom", custom_labels = custom_labels) ## End(Not run)
Ensures the object has the expected fields and types: 'labels', 'confidences', 'intensity', and 'evidence_chunks'.
validate_rag_json(x, error = TRUE)validate_rag_json(x, error = TRUE)
x |
A list (parsed JSON) to validate. |
error |
Logical; if TRUE, throws an error on invalid input. |
Invisibly returns TRUE when valid; otherwise FALSE or error.
Evaluates emotion/sentiment predictions from rag() or rag_sentemo() against ground truth labels using the same metrics pipeline as evaluate_emotions(). Supports table or JSON structured outputs.
validate_rag_predictions( rag_output, ground_truth, id_col = NULL, task = c("emotion", "sentiment"), labels_set = NULL, metrics = c("accuracy", "f1_macro", "confusion_matrix"), return_plot = FALSE )validate_rag_predictions( rag_output, ground_truth, id_col = NULL, task = c("emotion", "sentiment"), labels_set = NULL, metrics = c("accuracy", "f1_macro", "confusion_matrix"), return_plot = FALSE )
rag_output |
Output from rag() or rag_sentemo() with structured outputs (data.frame with columns like 'doc_id', 'label', 'confidence'; or JSON string with these fields). Global schema outputs with 'labels'/'confidences' are also handled by reducing to the top label. |
ground_truth |
Character vector of ground truth labels matching the number of predictions (or length of provided ids). |
id_col |
Optional identifier. If 'rag_output' is a data.frame and 'id_col' is a character scalar naming a column present in it, that column is used as the prediction id. Alternatively, 'id_col' can be a vector of ids (same length as 'ground_truth') used to align ground truth to the predictions by merge. |
task |
Task type: one of '"emotion"' or '"sentiment"' (used for metadata and optional label set enforcement). |
labels_set |
Optional character vector of allowed labels for validation. If provided, predictions will be lowercased and filtered to this set where possible. |
metrics |
Metrics to compute, forwarded to evaluate_emotions() (e.g., 'c("accuracy","f1_macro","confusion_matrix")'). |
return_plot |
Logical; whether to include plotting helpers. |
A list of evaluation results in the same format as evaluate_emotions(), augmented with '$rag_metadata' summarizing RAG-specific context (documents, transformer, task).
## Not run: texts <- c( "I feel so happy and grateful today!", "This is frustrating and makes me angry.", "I'm not sure how I feel about this." ) # Get predictions (structured per-document output) rag_results <- rag_sentemo( texts, task = "emotion", output = "table", transformer = "Qwen2.5-3B" ) # Ground truth labels ground_truth <- c("joy", "anger", "neutral") # Validate predictions validation_results <- validate_rag_predictions( rag_output = rag_results, ground_truth = ground_truth, task = "emotion", metrics = c("accuracy", "f1_macro", "confusion_matrix"), return_plot = TRUE ) ## End(Not run)## Not run: texts <- c( "I feel so happy and grateful today!", "This is frustrating and makes me angry.", "I'm not sure how I feel about this." ) # Get predictions (structured per-document output) rag_results <- rag_sentemo( texts, task = "emotion", output = "table", transformer = "Qwen2.5-3B" ) # Ground truth labels ground_truth <- c("joy", "anger", "neutral") # Validate predictions validation_results <- validate_rag_predictions( rag_output = rag_results, ground_truth = ground_truth, task = "emotion", metrics = c("accuracy", "f1_macro", "confusion_matrix"), return_plot = TRUE ) ## End(Not run)
This function retrieves facial expression recognition (FER) scores from a specific number of frames extracted from a YouTube video using a specified Hugging Face CLIP model. It utilizes Python libraries for facial recognition and emotion detection in text, images, and video.
video_scores( video, classes, nframes = 100, face_selection = "largest", start = 0, end = -1, uniform = FALSE, ffreq = 15, save_video = FALSE, save_frames = FALSE, save_dir = "temp/", video_name = "temp", model = "oai-base", local_model_path = NULL, face_detector = c("yunet", "haar") )video_scores( video, classes, nframes = 100, face_selection = "largest", start = 0, end = -1, uniform = FALSE, ffreq = 15, save_video = FALSE, save_frames = FALSE, save_dir = "temp/", video_name = "temp", model = "oai-base", local_model_path = NULL, face_detector = c("yunet", "haar") )
video |
The URL of the YouTube video to analyze. |
classes |
A character vector specifying the classes to analyze. |
nframes |
The number of frames to analyze in the video. Default is 100. |
face_selection |
The method for selecting faces in the video. Options are
"largest", "left", "right", "none", "all", or a data frame of faces.
Default is "largest". Use "none" to classify the whole frame without face
cropping. These four pick one face per frame and return one row per
frame, as before. "all" scores every face in every frame, and a data
frame from |
start |
The start time of the video range to analyze. Default is 0. |
end |
The end time of the video range to analyze. Default is -1 and this means that video won't be cut. If end is a positive number greater than start, the video will be cut from start to end. |
uniform |
Logical indicating whether to uniformly sample frames from the video. Default is FALSE. |
ffreq |
The frame frequency for sampling frames from the video. Default is 15. |
save_video |
Logical indicating whether to save the analyzed video. Default is FALSE.
A saved YouTube video has no sound when YouTube offers no stream with
video and audio in one file, which is now the usual case; use
|
save_frames |
Logical indicating whether to save the analyzed frames. Default is FALSE. |
save_dir |
The directory to save the analyzed frames. Default is "temp/". |
video_name |
The name of the analyzed video. Default is "temp". |
model |
A string specifying the vision model to use. Options include:
Use |
local_model_path |
Optional. Path to a local directory containing a pre-downloaded HuggingFace model. If provided, the model will be loaded from this directory instead of being downloaded from HuggingFace. This is useful for offline usage or for using custom fine-tuned models. On Linux/Mac, look in ~/.cache/huggingface/hub/ folder for downloaded models. Navigate to the snapshots folder for the relevant model and point to the directory which contains the config.json file. For example: "/home/username/.cache/huggingface/hub/models–cross-encoder–nli-distilroberta-base/snapshots/b5b020e8117e1ddc6a0c7ed0fd22c0e679edf0fa/" On Windows, the base path is C:\Users\USERNAME\.cache\huggingface\transformers\ Warning: Using very large models from local paths may cause memory issues or crashes depending on your system's resources, especially when processing videos with many frames. |
face_detector |
|
A data frame of scores, one row per frame that could be read (or
one row per face; see face_selection).
All processing is done locally with the downloaded model, and your video frames are never sent to any remote server or third-party.
Aleksandar Tomasevic <[email protected]>
Starts a small web page from R that shows every image, or every sampled
video frame, with its face boxes, and opens it in your web browser. Faces
of the same person share a colour, and a People panel lists each person
with the images, frames and tracks they appear in. With
select = TRUE, you click faces, video tracks or people, then
Send to R, and view_faces() returns the chosen faces.
view_faces( faces, select = FALSE, selected = NULL, live = TRUE, open = interactive(), dir = NULL, title = NULL, max_side = NULL, port = NULL, inline = .te_in_notebook() )view_faces( faces, select = FALSE, selected = NULL, live = TRUE, open = interactive(), dir = NULL, title = NULL, max_side = NULL, port = NULL, inline = .te_in_notebook() )
faces |
A data frame returned by |
select |
Logical. Let the viewer choose faces and return them.
Default is |
selected |
Faces to start with selected, as rows of |
live |
Logical. Serve the page from R (the default). |
open |
Logical. Open the page in the web browser. Default is
|
dir |
A new or empty folder for the page. Default is a new folder in the temporary directory, which R deletes when the session ends. |
title |
Title shown at the top of the page. |
max_side |
Longest side, in pixels, of the image and frame copies in the page. Default is 1280, or 640 inside a notebook, where the copies are stored in the notebook itself. Boxes are drawn in the original pixels, whatever the size of the copies. |
port |
Port of the local server. Default is a free port chosen at random; set one to forward it over SSH when R runs on a remote computer. |
inline |
Logical. Show the page inside the notebook, below the cell,
instead of in a browser tab. Default is |
The page is served by R itself (with httpuv) on your own computer
only (127.0.0.1), at an address with a random part that only you
get. It makes no outside requests. While R waits for a selection, the
console is busy; press Esc (Ctrl+C in a terminal) to stop waiting.
With live = FALSE, no server is started: the page is written as a
folder that also works when opened from disk, for example after copying it
from a remote computer to yours. Its Export selection button then
downloads a file that read_face_selection() reads.
In a Jupyter notebook, including Google Colab, the page is shown inside the
notebook, below the cell (inline = TRUE, the default there). A page
served from R cannot be reached from a notebook running on another
computer, so in select mode the page gives a line of R code instead:
click Copy R code, paste the line into a new cell and run it, and
read_face_selection() returns the chosen faces.
Images without a face are shown too when faces is a complete
detect_faces result; for a subset, only images with a face
in it are shown. For a video, every sampled frame is shown, and the frames
are extracted again (as in face_scores), unless
detect_faces kept them with save_frames = TRUE.
In select mode, clicking a face in a still image selects that face; clicking a face in a video selects its whole track; clicking a person selects every face of that person.
When R runs on a remote computer over SSH, forward the port first (the
message printed by view_faces() gives the command), or use
live = FALSE and copy the folder.
The page lists file names and shows the images, so share it only with people who may see them. It contains no face embeddings and no scores.
With select = TRUE: the chosen rows of faces, which
face_scores scores and face_selection in
image_scores, image_scores_dir and
video_scores accepts, or NULL if you click
Cancel. Otherwise, invisibly, the page: a list with its
url, its folder dir, and stop(), which stops the
server. With live = FALSE, the path of the page's
index.html, invisibly. With inline = TRUE, invisibly, the
path of the page as one self-contained file, faces.html in
dir; in select mode the chosen faces come from the copied line of
code.
draw_faces for the same boxes as plots in R,
select_faces to choose faces by rule, number or person.
## Not run: images <- system.file("extdata", c("two-people.jpg", "boris-2.png", "trump2.jpg"), package = "transforEmotion") faces <- detect_faces(images) view_faces(faces) # Click the faces to keep, then Send to R chosen <- view_faces(faces, select = TRUE) face_scores(chosen, classes = c("happy", "sad", "angry", "neutral")) # Without a server: a folder to copy, and a file to read back view_faces(faces, select = TRUE, live = FALSE, dir = "faces-page") chosen <- read_face_selection("~/Downloads/selection-faces-20261011-101500.json", faces) # In a notebook (Colab): the page appears below the cell. Click the faces, # then Copy R code, and run the copied line in a new cell, for example view_faces(faces, select = TRUE) chosen <- read_face_selection("te-selection:eyJzY2hlbWEiOi...", faces) ## End(Not run)## Not run: images <- system.file("extdata", c("two-people.jpg", "boris-2.png", "trump2.jpg"), package = "transforEmotion") faces <- detect_faces(images) view_faces(faces) # Click the faces to keep, then Send to R chosen <- view_faces(faces, select = TRUE) face_scores(chosen, classes = c("happy", "sad", "angry", "neutral")) # Without a server: a folder to copy, and a file to read back view_faces(faces, select = TRUE, live = FALSE, dir = "faces-page") chosen <- read_face_selection("~/Downloads/selection-faces-20261011-101500.json", faces) # In a notebook (Colab): the page appears below the cell. Click the faces, # then Copy R code, and run the copied line in a new cell, for example view_faces(faces, select = TRUE) chosen <- read_face_selection("te-selection:eyJzY2hlbWEiOi...", faces) ## End(Not run)