Executive Summary
The 80/20 split across 11,493 categories
Video game sales are highly concentrated: just 2,374 titles (20.7 percent of 11,493) drive 80 percent of total Global Sales, while Wii Sports alone accounts for 0.93 percent. The top 3 titles together hold 2.1 percent of the total, and the overall Gini coefficient is 0.755—indicating strong concentration. Effort directed at the A-class vital few will move the largest share of the total.
Analysis Overview
Pareto analysis of 11,493 Name categories across 16,598 rows.
This analysis applies Pareto's principle to rank 11,493 video game titles by Global Sales and identify the vital few that drive most revenue. The method totals sales by title, ranks from largest to smallest, and follows the cumulative share down the list. Here, 2,374 of the 11,493 titles (20.7 percent) together account for 80 percent of total Global Sales. The remaining 9,119 titles (79.3 percent) supply only 20 percent. This split is the foundation for ABC classification: A-class titles (the vital few) warrant tight management; B and C classes benefit from batching or bulk handling.
Data Quality
Rows, categories, negatives handling, and the chart rollup.
The analysis ingested 16,598 rows across 11,493 Name categories, with a grand total of 8,920 in Global Sales. All 16,598 rows were retained; no records were removed for blanks or missing values, and no negative values were present. For the Pareto chart visualization, the top 20 titles are displayed individually while the remaining 11,473 titles are pooled into an "Other" bar. This rollup is visual only; every title still counts toward the vital-few threshold and ABC class assignments. The concentration metrics and rankings reflect the full dataset.
Pareto Chart
Ranked category totals with the cumulative share.
The short answer
The top titles fall away sharply: Wii Sports leads at 0.93 percent of total sales, and the cumulative share flattens quickly, crossing 80 percent only at the 2,374th ranked title. This steep drop is the signature of extreme concentration.
The detail
Wii Sports holds 82.74 in Global Sales (0.93 percent of the total), followed by Grand Theft Auto V at 55.92 (1.55 percent cumulative). The top 20 titles shown reach only 7.62 percent cumulatively. The chart's rapid descent and flat tail illustrate the Pareto principle: the ranked bars drop from 82.74 down to 23.86 across just the first 20 categories, then the remaining 11,473 categories pool into "Other" at 8,240.42. The cumulative share crosses 80 percent at the 2,374th category.
What this can't tell you
The chart shows only the top 20 named titles; the detailed shape of the middle and tail ranks is pooled into "Other." A full ranked list would reveal whether concentration is driven by a handful of blockbusters or distributed across a wider middle class.
ABC Classes
Category counts and value share for the A, B, and C classes.
| Class | Categories | Value | Value Share PCT | Description |
|---|---|---|---|---|
| A | 2374 | 7,136 | 80 | Vital few - the top categories that together reach about 80 percent of the total. |
| B | 3243 | 1,338 | 15 | Useful many - the next categories carrying the 80 to 95 percent band. |
| C | 5876 | 446 | 5 | Trivial many - the long tail below the last 5 percent. |
The A-class vital few comprises 2,374 titles holding 7,136 in Global Sales (80 percent of the total). Class B—the useful many—contains 3,243 titles with 1,338 in sales (15 percent), bridging the 80 to 95 percent band. Class C, the trivial many, includes 5,876 titles generating 446 in sales (5 percent). The decisive split is A: management effort here moves 80 percent of revenue. The 9,119 titles outside class A (B and C combined) supply only 20 percent, so batching or bulk handling is appropriate for those categories.
Top Contributors
The highest-value categories ranked, with cumulative share.
| Rank | Category | Value | Share PCT | Cumulative PCT |
|---|---|---|---|---|
| 1 | Wii Sports | 83 | 0.93 | 0.93 |
| 2 | Grand Theft Auto V | 56 | 0.63 | 1.55 |
| 3 | Super Mario Bros. | 45 | 0.51 | 2.06 |
| 4 | Tetris | 36 | 0.4 | 2.46 |
| 5 | Mario Kart Wii | 36 | 0.4 | 2.87 |
| 6 | Wii Sports Resort | 33 | 0.37 | 3.24 |
| 7 | Pokemon Red/Pokemon Blue | 31 | 0.35 | 3.59 |
| 8 | Call of Duty: Black Ops | 31 | 0.35 | 3.94 |
| 9 | Call of Duty: Modern Warfare 3 | 31 | 0.35 | 4.28 |
| 10 | New Super Mario Bros. | 30 | 0.34 | 4.62 |
| 11 | Call of Duty: Black Ops II | 30 | 0.33 | 4.95 |
| 12 | Wii Play | 29 | 0.33 | 5.28 |
| 13 | New Super Mario Bros. Wii | 29 | 0.32 | 5.6 |
| 14 | Duck Hunt | 28 | 0.32 | 5.91 |
| 15 | Call of Duty: Ghosts | 27 | 0.31 | 6.22 |
Wii Sports ranks first with 83 in Global Sales and a 0.93 percent share. Grand Theft Auto V is second at 56 and 0.63 percent, followed by Super Mario Bros. at 45 and 0.51 percent. By rank 15 (Call of Duty: Ghosts at 27 and 0.31 percent), the cumulative share reaches 6.22 percent. The vital-few line (80 percent) is crossed at the 2,374th title, meaning the top 2,374 titles span from Wii Sports down to titles with individually negligible shares. This ranking is the concrete priority list: the first 2,374 rows are the focus; beyond that, individual title performance becomes marginal to overall sales.
Pareto Analysis — The Vital Few (80/20)
Finds the few categories that drive most of the total. Aggregates a raw category/value table, sorts the categories from biggest to smallest, builds the cumulative curve, and reports the "vital few" that reach 80% of the total, the ABC classes, and how concentrated the total is.
Why This Method?
The Pareto principle — roughly 80% of effects come from 20% of causes — is the workhorse of Lean and Six Sigma. It turns a long, flat list of defects, complaints, SKUs, or revenue lines into a ranked priority list: fix the vital few and you move most of the total. It needs no model, only a category and an amount.
What This Analysis Covers
- The vital few: the smallest set of categories reaching 80% of the total
- ABC classes (A up to 80%, B 80-95%, C the tail)
- The Pareto chart (ranked bars with the cumulative share)
- Concentration: the top category and top-three shares
Standard Library
Platform standard-library module (LAT-1441): runs on ANY dataset via the semantic mapping {category, value}. All narrative is derived from the user's own column names and computed values.
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))Core Analysis Pipeline
Number formatter — plain rounded strings with thousands separators.
Table numerics are formatted through this so large totals never serialize in scientific notation (e.g. 2.76e+04) inside a rendered table.
fmt_num <- function(x, digits = 0) {
format(round(as.numeric(x), digits), big.mark = ",", scientific = FALSE,
trim = TRUE, nsmall = digits)
}Gini concentration (0 = every category equal, 1 = one holds it all).
Defensive: needs a positive total and >=2 categories, else returns NA.
gini_concentration <- function(v) {
v <- v[!is.na(v)]
n <- length(v)
s <- sum(v)
if (n < 2 || s <= 0) return(NA_real_)
vs <- sort(v)
(2 * sum(seq_len(n) * vs) / (n * s)) - (n + 1) / n
}
compute_shared <- function(df, params, col_map = list()) {
# === SHARED EXPORTS ===
# initial_rows/final_rows/rows_removed $ row accounting
# n_missing_cat / n_missing_value / n_negative / n_zero $ preprocessing counts
# negative_total $ numeric — summed value of dropped negatives
# cat_name / value_name $ humanized user column names
# agg_df $ data.frame(category, value, share_pct,
# cumulative, cumulative_pct, rank, abc) sorted desc
# n_categories / total_value $ integer / numeric
# vital_few_n / vital_few_cat_share / vital_few_value_share
# top_category / top_share / top3_share $ concentration headline
# gini $ numeric — Gini concentration
# abc_summary_df $ data.frame(class, categories, value,
# value_share_pct, description)
# n_a / n_b / n_c $ integer — ABC class sizes
# pareto_df $ data.frame(category, value, cumulative_pct) top 20 + Other
# top_contributors_df $ data.frame(rank, category, value, share_pct, cumulative_pct)
# metrics / json_output
# === /SHARED EXPORTS ===Step 1: Required semantic columns + humanized names
initial_rows <- nrow(df)
cat_name <- humanize_semantic("category", col_map)[1]
value_name <- humanize_semantic("value", col_map)[1]
for (need in c("category", "value")) {
if (!need %in% names(df)) {
stop(sprintf("Required column '%s' is not mapped.",
humanize_semantic(need, col_map)[1]))
}
}Step 2: Category — character, drop blank/missing
cat_v <- trimws(as.character(df$category))
keep_cat <- !is.na(cat_v) & cat_v != ""
n_missing_cat <- sum(!keep_cat)
cat_v <- cat_v[keep_cat]
val <- df$value[keep_cat]Step 3: Value — 95% numeric coercion rule; drop NA
if (!is.numeric(val)) {
conv <- suppressWarnings(as.numeric(as.character(val)))
n_orig <- sum(!is.na(val) & trimws(as.character(val)) != "")
if (n_orig == 0 || sum(!is.na(conv)) < 0.95 * n_orig) {
stop(sprintf(
"Column '%s' is not numeric enough to total up — fewer than 95%% of its values could be read as numbers.",
value_name))
}
val <- conv
}
keep_val <- !is.na(val)
n_missing_value <- sum(!keep_val)
cat_v <- cat_v[keep_val]
val <- val[keep_val]Step 4: Drop negatives (Pareto assumes non-negative contributions),
keep zeros. Count and total the dropped negatives for the narrative.
neg_mask <- val < 0
n_negative <- sum(neg_mask)
negative_total <- if (n_negative > 0) sum(val[neg_mask]) else 0
cat_v <- cat_v[!neg_mask]
val <- val[!neg_mask]
n_zero <- sum(val == 0)
final_rows <- length(val)
rows_removed <- initial_rows - final_rows
if (final_rows < 5) {
stop(sprintf(
"Only %d usable rows remain after cleaning '%s' and '%s' — need at least 5.",
final_rows, cat_name, value_name))
}Step 5: Aggregate (sum) by category, sort biggest to smallest
agg <- data.frame(category = cat_v, value = val, stringsAsFactors = FALSE) %>%
group_by(category) %>%
summarise(value = sum(value), .groups = "drop") %>%
as.data.frame(stringsAsFactors = FALSE)
agg <- agg[order(-agg$value), , drop = FALSE]
rownames(agg) <- NULL
n_categories <- nrow(agg)
total_value <- sum(agg$value)
if (total_value <= 0) {
stop(sprintf(
"The total of '%s' across all categories is zero, so there is no share to distribute.",
value_name))
}Step 6: Shares + cumulative curve
agg$share_pct <- round(100 * agg$value / total_value, 2)
agg$cumulative <- cumsum(agg$value)
agg$cumulative_pct <- round(100 * agg$cumulative / total_value, 2)
agg$rank <- seq_len(n_categories)Step 7: The vital few — smallest prefix reaching 80% cumulative.
cumulative_pct is monotonic and its last value is 100, so a match always exists; which.max returns the first TRUE (guarded fallback regardless).
reach80 <- agg$cumulative_pct >= 80
vital_few_n <- if (any(reach80)) which.max(reach80) else n_categories
vital_few_cat_share <- round(100 * vital_few_n / n_categories, 1)
vital_few_value_share <- round(agg$cumulative_pct[vital_few_n], 1)Step 8: ABC classes (A up to 80%, B 80-95%, C the tail)
reach95 <- agg$cumulative_pct >= 95
b_end <- if (any(reach95)) max(which.max(reach95), vital_few_n) else n_categories
abc <- rep("C", n_categories)
abc[seq_len(vital_few_n)] <- "A"
if (b_end > vital_few_n) abc[(vital_few_n + 1):b_end] <- "B"
agg$abc <- abc
n_a <- sum(abc == "A"); n_b <- sum(abc == "B"); n_c <- sum(abc == "C")
abc_desc <- c(
A = "Vital few - the top categories that together reach about 80 percent of the total.",
B = "Useful many - the next categories carrying the 80 to 95 percent band.",
C = "Trivial many - the long tail below the last 5 percent."
)
abc_summary_df <- do.call(rbind, lapply(c("A", "B", "C"), function(cl) {
idx <- abc == cl
data.frame(
class = cl,
categories = sum(idx),
value = fmt_num(sum(agg$value[idx]), 0),
value_share_pct = round(100 * sum(agg$value[idx]) / total_value, 1),
description = unname(abc_desc[cl]),
stringsAsFactors = FALSE
)
}))
rownames(abc_summary_df) <- NULLStep 9: Concentration headline
top_category <- agg$category[1]
top_share <- agg$share_pct[1]
top3_n <- min(3, n_categories)
top3_share <- round(sum(agg$share_pct[seq_len(top3_n)]), 1)
gini <- round(gini_concentration(agg$value), 3)Step 10: Pareto chart data — top 20 categories, rest rolled into
"Other" (chart only; the vital-few and ABC counts use the FULL data).
cap <- 20
if (n_categories > cap) {
head_df <- agg[seq_len(cap), c("category", "value", "cumulative_pct")]
other_value <- total_value - sum(agg$value[seq_len(cap)])
pareto_df <- rbind(
head_df,
data.frame(category = "Other", value = round(other_value, 2),
cumulative_pct = 100, stringsAsFactors = FALSE)
)
} else {
pareto_df <- agg[, c("category", "value", "cumulative_pct")]
}
pareto_df$value <- round(pareto_df$value, 2)
rownames(pareto_df) <- NULLStep 11: Top contributors table — top 15, values as plain strings
tc_n <- min(15, n_categories)
top_contributors_df <- data.frame(
rank = agg$rank[seq_len(tc_n)],
category = agg$category[seq_len(tc_n)],
value = fmt_num(agg$value[seq_len(tc_n)], 0),
share_pct = agg$share_pct[seq_len(tc_n)],
cumulative_pct = agg$cumulative_pct[seq_len(tc_n)],
stringsAsFactors = FALSE
)
rownames(top_contributors_df) <- NULL
metrics <- list(
`Categories` = n_categories,
`Vital Few(80%)` = vital_few_n,
`Vital Few Share %` = vital_few_cat_share,
`Top Category Share %` = top_share,
`Top 3 Share %` = top3_share,
`Concentration(Gini)` = gini
)
json_output <- list(
answer = paste0(
"Pareto analysis of ", format(n_categories, big.mark = ","),
" categories from ", format(final_rows, big.mark = ","), " rows: ",
"the vital few is ", vital_few_n, " ", cat_name,
if (vital_few_n == 1) " category" else " categories", " (",
vital_few_cat_share, "% of them) driving ", vital_few_value_share,
"% of total ", value_name, ". The single biggest is ", top_category,
" at ", top_share, "%; the top ", top3_n, " together hold ", top3_share,
"%. ABC split: A = ", n_a, ", B = ", n_b, ", C = ", n_c, " categories."
),
cards = lapply(
c("tldr", "overview", "preprocessing", "pareto_chart",
"abc_classes", "top_contributors"),
function(cid) list(id = cid, metrics = metrics)
)
)
list(
initial_rows = initial_rows, final_rows = final_rows,
rows_removed = rows_removed,
n_missing_cat = n_missing_cat, n_missing_value = n_missing_value,
n_negative = n_negative, negative_total = negative_total, n_zero = n_zero,
cat_name = cat_name, value_name = value_name,
agg_df = agg, n_categories = n_categories, total_value = total_value,
vital_few_n = vital_few_n, vital_few_cat_share = vital_few_cat_share,
vital_few_value_share = vital_few_value_share,
top_category = top_category, top_share = top_share, top3_share = top3_share,
top3_n = top3_n, gini = gini,
abc_summary_df = abc_summary_df, n_a = n_a, n_b = n_b, n_c = n_c,
pareto_df = pareto_df, top_contributors_df = top_contributors_df,
metrics = metrics, json_output = json_output
)
}