vivaglint is an R package built for HR analysts who work with Microsoft Viva Glint survey exports. It handles the repetitive data wrangling that Glint’s native UI doesn’t support — multi-cycle trend analysis, manager roll-ups, demographic segmentation, attrition risk scoring, and comment search — so you can spend more time interpreting results and less time reshaping data.
Export your survey from Viva Glint as a CSV and load it with
read_glint_survey():
survey_path <- system.file("extdata", "survey_export.csv", package = "vivaglint")
survey <- read_glint_survey(survey_path, emp_id_col = "EMP ID")If you prefer to pull data directly from the Viva Glint API,
configure credentials once and use
read_glint_survey_api():
glint_setup(
tenant_id = "your-tenant-id",
client_id = "your-client-id",
client_secret = "your-client-secret",
experience_name = "your-experience-name"
)
survey <- read_glint_survey_api(
survey_uuid = "your-survey-uuid",
cycle_id = "your-cycle-id",
emp_id_col = "EMP ID"
)This returns a glint_survey object containing: -
data: The full survey response table -
metadata: Question names, column mappings, and
respondent counts
Calculate metrics across all survey questions in one call:
Each row in the output represents one question and includes: -
mean — Average response on the raw scale - sd
— Standard deviation - glint_score — Score transformed to
0–100, matching what appears in the Viva Glint UI -
n_responses — Number of employees who answered the question
- n_skips — Number of employees who skipped the question -
n_total — Total respondents - pct_favorable —
Percentage of favorable responses - pct_neutral —
Percentage of neutral responses - pct_unfavorable —
Percentage of unfavorable responses
About Glint Score: The Glint Score is calculated as
round(((mean - 1) / (scale_points - 1)) * 100), placing
every question on a common 0–100 scale regardless of the original scale
format.
About Favorability: The package uses Viva Glint’s standard favorability thresholds for each scale type. On a 5-point scale, for example, 4–5 is favorable, 3 is neutral, and 1–2 is unfavorable.
If you’re presenting to a leadership team or investigating a particular theme, filter to just the questions you care about:
One of the most common HR reporting tasks is tracking whether
engagement improved since the last survey. compare_cycles()
takes multiple survey objects and aligns them by question:
survey_path <- system.file("extdata", "survey_export.csv", package = "vivaglint")
survey_q1 <- read_glint_survey(survey_path, emp_id_col = "EMP ID")
survey_q2 <- read_glint_survey(survey_path, emp_id_col = "EMP ID")
survey_q3 <- read_glint_survey(survey_path, emp_id_col = "EMP ID")
trends <- compare_cycles(
survey_q1, survey_q2, survey_q3,
scale_points = 5,
cycle_names = c("Q1 FY25", "Q2 FY25", "Q3 FY25")
)The output includes all metrics from summarize_survey()
for each cycle, plus: - change_from_previous — Point change
in mean score vs. prior cycle - pct_change_from_previous —
Percentage change vs. prior cycle
This is the foundation for executive trend slides: which items are improving, holding steady, or declining quarter over quarter.
HR business partners often need to understand which managers’ teams are scoring below average, or identify pockets of high engagement to learn from.
# Direct reports only
manager_summary <- aggregate_by_manager(survey, scale_points = 5)
# Full org tree (includes indirect reports)
manager_full <- aggregate_by_manager(survey, scale_points = 5, full_tree = TRUE)Each row represents one manager × question combination and includes:
- manager_id, manager_name -
team_size - All standard metrics: mean,
sd, glint_score, n_responses,
n_skips, n_total, pct_favorable,
pct_neutral, pct_unfavorable
You can filter the result to identify managers with low favorability on a specific question:
library(dplyr)
# Managers where fewer than 50% of their team is favorable on a key item
low_engagement_managers <- manager_summary %>%
filter(question == "I would recommend my team as a great place to work",
pct_favorable < 50) %>%
arrange(pct_favorable)analyze_by_attributes() lets you break survey results
down by any combination of employee attributes — department, gender,
tenure group, location, job level, etc.
attr_path <- system.file("extdata", "employee_attributes.csv", package = "vivaglint")
demo_results <- analyze_by_attributes(
survey,
attribute_file = attr_path,
scale_points = 5,
attribute_cols = c("Department", "Gender", "Tenure Group"),
min_group_size = 10 # Suppress groups below this size for privacy
)This is useful for identifying which employee populations have systematically lower scores and on which questions.
If you plan to run several analyses against the same attribute file, join it once and reuse the enriched survey object:
# Join once
attr_path <- system.file("extdata", "employee_attributes.csv", package = "vivaglint")
survey_enriched <- join_attributes(survey, attr_path)
# Reuse for multiple analyses — no need to re-read the file
dept_results <- analyze_by_attributes(
survey_enriched,
scale_points = 5,
attribute_cols = "Department"
)
gender_results <- analyze_by_attributes(
survey_enriched,
scale_points = 5,
attribute_cols = "Gender"
)
# Filter to a subpopulation before analyzing
na_only <- survey_enriched
na_only$data <- filter(survey_enriched$data, Region == "North America")
na_results <- analyze_by_attributes(na_only, scale_points = 5,
attribute_cols = "Department")Attributes are stored in survey$metadata$attribute_cols
after joining, so they are excluded from question detection in
downstream analyses.
Linking engagement scores to actual turnover is a high-value analysis
for HR leaders. analyze_attrition() computes attrition
rates by favorability group — showing whether employees who scored low
on engagement were more likely to leave.
# Basic attrition analysis (90, 180, and 365 days post-survey)
attrition_path <- system.file("extdata", "attrition.csv", package = "vivaglint")
attrition <- analyze_attrition(
survey,
attrition_file = attrition_path,
emp_id_col = "EMP ID",
term_date_col = "Termination Date",
scale_points = 5
)The output shows, for each question and time period, the attrition rates for favorable vs. unfavorable responders along with a risk ratio — making it straightforward to identify which survey items are the strongest leading indicators of turnover.
Combine attrition analysis with employee attributes to answer questions like “Are unfavorable responders in Engineering leaving at higher rates than those in Sales?”:
attr_path <- system.file("extdata", "employee_attributes.csv", package = "vivaglint")
attrition_path <- system.file("extdata", "attrition.csv", package = "vivaglint")
survey_enriched <- join_attributes(survey, attr_path)
attrition_by_dept <- analyze_attrition(
survey_enriched,
attrition_file = attrition_path,
emp_id_col = "EMP ID",
term_date_col = "Termination Date",
scale_points = 5,
attribute_cols = c("Department", "Job Level"),
min_group_size = 10
)Correlation analysis is useful for identifying clusters of related questions — often as a first step before building a composite score or validating that a set of items measures a single construct:
# Pearson correlations in long format (default)
correlations <- get_correlations(survey)
# Spearman correlations (more robust for ordinal scale data)
correlations_spearman <- get_correlations(survey, method = "spearman")
# Correlation matrix
cor_matrix <- get_correlations(survey, format = "matrix")Supported methods: "pearson" (default),
"spearman", "kendall"
Factor analysis identifies the latent constructs underlying a set of survey items. This can validate whether your “manager effectiveness” items truly cluster together, or reveal unexpected groupings:
# Requires the psych package
factors <- extract_survey_factors(survey, n_factors = 3, rotation = "oblimin")
# Consolidated summary: item, factor assignment, loading, label, communality
print(factors$factor_summary)
# Filter to items with strong factor loadings only
strong_loaders <- dplyr::filter(factors$factor_summary, loading_label == "Strong")
# Access the raw psych object for advanced use
factors$fa_objectLoading labels: Strong (≥ 0.75), Medium (0.60–0.74), Weak (< 0.60)
Search across all comment columns at once — useful for surfacing themes around specific topics like “flexibility”, “burnout”, or a manager’s name:
# Fuzzy search (default) — tolerates minor spelling differences
flexibility_comments <- search_comments(survey, "flexibility")
# Exact, case-sensitive match
exact_results <- search_comments(survey, "work from home", exact = TRUE)
# Broaden fuzzy tolerance to catch more spelling variation
results <- search_comments(survey, "colaboration", max_distance = 0.3)Each result row includes: - question — Which question
the comment was attached to - response — The numeric score
the employee gave - comment — The comment text -
topics — Topic tags assigned by Glint
To route comments into a text analysis or NLP pipeline, reshape the survey to long format and filter to rows with comments:
# All responses in long format
long_all <- pivot_long(survey, data_type = "all")
# Comments only
long_comments <- pivot_long(survey, data_type = "comments")
# Both as separate tibbles
both <- pivot_long(survey, data_type = "both")
comments_df <- both$commentsFor workflows that route numeric scores and open-text comments to
different pipelines (e.g., numeric data to a statistical model, comments
to an LLM), use split_survey_data():
parts <- split_survey_data(survey)
# Numeric scores only — standard respondent columns + one score column per question
quantitative <- parts$quantitative
# Comments only — EMP ID + all _COMMENT, _COMMENT_TOPICS, _SENSITIVE_COMMENT_FLAG columns
qualitative <- parts$qualitative
# Pass numeric data directly to vivaglint functions
summary <- summarize_survey(parts$quantitative, scale_points = 5,
emp_id_col = "EMP ID")
# Rejoin at any time using EMP ID
full_data <- dplyr::left_join(parts$quantitative, parts$qualitative, by = "EMP ID")Use min_group_size wherever it’s available to suppress
results for groups that are too small to protect individual
anonymity:
# Default is 5; consider 10 or higher for sensitive analyses
attr_path <- system.file("extdata", "employee_attributes.csv", package = "vivaglint")
demo_results <- analyze_by_attributes(
survey,
attribute_file = attr_path,
scale_points = 5,
attribute_cols = c("Department", "Gender"),
min_group_size = 10
)This package processes all data locally within your R environment. No employee data is transmitted to any external service, including Microsoft. Always follow your organization’s data handling and privacy policies when working with employee survey data.
?read_glint_survey, ?summarize_survey,
?analyze_by_attributes, etc.