Standard Key Drivers
Executive Summary

Executive Summary

Top driver and the priority opportunity for Score across 156 responses.

Responses
156
Drivers Analysed
6
Top Driver
Social support
Top Relative Importance
26.8
Model R Squared
0.779
Priority Drivers
0
Across 156 responses, Social support is the strongest influence on Score, holding about 27% of the combined driver importance, ahead of GDP per capita at 25%. No driver falls in the Improve / Priority quadrant — the most important drivers are already rated well, so the focus is on protecting those strengths. Importance reflects association with Score, not proof of what moves it.
What this means

Social support is the strongest association with national happiness scores, accounting for 26.8% of combined driver importance, followed by GDP per capita at 24.6%. Across 156 responses, no driver falls into the priority (high importance, low performance) quadrant—the most important drivers are already rated well. Focus is on protecting these strengths rather than fixing gaps. Importance reflects association with Score, not proof of causal effect.

Overview

Analysis Overview

What Key Driver Analysis measures for Score across 6 drivers.

N Observations156
N Drivers6
R Squared0.779
N Priority0
What this means

Key Driver Analysis identifies which attributes correlate most tightly with national happiness scores and ranks them by their combined standardized effect. The method crosses importance (how closely each driver tracks the outcome) with performance (how well each is rated today) to create an action quadrant. This analysis of 156 responses examined 6 drivers and found they collectively explain 77.9% of variation in Score. The analysis is strictly correlational—it shows which attributes move together with happiness, not which ones would move it if changed.

Data Preparation

Data Quality

Outcome, drivers used and dropped, imputation, and model fit.

Initial Rows156
Final Rows156
Rows Removed0
N Drivers6
R Squared0.779
What this means

All 156 responses contained usable Score values; no rows were removed. The 6 drivers were all numeric and usable; missing values were imputed with column medians. Driver ratings follow a fixed scale, so performance was rescaled to 0–100 against that range. The model R-squared of 0.779 indicates the driver set accounts for 77.9% of Score variation, establishing the baseline for interpreting importance shares. This fit is strong enough to support the relative ranking of drivers and the quadrant placements that follow.

Visualization

What Drives the Outcome

Each driver's share of the combined importance, ranked.

What this means

Social support leads at 26.8% relative importance, closely followed by GDP per capita at 24.6% and Healthy life expectancy at 20.8%. Together, these three drivers account for over 71% of combined importance. Freedom to make life choices contributes 16.6%, while Perceptions of corruption (7.3%) and Generosity (3.7%) trail significantly. The concentration in the top three reflects a clear hierarchy: social and economic factors dominate the association with happiness scores, while trust and charitable behavior show weaker correlations. High importance means tight tracking with Score, not proof that changing the driver will move it.

Data Table

Driver Detail

Importance, correlation, performance, and quadrant for each driver of Score.

DriverRelative ImportanceCorrelationStd BetaMean PerformanceQuadrant
Social support26.80.7770.30224.2Maintain
GDP per capita24.60.7940.27818.1Maintain
Healthy life expectancy20.80.780.23514.5Maintain
Freedom to make life choices16.60.5670.1877.9Monitor
Perceptions of corruption7.30.3860.0832.2Monitor
Generosity3.70.0760.0423.7Monitor
What this means

The short answer

GDP per capita shows the highest plain correlation with happiness scores (0.794), while Social support has the largest standardized effect (0.302). All six drivers move in the same direction as happiness, but their current performance levels are low relative to their importance—meaning these are strengths to protect, not gaps to close.

The detail

GDP per capita: correlation 0.794, standardized effect 0.278, mean performance 18.1 (Maintain quadrant). Social support: correlation 0.777, standardized effect 0.302, mean performance 24.2 (Maintain quadrant). Healthy life expectancy: correlation 0.78, standardized effect 0.235, mean performance 14.5 (Maintain quadrant). Freedom to make life choices: correlation 0.567, standardized effect 0.187, mean performance 7.9 (Monitor quadrant). Perceptions of corruption: correlation 0.386, standardized effect 0.083, mean performance 2.2 (Monitor quadrant). Generosity: correlation 0.076, standardized effect 0.042, mean performance 3.7 (Monitor quadrant). Zero drivers sit in the Improve/Priority quadrant.

What this can't tell you

Correlation does not imply causation. A nation's GDP per capita and social support networks are entangled with governance, history, and culture—isolating the effect of any single driver would require experimental or quasi-experimental evidence. Current performance is measured on the survey's scale; absolute comparability across nations or over time depends on scale stability.

Visualization

Importance vs Performance

Action quadrant: relative importance against current performance for each driver of Score.

What this means

