Standard Pca
Executive Summary

Executive Summary

Dimensionality and dominant structure across 768 observations

Observations
768
Features Analysed
8
PC1 Variance %
26.2
Components for 80%
5
Kaiser Components
3
Top PC1 Feature
BMI
PCA on 768 observations and 8 features shows the data is moderately compressible. The first component alone explains 26.2% of total variation and is driven by BMI, SkinThickness, Insulin. 5 component(s) reach 80% of the variance (7 reach 90%); the Kaiser criterion (eigenvalue > 1) keeps 3.
What this means

The short answer

The 8 clinical measurements compress into 5 dominant components that explain 80% of total variation. The first component, driven by BMI, SkinThickness, and Insulin, alone accounts for 26.2% of variation.

The detail

768 observations, 8 features analyzed. PC1 variance: 26.2%; components for 80% cumulative variance: 5; Kaiser components (eigenvalue > 1): 3. PC1 is the strongest axis of variation and is defined by BMI (loading −0.452), SkinThickness (−0.44), and Insulin (−0.435), indicating these measurements co-vary strongly and define the primary structure in the dataset.

What this can't tell you

Effective dimensionality depends on the downstream task. The 80% threshold is conventional but arbitrary; whether 5 components are sufficient for your clinical model depends on the signal-to-noise ratio required for prediction or classification.

Overview

Analysis Overview

Principal component analysis of 8 standardized features across 768 observations.

N Observations768
N Features8
Pc1 Variance26.2
Components 805
Kaiser3
What this means

The short answer

The analysis standardizes 8 clinical measurements and decomposes them into independent components to reveal the dominant patterns in the data. The first component captures 26.2% of variation; 5 components together explain 80%.

The detail

All 768 observations and 8 features (Pregnancies, Glucose, BloodPressure, SkinThickness, Insulin, BMI, DiabetesPedigreeFunction, Age) were standardized to mean 0 and variance 1 before decomposition. Loadings show which original features drive each component—features loading together vary together in the dataset. The Kaiser criterion (eigenvalue > 1) identifies 3 components as carrying signal above the noise floor of a single original feature.

What this can't tell you

This method assumes linear relationships among features. Nonlinear patterns or domain-specific interactions not captured by correlation will remain hidden in the residual components.

Data Preparation

Data Quality

Feature typing, imputation, and exclusions before PCA.

Initial Rows768
Final Rows768
Rows Removed0
What this means

The short answer

All 768 rows were retained with no exclusions. Missing values were imputed with each column's median, and all features were standardized so that measurement scale differences do not distort the components.

The detail

Initial load: 768 rows; final rows used: 768 (0 removed). All 8 mapped feature columns were numeric and usable. Standardization ensures that a feature measured in large units (e.g., Glucose in mg/dL) does not dominate the decomposition over one measured in small units (e.g., Pregnancies as a count). Observations are colored by Outcome on the component map to reveal whether the leading components separate outcome groups.

What this can't tell you

Median imputation assumes data are missing at random and does not recover information lost to missingness. If missingness is associated with outcome or with unmeasured factors, the imputed values may bias downstream inference. Consider reviewing the original missingness pattern if available.

Visualization

Variance Explained by Component

How much of the total variation each principal component captures (scree chart).

What this means

The short answer

The first component carries 26.2% of variation; the second adds 21.6%. The drop-off steepens after PC3 (12.9%), indicating that the dataset has moderate compressibility—no single dominant pattern, but clear structure in the top few axes.

The detail

PC1: 26.2%; PC2: 21.6%; PC3: 12.9%; PC4: 10.9%; PC5: 9.5%. Cumulative: 5 components reach 81.2%, crossing the 80% threshold. PC6 (8.5%) and PC7 (5.2%) add incrementally; PC8 (5.1%) closes the remaining variance. The visible drop-off between PC3 and PC4 suggests that meaningful signal concentrates in the first three components, with PC4 and PC5 capturing secondary structure and components beyond PC5 encoding mostly noise.

What this can't tell you

Variance explained does not measure whether the components are interpretable or clinically meaningful. High variance does not guarantee that a component relates to the outcome of interest.

Data Table

Component Summary

Eigenvalues and variance shares for the leading components.

ComponentEigenvalueVariance PCTCumulative PCT
PC12.09426.226.2
PC21.73121.647.8
PC31.0312.960.7
PC40.87610.971.6
PC50.7629.581.2
PC60.6838.589.7
PC70.425.294.9
PC80.4045.1100
What this means

