--- title: "Which pros win the Mirrorball \u2014 and who lifts their celebrity?" subtitle: "Data with the Stars · An Artometrics project" date: 2026-11-03 format: html: toc: true embed-resources: true theme: cosmo pdf: documentclass: article geometry: margin=1in execute: echo: false warning: false message: false --- **Finding.** Derek Hough leads with 6 Mirrorball wins and +1.34 peer lift; Julianne Hough has the highest win rate among pros with 3+ seasons. ```{r} #| label: setup #| include: false source("R/_setup.R") ``` ## Charts ```{r} #| label: 02_pro_lift #| fig-cap: "02_pro_lift" #| echo: false # Rebuild chart with ggplot2 from bundled CSVs / story data.csv story_dir <- file.path(root, "public/downloads/stories", "02_pro_win_rates") df <- read_csv(file.path(story_dir, "data.csv"), show_col_types = FALSE) # Prefer a pre-defined plot expression file if present; else smart default plot_r <- file.path(root, "reports/qmd/R/plots", "02_pro_lift.R") if (file.exists(plot_r)) { source(plot_r, local = TRUE) # expects object `p` } else { # Default: if typical named mean columns, bar them num_cols <- names(df)[sapply(df, is.numeric)] chr_cols <- names(df)[sapply(df, function(z) is.character(z) || is.factor(z))] ycol <- intersect(c("mean","wins","delta","boost","composite","accuracy","n","perfects","share","lift","pro_mean","resid","streak","guest_mean","field_mean","pts"), num_cols) ycol <- if (length(ycol)) ycol[[1]] else num_cols[[1]] xcol <- intersect(c("dance_style","pro","celebrity","judge","bucket","theme_key","hometown_state","checkpoint","label","song","professional","role","era","bin","metric"), chr_cols) xcol <- if (length(xcol)) xcol[[1]] else if (length(chr_cols)) chr_cols[[1]] else NULL if (!is.null(xcol)) { dd <- df if (nrow(dd) > 20) dd <- dd[1:20,] dd[[xcol]] <- as.character(dd[[xcol]]) dd <- dd[!duplicated(dd[[xcol]]), , drop = FALSE] dd[[xcol]] <- factor(dd[[xcol]], levels = rev(unique(dd[[xcol]]))) p <- ggplot(dd, aes(.data[[ycol]], .data[[xcol]], fill = .data[[ycol]])) + geom_col() + scale_fill_gradient(low = "#A8DADC", high = "#6B4CE6") + labs(title = "02_pro_lift", x = ycol, y = NULL) + theme_dwts() + theme(legend.position = "none") } else { p <- ggplot(df, aes(.data[[num_cols[[1]]]])) + geom_histogram(bins = 30, fill = "#6B4CE6", color = "white") + labs(title = "02_pro_lift") + theme_dwts() } } save_both(p, "02_pro_lift") p ``` [PNG](charts/02_pro_lift.png){download="02_pro_lift.png"} · [SVG](charts/02_pro_lift.svg){download="02_pro_lift.svg"} ```{r} #| label: 02_pro_scatter #| fig-cap: "02_pro_scatter" #| echo: false # Rebuild chart with ggplot2 from bundled CSVs / story data.csv story_dir <- file.path(root, "public/downloads/stories", "02_pro_win_rates") df <- read_csv(file.path(story_dir, "data.csv"), show_col_types = FALSE) # Prefer a pre-defined plot expression file if present; else smart default plot_r <- file.path(root, "reports/qmd/R/plots", "02_pro_scatter.R") if (file.exists(plot_r)) { source(plot_r, local = TRUE) # expects object `p` } else { # Default: if typical named mean columns, bar them num_cols <- names(df)[sapply(df, is.numeric)] chr_cols <- names(df)[sapply(df, function(z) is.character(z) || is.factor(z))] ycol <- intersect(c("mean","wins","delta","boost","composite","accuracy","n","perfects","share","lift","pro_mean","resid","streak","guest_mean","field_mean","pts"), num_cols) ycol <- if (length(ycol)) ycol[[1]] else num_cols[[1]] xcol <- intersect(c("dance_style","pro","celebrity","judge","bucket","theme_key","hometown_state","checkpoint","label","song","professional","role","era","bin","metric"), chr_cols) xcol <- if (length(xcol)) xcol[[1]] else if (length(chr_cols)) chr_cols[[1]] else NULL if (!is.null(xcol)) { dd <- df if (nrow(dd) > 20) dd <- dd[1:20,] dd[[xcol]] <- as.character(dd[[xcol]]) dd <- dd[!duplicated(dd[[xcol]]), , drop = FALSE] dd[[xcol]] <- factor(dd[[xcol]], levels = rev(unique(dd[[xcol]]))) p <- ggplot(dd, aes(.data[[ycol]], .data[[xcol]], fill = .data[[ycol]])) + geom_col() + scale_fill_gradient(low = "#A8DADC", high = "#6B4CE6") + labs(title = "02_pro_scatter", x = ycol, y = NULL) + theme_dwts() + theme(legend.position = "none") } else { p <- ggplot(df, aes(.data[[num_cols[[1]]]])) + geom_histogram(bins = 30, fill = "#6B4CE6", color = "white") + labs(title = "02_pro_scatter") + theme_dwts() } } save_both(p, "02_pro_scatter") p ``` [PNG](charts/02_pro_scatter.png){download="02_pro_scatter.png"} · [SVG](charts/02_pro_scatter.svg){download="02_pro_scatter.svg"} ```{r} #| label: 02_pro_wins #| fig-cap: "02_pro_wins" #| echo: false # Rebuild chart with ggplot2 from bundled CSVs / story data.csv story_dir <- file.path(root, "public/downloads/stories", "02_pro_win_rates") df <- read_csv(file.path(story_dir, "data.csv"), show_col_types = FALSE) # Prefer a pre-defined plot expression file if present; else smart default plot_r <- file.path(root, "reports/qmd/R/plots", "02_pro_wins.R") if (file.exists(plot_r)) { source(plot_r, local = TRUE) # expects object `p` } else { # Default: if typical named mean columns, bar them num_cols <- names(df)[sapply(df, is.numeric)] chr_cols <- names(df)[sapply(df, function(z) is.character(z) || is.factor(z))] ycol <- intersect(c("mean","wins","delta","boost","composite","accuracy","n","perfects","share","lift","pro_mean","resid","streak","guest_mean","field_mean","pts"), num_cols) ycol <- if (length(ycol)) ycol[[1]] else num_cols[[1]] xcol <- intersect(c("dance_style","pro","celebrity","judge","bucket","theme_key","hometown_state","checkpoint","label","song","professional","role","era","bin","metric"), chr_cols) xcol <- if (length(xcol)) xcol[[1]] else if (length(chr_cols)) chr_cols[[1]] else NULL if (!is.null(xcol)) { dd <- df if (nrow(dd) > 20) dd <- dd[1:20,] dd[[xcol]] <- as.character(dd[[xcol]]) dd <- dd[!duplicated(dd[[xcol]]), , drop = FALSE] dd[[xcol]] <- factor(dd[[xcol]], levels = rev(unique(dd[[xcol]]))) p <- ggplot(dd, aes(.data[[ycol]], .data[[xcol]], fill = .data[[ycol]])) + geom_col() + scale_fill_gradient(low = "#A8DADC", high = "#6B4CE6") + labs(title = "02_pro_wins", x = ycol, y = NULL) + theme_dwts() + theme(legend.position = "none") } else { p <- ggplot(df, aes(.data[[num_cols[[1]]]])) + geom_histogram(bins = 30, fill = "#6B4CE6", color = "white") + labs(title = "02_pro_wins") + theme_dwts() } } save_both(p, "02_pro_wins") p ``` [PNG](charts/02_pro_wins.png){download="02_pro_wins.png"} · [SVG](charts/02_pro_wins.svg){download="02_pro_wins.svg"} ## Data Analysis CSV: [data.csv](../../public/downloads/stories/02_pro_win_rates/data.csv){download="02_pro_win_rates-data.csv"} Source tables: `performances.csv`, `scores.csv`, `contestants.csv`, `seasons.csv` under `/downloads/`.