All six drivers plot across Maintain and Monitor quadrants; none occupy the high-importance, low-performance Improve/Priority region. Social support, GDP per capita, and Healthy life expectancy cluster in the upper-left (Maintain): important and well-rated. Freedom to make life choices, Perceptions of corruption, and Generosity occupy the lower-left (Monitor): lower importance and lower performance. This pattern reflects a favorable situation—the strongest associations with happiness are already performing well. The layout is consistent with association, not causation; placement shows which attributes move together with Score, not which would move it if changed.

Data Table

Action Summary

Recommended next step for each importance-performance quadrant.

QuadrantDriversAction
MaintainSocial support, GDP per capita, Healthy life expectancyProtect these strengths. They are strongly associated with the outcome and already rated well.
Improve / PriorityNoneAct here first. These are strongly associated with the outcome but rated low today, so gains here are most likely to move the outcome.
MonitorFreedom to make life choices, Perceptions of corruption, GenerosityLow priority. Weakly associated with the outcome and rated low; keep an eye on them but do not over-invest.
Reduce effort / Possible over-investNonePossible over-investment. Rated highly but only weakly associated with the outcome, so extra effort here may not pay off.
What this means

Three drivers occupy Maintain: Social support, GDP per capita, and Healthy life expectancy are strongly associated with Score and already rated well, so the action is to protect these strengths. Three drivers sit in Monitor: Freedom to make life choices, Perceptions of corruption, and Generosity are weakly associated with the outcome and rated low, warranting only light attention. No drivers require priority action (Improve/Priority quadrant is empty), and none suggest over-investment (Reduce effort quadrant is empty). Every recommendation rests on association with Score; treat the quadrants as places to test, not proven levers for change.

Rate this report Was this the answer you needed?
The exact source that produced this report — yours to keep, read, and re-run.
Download PDF
How this was computed method · R source · citation
The code that did it

Key Driver Analysis — What Moves Satisfaction

The marquee CX / survey analysis: of the attributes you measure, which ones most move an outcome you care about (satisfaction, NPS, spend, retention)? Key Driver Analysis combines two lenses — statistical IMPORTANCE (how tightly each attribute tracks the outcome) and current PERFORMANCE (how well each attribute is rated today) — into an action quadrant that tells you where to invest first.

Why This Method?

Ranking drivers by raw correlation alone tells you what matters but not where you are weak. Ranking by performance alone tells you where you are weak but not whether it matters. Plotting importance against performance resolves both at once: the drivers that are important AND under-performing are the priorities; important-and-strong drivers are strengths to protect.

What This Analysis Covers

  • Relative importance of each driver (standardized effect + correlation)
  • Current performance of each driver on a 0-100 scale
  • The importance-vs-performance action quadrant
  • A per-quadrant action summary

Standard Library

Platform standard-library module (LAT-1441): runs on ANY dataset via the semantic mapping {outcome, driver_1..driver_N}. All narrative is derived from the user's own column names and computed values. This is a CORRELATIONAL analysis — it reports association, never proven causation.

suppressPackageStartupMessages(library(DT))
suppressPackageStartupMessages(library(htmlwidgets))
suppressPackageStartupMessages(library(arrow))
suppressPackageStartupMessages(library(knitr))
suppressPackageStartupMessages(library(rmarkdown))
suppressPackageStartupMessages(library(dplyr))
suppressPackageStartupMessages(library(tidyr))
suppressPackageStartupMessages(library(ggplot2))
suppressPackageStartupMessages(library(stringr))
suppressPackageStartupMessages(library(lubridate))
suppressPackageStartupMessages(library(broom))
suppressPackageStartupMessages(library(Matrix))
suppressPackageStartupMessages(library(cluster))
suppressPackageStartupMessages(library(data.table))

Step 1: Row accounting + semantic column discovery

initial_rows <- nrow(df)
  if (!"outcome" %in% names(df)) {
    stop("column_mapping must map an &#x27;outcome' column (the numeric outcome to explain, e.g. satisfaction, NPS, or spend)")
  }
  driver_cols <- grep("^driver_[0-9]+$", names(df), value = TRUE)
  driver_cols <- driver_cols[order(as.integer(sub("^driver_", "", driver_cols)))]
  if (length(driver_cols) == 0) {
    stop("column_mapping must map at least one driver column(driver_1)")
  }

  outcome_name <- humanize_semantic("outcome", col_map)
  driver_names <- setNames(humanize_semantic(driver_cols, col_map), driver_cols)

Step 2: Coerce outcome to numeric; drop rows with a missing outcome

df$outcome <- suppressWarnings(as.numeric(df$outcome))
  df <- df[!is.na(df$outcome), , drop = FALSE]
  if (nrow(df) < 10) {
    stop(sprintf(
      "Only %d rows have a usable numeric value in the outcome column &#x27;%s'. At least 10 are required for Key Driver Analysis.",
      nrow(df), outcome_name))
  }