The short answer

Three components pass the Kaiser criterion (eigenvalue > 1), but you need 5 components to capture 80% of the total variation. The first component is substantially stronger than the rest, explaining 26.2% alone, while the second and third contribute 21.6% and 12.9% respectively.

The detail

PC1 has eigenvalue 2.094 and variance 26.2%; PC2 has eigenvalue 1.731 and variance 21.6%; PC3 has eigenvalue 1.03 and variance 12.9%. All three exceed eigenvalue 1. PC4 (eigenvalue 0.876, variance 10.9%) and PC5 (eigenvalue 0.762, variance 9.5%) both fall below the Kaiser threshold but are needed to reach 81.2% cumulative variance. PC6 and PC7 together account for 13.7% more variance (cumulative 94.9%), while PC8 (eigenvalue 0.404, variance 5.1%) completes the set. The 80% threshold lands exactly at PC5 cumulative variance of 81.2%.

What this can't tell you

The Kaiser criterion and 80% threshold are statistical heuristics; your choice of how many components to retain should also consider the interpretability and practical utility of the remaining dimensions in your downstream analysis.

Visualization

Feature Contributions

Which features define the first two components, scaled 0-100.

What this means

The short answer

SkinThickness and Age dominate the main structure (contributions 100 and 99.5), followed by Insulin (89.9) and Pregnancies (86.7). The remaining four features contribute less to the dominant pattern, with DiabetesPedigreeFunction the weakest at 52.

The detail

Feature contributions to PC1 and PC2 combined are ranked: SkinThickness 100, Age 99.5, Insulin 89.9, Pregnancies 86.7, Glucose 75.2, BMI 75, BloodPressure 71.7, and DiabetesPedigreeFunction 52. The top two features (SkinThickness and Age) are tightly clustered and substantially ahead of the rest. The contribution metric weights each feature's loadings on the first two components by the variance those components explain, so it captures which columns define the dataset's main axes of variation. DiabetesPedigreeFunction varies mostly independently of the dominant pattern.

What this can't tell you

High contribution to PC1+PC2 does not mean a feature is clinically important—only that it aligns with the main statistical structure. Features with low contributions may still carry clinical signal on other dimensions (PC3–PC8).

Visualization

Observation Map (PC1 vs PC2)

Every observation positioned on the two dominant components.

What this means

The short answer

Observations spread across a loose cloud on the two dominant components (together 47.8% of variance), with no tight clustering or clear separation between outcome groups. The range spans from about −3.8 to 2.4 on PC1 and −2.2 to 3.3 on PC2, indicating substantial heterogeneity.

The detail

PC1 (26.2% variance) is driven by BMI and SkinThickness; PC2 (21.6% variance) is driven by Age. Observation scores range from −3.785 to 2.415 on PC1 and −2.183 to 3.296 on PC2, showing wide dispersion. The two outcome groups (labeled 0 and 1) are heavily overlapped across the plot, indicating that the dominant two components do not capture the main differences between groups. Observations are distributed throughout the space rather than forming distinct clusters, consistent with the moderate variance explained by PC1+PC2 (47.8%).

What this can't tell you

Heavy overlap between outcome groups on PC1–PC2 does not mean they are identical—only that their main differences lie on the remaining components (PC3–PC8). A more detailed analysis of group separation would require examining all 5 or 7 components needed for 80–90% variance.

Data Table

Component Loadings

How strongly each feature defines each leading component.

FeaturePc1Pc2Pc3
Pregnancies-0.1280.594-0.013
Glucose-0.3930.1740.468
BloodPressure-0.360.184-0.535
SkinThickness-0.44-0.332-0.238
Insulin-0.435-0.2510.337
BMI-0.452-0.101-0.362
DiabetesPedigreeFunction-0.271-0.1220.433
Age-0.1980.6210.075
What this means

The short answer

PC1 is defined by three features loading near −0.44 to −0.45: BMI (−0.452), SkinThickness (−0.44), and Insulin (−0.435). PC2 is defined by Age (0.621) and Pregnancies (0.594), both positive, indicating these age-related measures move together on the second axis.

The detail

