--- title: "Analyzing Ultramarathon Race Performances example" author: "A. Ghosh, C. Agostinelli and A. Basu" date: "`r Sys.Date()`" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{mvogammaDPD examples} %\VignetteEngine{knitr::knitr} %\VignetteEncoding{UTF-8} --- ```{r, echo = FALSE} knitr::opts_chunk$set( fig.width = 8 , fig.height = 12, fig.align ='center' ) ``` # Introduction This file contains an example of the function ```mvogammaDPD```. It reproduces all the figures and tables in section "Application: Analyzing Ultramarathon Race Performances" of A. Ghosh, C. Agostinelli and A. Basu (2026) A Composite Divergence Approach to Robust Multivariate Estimation under Cellwise and Casewise Contamination, arXiv:2608.18914, https://arxiv.org/abs/2608.18914. The dataset was obtained from the publicly available data on the website [www.kaggle.com/datasets/fatihyavuzz/two-centuries-of-um-races](https://www.kaggle.com/datasets/fatihyavuzz/two-centuries-of-um-races) which contains ace results from ultramarathon events held worldwide over approximately two centuries. We restrict to mens' 50-mile races. For each race event (identified by the event name and date), finishing times were converted to hours and the ten fastest finishers were retained, producing one ordered $10$-dimensional observation per race. Events with fewer than ten recorded finishers or with tied finishing times among the top ten were discarded, yielding a collection of complete ordered observations of 4683 races. ```{r} library("mvdpd") library("ggplot2") library("reshape2") library(tidyr) library(knitr) ``` ## Load data and prepare datasets ```{r} data("twocenturies50") ordered_data <- twocenturies50[order(twocenturies50[,1]),] n <- nrow(twocenturies50) p <- ncol(twocenturies50) ``` ### Best 200 races in term of the first finisher (clean data?) ```{r} best0 <- ordered_data[1:200,] best0 <- best0/3600 ``` ### Casewise contaminated data ```{r} best1 <- ordered_data[c(1:180, (n-19):n),] best1 <- best1/3600 ``` ### Cellwise contaminated data ```{r} best2 <- ordered_data[1:200,] set.seed(1234) for (i in 1:200) { nn <- rbinom(n=1, size=10, prob=0.1) if (nn){ pos <- sample(1:p, size=nn) best2[i,pos] <- runif(1, 0, 3)*best0[i,pos] best2[i,] <- sort(best2[i,]) } } best2 <- best2/3600 ``` ## PLot Heatmaps of data in common scale ```{r} min_val <- min(best0, best1, best2) max_val <- max(best0, best1, best2) heatplot <- function(mat) { df <- melt(mat) colnames(df) <- c("Event", "FinishingTime", "Hours") df$FinishingTime <- as.numeric(as.factor(df$FinishingTime)) ggplot(df, aes(FinishingTime, Event, fill = Hours)) + geom_tile() + scale_x_continuous( breaks = 1:10, labels = 1:10, expand = expansion(mult = c(0.01, 0.01)) ) + scale_y_discrete( expand = expansion(mult = c(0.03, 0.03)) ) + scale_fill_gradient2( low = "blue", mid = "white", high = "red", midpoint = (min_val + max_val) / 2, limits = c(min_val, max_val), breaks = round(seq(min_val, max_val, length.out = 5),0) ) + labs( x = "Top 10 Finishing Times", y = "Events", fill = "Hours" ) + theme_classic(base_size = 14) } ``` ### Figure 5 ```{r, fig=TRUE} heatplot(best0) ``` ```{r, fig=TRUE} heatplot(best1) ``` ```{r, fig=TRUE} heatplot(best2) ``` ## Estimation of parametrs ```{r} perform.analysis <- function(x) { # Maximum likelihood ml <- mvogammaML(x=x, method="multivariate", initial=list(delta=c(18, rep(0.5, 9)), lambda=5)) # Maximum composite likelihood (minimum CDPD with beta=0) cml <- mvogammaML(x=x, method="composite", initial=list(delta=ml$delta, lambda=ml$lambda)) # Minimum composite DPD cdpd1 <- mvogammaDPD(x=x, beta=0.1, method="composite", initial=list(delta=ml$delta, lambda=ml$lambda)) cdpd2 <- mvogammaDPD(x=x, beta=0.2, method="composite", initial=list(delta=ml$delta, lambda=ml$lambda)) cdpd3 <- mvogammaDPD(x=x, beta=0.3, method="composite", initial=list(delta=ml$delta, lambda=ml$lambda)) cdpd5 <- mvogammaDPD(x=x, beta=0.5, method="composite", initial=list(delta=ml$delta, lambda=ml$lambda)) # Minimum (multivariate) DPD mdpd1 <- mvogammaDPD(x=x, beta=0.1, method="multivariate", initial=list(delta=ml$delta, lambda=ml$lambda)) mdpd2 <- mvogammaDPD(x=x, beta=0.2, method="multivariate", initial=list(delta=ml$delta, lambda=ml$lambda)) mdpd3 <- mvogammaDPD(x=x, beta=0.3, method="multivariate", initial=list(delta=ml$delta, lambda=ml$lambda)) mdpd5 <- mvogammaDPD(x=x, beta=0.5, method="multivariate", initial=list(delta=ml$delta, lambda=ml$lambda)) results.multivariate <- cbind( c(ml$delta, ml$lambda), c(mdpd1$delta, mdpd1$lambda), c(mdpd2$delta, mdpd2$lambda), c(mdpd3$delta, mdpd3$lambda), c(mdpd5$delta, mdpd5$lambda) ) results.composite <- cbind( c(cml$delta, cml$lambda), c(cdpd1$delta, cdpd1$lambda), c(cdpd2$delta, cdpd2$lambda), c(cdpd3$delta, cdpd3$lambda), c(cdpd5$delta, cdpd5$lambda) ) colnames(results.multivariate) <- c("ML", "DPD(0.1)", "DPD(0.2)", "DPD(0.3)", "DPD(0.5)") colnames(results.composite) <- c("CML", "CDPD(0.1)", "CDPD(0.2)", "CDPD(0.3)", "CDPD(0.5)") rownames(results.multivariate) <- c(paste0("delta", 1:10), "lambda") rownames(results.composite) <- rownames(results.multivariate) res <- list(MDPDE=results.multivariate, MCDPDE=results.composite) return(res) } ``` ```{r} results0 <- perform.analysis(best0) results1 <- perform.analysis(best1) results2 <- perform.analysis(best2) ``` ### Figure 5 ### Compute absolute relative changes (in %) ```{r} casewiseCDPD <- 100*abs(results1$MCDPDE-results0$MCDPDE)/results0$MCDPDE cellwiseCDPD <- 100*abs(results2$MCDPDE-results0$MCDPDE)/results0$MCDPDE ``` ### Grouped Bar Plot for relative changes ```{r} # Define the methods (X-axis categories) in the exact order from the image methods <- c("ML", "CML", "DPD(0.1)", "DPD(0.2)", "DPD(0.3)", "DPD(0.5)", "CDPD(0.1)", "CDPD(0.2)", "CDPD(0.3)", "CDPD(0.5)") mybarplot <- function(X1, X2) { # Create a data frame with the extracted percentages df <- data.frame(Method = factor(methods, levels = methods), Row_1 = X1, Row_2 = X2) # Convert data to long format for ggplot2 df_long <- pivot_longer(df, cols = c(Row_1, Row_2), names_to = "Parameter", values_to = "Percentage") # Create the Plot p <- ggplot(df_long, aes(x = Method, y = Percentage, fill = Parameter)) + # Add grouped bars with black outlines # Matching position_dodge width and bar width removes the inner gap geom_bar( stat = "identity", position = position_dodge(width = 0.8), # Changed from 0.85 color = "black", width = 0.8, # Changed from 0.75 to match dodge width linewidth = 0.6 ) + # Add data labels on top of the bars geom_text( aes(label = sprintf("%.0f", Percentage)), position = position_dodge(width = 0.8), # Must match the dodge width in geom_bar vjust = -0.5, size = 4, color = "black" ) + # Map colors and use mathematical expressions for the legend labels (lambda and delta) scale_fill_manual( values = c("Row_1" = "#4C72B0", "Row_2" = "#DD8452"), labels = c(expression(lambda), expression(delta[1])) ) + # Configure Y-axis: force origin at 0 and set max limit slightly above data scale_y_continuous( limits = c(0, max(df_long$Percentage) * 1.15), breaks = seq(0, 100, 20), expand = expansion(mult = c(0, 0)) ) + # Axis and Legend Labels labs( x = "Estimation Method", y = "Absolute relative changes (in %)", fill = "Parameter" ) + # Theme Modifications theme_classic(base_size = 14) + theme( # Axis tick values bold and larger axis.text.x = element_text(angle = 45, hjust = 1, color = "black", size = 14, face = "bold"), axis.text.y = element_text(color = "black", size = 14, face = "bold"), # Axis titles axis.title = element_text(face = "bold", size = 16), axis.line = element_line(color = "black", linewidth = 0.7), axis.ticks = element_line(color = "black", linewidth = 0.7), # Legend formatting legend.position = c(0.88, 0.88), legend.background = element_rect(fill = "white", color = NA), legend.box.background = element_rect(color = "black", linewidth = 0.5), legend.title = element_text(face = "bold", size = 14), legend.text = element_text(size = 15), # Plot margins plot.margin = margin(t = 15, r = 15, b = 10, l = 10) ) # Display the plot print(p) } ``` ### Casewise contamination ```{r, fig=TRUE} mybarplot(casewiseCDPD[11,], casewiseCDPD[1,]) ``` ### Cellwise contamination ```{r, fig=TRUE} mybarplot(cellwiseCDPD[11,], cellwiseCDPD[1,]) ``` ### Table S3 ### Clean data ```{r} kable(cbind(results0$MDPDE, results0$MCDPDE)) ``` ### Casewise contamination ```{r} kable(cbind(results1$MDPDE, results1$MCDPDE)) ``` ### Cellwise contamination ```{r} kable(cbind(results2$MDPDE, results2$MCDPDE)) ```