Step 3: Coerce each driver to numeric (95%% rule); impute NA with median

dropped_drivers <- character(0)
  for (dc in driver_cols) {
    v <- df[[dc]]
    if (!is.numeric(v)) {
      conv <- suppressWarnings(as.numeric(as.character(v)))
      n_orig <- sum(!is.na(v) & as.character(v) != "")
      if (n_orig > 0 && sum(!is.na(conv)) >= 0.95 * n_orig) {
        df[[dc]] <- conv
      } else {
        dropped_drivers <- c(dropped_drivers, dc); next
      }
    }
    v <- df[[dc]]
    med <- median(v, na.rm = TRUE)
    if (is.na(med)) { dropped_drivers <- c(dropped_drivers, dc); next }
    v[is.na(v)] <- med
    df[[dc]] <- v
  }

Step 4: Drop zero-variance (constant) drivers — report them

for (dc in setdiff(driver_cols, dropped_drivers)) {
    v <- df[[dc]]
    if (isTRUE(var(v, na.rm = TRUE) == 0) || is.na(var(v, na.rm = TRUE))) {
      dropped_drivers <- c(dropped_drivers, dc)
    }
  }
  model_drivers <- setdiff(driver_cols, dropped_drivers)
  if (length(model_drivers) == 0) {
    stop("No usable driver columns remained after cleaning(all were constant, empty, or non-numeric).")
  }

  df_clean <- df[, c("outcome", model_drivers), drop = FALSE]
  final_rows <- nrow(df_clean)
  rows_removed <- initial_rows - final_rows

Step 5: Guard — need clearly more rows than drivers (p < n)

while (length(model_drivers) >= final_rows - 2 && length(model_drivers) > 1) {
    drop_dc <- model_drivers[length(model_drivers)]
    dropped_drivers <- c(dropped_drivers, drop_dc)
    model_drivers <- model_drivers[-length(model_drivers)]
    df_clean <- df_clean[, c("outcome", model_drivers), drop = FALSE]
  }

Step 6: Importance — standardized regression coefficients + correlations

Standardize outcome + drivers, fit OLS on the z-scores. |standardized beta| is each driver's independent effect on a common scale. Relative importance = each driver's share of the total |beta|, times 100 (a practical relative-weights proxy). Perfectly collinear drivers are aliased by lm (NA beta) — they carry no independent share, so their |beta| is treated as 0 and a collinearity note is raised.

zdf <- as.data.frame(scale(df_clean[, c("outcome", model_drivers), drop = FALSE]))
  z_model <- lm(outcome ~ ., data = zdf)
  z_coef <- coef(z_model)
  std_betas_raw <- z_coef[model_drivers]           # named by semantic; NA if aliased
  aliased_any <- any(is.na(std_betas_raw))
  abs_beta <- abs(std_betas_raw)
  abs_beta[is.na(abs_beta)] <- 0
  total_beta <- sum(abs_beta)
  rel_importance <- if (total_beta > 0) 100 * abs_beta / total_beta else rep(0, length(abs_beta))
  rel_importance <- round(as.numeric(rel_importance), 1)

  r_squared <- summary(z_model)$r.squared
  if (is.na(r_squared)) r_squared <- 0

  correlations <- sapply(model_drivers, function(dc) {
    suppressWarnings(cor(df_clean[[dc]], df_clean$outcome, use = "complete.obs"))
  })
  correlations[is.na(correlations)] <- 0

Collinearity check — exact aliasing or any driver pair above 0.9 |r|.

max_pair_cor <- 0
  if (length(model_drivers) >= 2) {
    dm <- suppressWarnings(cor(df_clean[, model_drivers, drop = FALSE],
                               use = "pairwise.complete.obs"))
    dm[!is.finite(dm)] <- 0
    diag(dm) <- 0
    max_pair_cor <- max(abs(dm))
  }
  collinear <- aliased_any || (max_pair_cor > 0.9)

Step 7: Performance — mean rating normalized to 0-100

mean_ratings <- sapply(model_drivers, function(dc) mean(df_clean[[dc]], na.rm = TRUE))
  all_vals <- unlist(df_clean[, model_drivers], use.names = FALSE)
  vmax <- max(all_vals, na.rm = TRUE)
  vmin <- min(all_vals, na.rm = TRUE)
  scale_detected <- is.finite(vmax) && vmax <= 10 && vmin >= 0
  if (scale_detected) {
    scale_min <- if (vmin < 1) 0 else 1
    scale_max <- if (vmax <= 5) 5 else if (vmax <= 7) 7 else 10
    performance <- 100 * (mean_ratings - scale_min) / (scale_max - scale_min)
  } else {
    performance <- sapply(model_drivers, function(dc) {
      lo <- min(df_clean[[dc]], na.rm = TRUE)
      hi <- max(df_clean[[dc]], na.rm = TRUE)
      if (hi > lo) 100 * (mean(df_clean[[dc]], na.rm = TRUE) - lo) / (hi - lo) else 50
    })
  }
  performance <- round(pmin(100, pmax(0, as.numeric(performance))), 1)