PC1 loadings: BMI −0.452, SkinThickness −0.44, Insulin −0.435, BloodPressure −0.36, Glucose −0.393. These five features load similarly (all negative), suggesting they co-vary as a syndrome. PC2 loadings: Age 0.621, Pregnancies 0.594 (positive); SkinThickness −0.332, Insulin −0.251 (negative). Features with near-zero loadings on PC1 and PC2 (e.g., DiabetesPedigreeFunction on PC1: −0.271) carry information the dominant structure does not capture. PC3 is driven by BloodPressure (−0.535) and Glucose (0.468).

What this can't tell you

Loadings describe linear relationships. If clinical features interact nonlinearly or in domain-specific combinations, those patterns will not appear in the loadings. Naming components in clinical language requires domain expertise beyond the statistical loadings.

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

Principal Component Analysis — Structure Finder

Runs PCA on the numeric feature columns the user selects, revealing how many independent dimensions the data really has, which features move together, and how observations spread across the leading components.

Why This Method?

PCA is the standard first tool for understanding high-dimensional data: it compresses correlated features into a few independent components, quantifies how much signal each carries, and exposes the groups of features that vary together — before any modelling is attempted.

What This Analysis Covers

  • Variance explained per component (scree) + how many components you need
  • Component summary table (eigenvalues, cumulative variance)
  • Which features drive the leading components (loadings + contributions)
  • Observation map on the first two components (optional group coloring)

Standard Library

Platform standard-library module (LAT-1441): runs on ANY dataset via the semantic mapping {feature_1..feature_N, optional label}. All narrative is derived from the user's own column names and computed values.

suppressPackageStartupMessages(library(htmltools))
suppressPackageStartupMessages(library(jsonlite))
suppressPackageStartupMessages(library(plotly))
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: Discover mapped feature columns

initial_rows <- nrow(df)
  feat_cols <- grep("^feature_[0-9]+$", names(df), value = TRUE)
  feat_cols <- feat_cols[order(as.integer(sub("^feature_", "", feat_cols)))]
  if (length(feat_cols) < 2) {
    stop("column_mapping must map at least two feature columns(feature_1, feature_2)")
  }
  feature_names <- setNames(humanize_semantic(feat_cols, col_map), feat_cols)

  has_label <- "label" %in% names(df)
  label_name <- if (has_label) humanize_semantic("label", col_map) else ""

Step 2: Coerce features numeric (95% rule); impute median; drop unusable

dropped_features <- character(0)
  for (fc in feat_cols) {
    v <- df[[fc]]
    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[[fc]] <- conv
      } else {
        dropped_features <- c(dropped_features, fc)
        next
      }
    }
    v <- df[[fc]]
    med <- median(v, na.rm = TRUE)
    if (is.na(med)) { dropped_features <- c(dropped_features, fc); next }
    v[is.na(v)] <- med
    df[[fc]] <- v
    if (isTRUE(var(v) == 0) || is.na(var(v))) {
      dropped_features <- c(dropped_features, fc)
    }
  }
  used_features <- setdiff(feat_cols, dropped_features)
  if (length(used_features) < 2) {
    stop(paste0(
      "PCA needs at least two usable numeric feature columns; only ",
      length(used_features), " remained after cleaning. Excluded: ",
      paste(feature_names[dropped_features], collapse = ", ")
    ))
  }

  X <- df[, used_features, drop = FALSE]
  final_rows <- nrow(X)
  rows_removed <- initial_rows - final_rows
  if (final_rows < 10) {
    stop(sprintf("Only %d usable rows — PCA needs at least 10.", final_rows))
  }

Step 3: PCA on standardized features

pca <- prcomp(X, center = TRUE, scale. = TRUE)
  eig <- pca$sdev^2
  var_pct <- 100 * eig / sum(eig)
  cum_pct <- cumsum(var_pct)
  n_comp_all <- length(eig)
  n_show <- min(n_comp_all, 10)

  variance_df <- data.frame(
    component      = paste0("PC", seq_len(n_show)),
    variance_pct   = round(var_pct[seq_len(n_show)], 1),
    cumulative_pct = round(cum_pct[seq_len(n_show)], 1),
    stringsAsFactors = FALSE
  )
  components_df <- data.frame(
    component      = paste0("PC", seq_len(n_show)),
    eigenvalue     = round(eig[seq_len(n_show)], 3),
    variance_pct   = round(var_pct[seq_len(n_show)], 1),
    cumulative_pct = round(cum_pct[seq_len(n_show)], 1),
    stringsAsFactors = FALSE
  )

  n_for_80 <- which(cum_pct >= 80)[1]
  n_for_90 <- which(cum_pct >= 90)[1]
  n_kaiser <- sum(eig > 1)