Step 8: Quadrants — median split of importance x performance

imp_median  <- median(rel_importance)
  perf_median <- median(performance)
  high_imp  <- rel_importance >= imp_median
  high_perf <- performance   >= perf_median
  quadrant <- ifelse(high_imp & high_perf, "Maintain",
              ifelse(high_imp & !high_perf, "Improve / Priority",
              ifelse(!high_imp & !high_perf, "Monitor",
                     "Reduce effort / Possible over-invest")))

  drivers_df <- data.frame(
    semantic             = model_drivers,
    driver               = unname(driver_names[model_drivers]),
    relative_importance  = rel_importance,
    correlation          = round(as.numeric(correlations), 3),
    std_beta             = round(as.numeric(ifelse(is.na(std_betas_raw), 0, std_betas_raw)), 3),
    mean_rating          = round(as.numeric(mean_ratings), 2),
    mean_performance     = performance,
    quadrant             = quadrant,
    stringsAsFactors     = FALSE
  )
  drivers_df <- drivers_df[order(-drivers_df$relative_importance,
                                 -abs(drivers_df$correlation)), , drop = FALSE]
  rownames(drivers_df) <- NULL

Step 10: Headline drivers

top_driver_name    <- drivers_df$driver[1]
  top_rel_importance <- drivers_df$relative_importance[1]
  priority_rows <- drivers_df[drivers_df$quadrant == "Improve / Priority", , drop = FALSE]
  priority_driver_name <- if (nrow(priority_rows) > 0) priority_rows$driver[1] else NA_character_
  priority_driver_perf <- if (nrow(priority_rows) > 0) priority_rows$mean_performance[1] else NA_real_
  n_priority <- nrow(priority_rows)

Step 11: KPI metrics

metrics <- list(
    `Responses`               = final_rows,
    `Drivers Analysed`        = length(model_drivers),
    `Top Driver`              = top_driver_name,
    `Top Relative Importance` = round(top_rel_importance, 1),
    `Model R Squared`         = round(r_squared, 3),
    `Priority Drivers`        = n_priority
  )

Step 12: json_output machine channel

priority_clause <- if (!is.na(priority_driver_name)) {
    paste0(priority_driver_name, " is the clearest priority — important yet rated ",
           round(priority_driver_perf, 0), " out of 100.")
  } else {
    "No driver falls in the Improve / Priority quadrant."
  }
  json_output <- list(
    answer = paste0(
      "Key Driver Analysis of ", outcome_name, " across ",
      format(final_rows, big.mark = ","), " responses on ",
      n_things(length(model_drivers), "driver"), ": ", top_driver_name,
      " is the most influential, holding about ", round(top_rel_importance, 0),
      "% of the combined driver importance. ", priority_clause,
      " Model R-squared is ", round(r_squared, 3),
      ". These are associations, not proof that a driver changes the outcome."
    ),
    cards = lapply(
      c("tldr", "overview", "preprocessing", "importance_chart",
        "driver_table", "priority_matrix", "action_summary"),
      function(cid) list(id = cid, metrics = metrics)
    )
  )

  list(
    initial_rows = initial_rows, final_rows = final_rows, rows_removed = rows_removed,
    outcome_name = outcome_name, driver_names = driver_names,
    model_drivers = model_drivers, dropped_drivers = dropped_drivers,
    df_clean = df_clean, r_squared = r_squared,
    scale_detected = scale_detected, collinear = collinear,
    drivers_df = drivers_df, importance_df = importance_df,
    driver_details_df = driver_details_df,
    importance_performance_df = importance_performance_df,
    quadrant_summary_df = quadrant_summary_df,
    top_driver_name = top_driver_name, top_rel_importance = top_rel_importance,
    priority_driver_name = priority_driver_name,
    priority_driver_perf = priority_driver_perf, n_priority = n_priority,
    metrics = metrics, json_output = json_output
  )
}
Your data has more stories to tell.Run any analysis on your own data — validated R modules, interactive reports, AI insights, and PDF export. 500 free credits on signup.
Try Free — No SignupSign Up Free

Cite this analysis

Report an Issue

Tell us what's wrong. You'll get a free re-run of this analysis so you can try again with different parameters. If the re-run still doesn't meet your expectations, we'll refund your credits.

Want to run this analysis on your own data? Upload CSV — Free Analysis See Pricing