Step 4: Loadings (signed, first 3 PCs) with humanized feature names

n_pcs_load <- min(3, n_comp_all)
  L <- pca$rotation[, seq_len(n_pcs_load), drop = FALSE]
  loadings_df <- data.frame(
    feature = unname(feature_names[used_features]),
    stringsAsFactors = FALSE
  )
  for (j in seq_len(n_pcs_load)) {
    loadings_df[[paste0("pc", j)]] <- round(L[, j], 3)
  }

Step 5: Feature contributions — |loading| weighted by PC1+PC2 variance, 0-100

w <- var_pct[seq_len(min(2, n_comp_all))]
  contrib_raw <- as.vector(abs(L[, 1]) * w[1] +
    (if (n_comp_all >= 2) abs(L[, 2]) * w[2] else 0))
  contributions_df <- data.frame(
    feature      = unname(feature_names[used_features]),
    contribution = round(100 * contrib_raw / max(contrib_raw), 1),
    stringsAsFactors = FALSE
  )
  contributions_df <- contributions_df[order(-contributions_df$contribution), , drop = FALSE]
  rownames(contributions_df) <- NULL

Step 6: Scores map (PC1 vs PC2), <=1000 sample; label always present

set.seed(42)
  sidx <- if (final_rows > 1000) sample(final_rows, 1000) else seq_len(final_rows)
  lab_vals <- if (has_label) {
    lv <- as.character(df$label)
    lv[is.na(lv) | trimws(lv) == ""] <- "Missing"
    tab <- sort(table(lv), decreasing = TRUE)
    if (length(tab) > 10) {
      keep <- names(tab)[1:10]
      lv[!(lv %in% keep)] <- "Other"
    }
    lv
  } else {
    rep("All rows", final_rows)
  }
  scores_df <- data.frame(
    pc1   = round(pca$x[sidx, 1], 3),
    pc2   = if (n_comp_all >= 2) round(pca$x[sidx, 2], 3) else 0,
    label = lab_vals[sidx],
    stringsAsFactors = FALSE
  )
  rownames(scores_df) <- NULL

Step 7: Narrative anchors

pc1_order <- order(-abs(L[, 1]))
  top_pc1_features <- unname(feature_names[used_features])[pc1_order][
    seq_len(min(3, length(used_features)))]
  pc2_order <- if (n_comp_all >= 2) order(-abs(L[, 2])) else pc1_order
  top_pc2_feature <- unname(feature_names[used_features])[pc2_order][1]

  metrics <- list(
    `Observations`         = final_rows,
    `Features Analysed`    = length(used_features),
    `PC1 Variance %`       = round(var_pct[1], 1),
    `Components for 80%`   = as.integer(n_for_80),
    `Kaiser Components`    = as.integer(n_kaiser),
    `Top PC1 Feature`      = top_pc1_features[1]
  )

  json_output <- list(
    answer = paste0(
      "PCA on ", length(used_features), " features across ",
      format(final_rows, big.mark = ","), " rows: PC1 explains ",
      round(var_pct[1], 1), "% of variance(",
      n_for_80, " component(s) reach 80%, Kaiser criterion keeps ",
      n_kaiser, "). PC1 is driven by ",
      paste(top_pc1_features, collapse = ", "), "."
    ),
    cards = lapply(
      c("tldr", "overview", "preprocessing", "variance_explained",
        "components_table", "feature_contributions", "scores_scatter",
        "loadings_table"),
      function(cid) list(id = cid, metrics = metrics)
    )
  )

  list(
    initial_rows      = initial_rows,
    final_rows        = final_rows,
    rows_removed      = rows_removed,
    feature_names     = feature_names,
    used_features     = used_features,
    dropped_features  = dropped_features,
    has_label         = has_label,
    label_name        = label_name,
    pca               = pca,
    variance_df       = variance_df,
    components_df     = components_df,
    loadings_df       = loadings_df,
    contributions_df  = contributions_df,
    scores_df         = scores_df,
    n_for_80          = n_for_80,
    n_for_90          = n_for_90,
    n_kaiser          = n_kaiser,
    top_pc1_features  = top_pc1_features,
    top_pc2_feature   = top_pc2_feature,
    var_pct           = var_pct,
    metrics           = metrics,
    json_output       = json_output
  )
}

Compute shared resources

shared <- compute_shared(df, params, col_map)

Finalize (do not modify)

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