I decided I’d make the ranking table I created in my last post into an interactive display so that you can play around with what’s included and how it’s weighted yourself.
The dashboard (below) can be pretty slow to load. If it doesn’t load after 30s your browser is probably blocking it, so you may want to check the browser console.
I made this using the Shinylive package and extension for Quarto. It’s far from optimal to be honest, but I’m happy I eventually got this to work.
#| '!! shinylive warning !!': |
#| shinylive does not work in self-contained HTML documents.
#| Please set `embed-resources: false` in your metadata.
#| standalone: true
#| viewerHeight: 1200
library(shiny)
library(DT)
df_z <- structure(list(Country = c("Albania", "Argentina", "Armenia",
"Australia", "Austria", "Bangladesh", "Belgium", "Brazil", "Bulgaria",
"Canada", "Chile", "China", "Colombia", "Costa Rica", "Ivory Coast",
"Croatia", "Cyprus", "Czech Republic", "Denmark", "Ecuador",
"Egypt", "El Salvador", "Estonia", "Finland", "France", "Georgia",
"Germany", "Greece", "Honduras", "Hungary", "India", "Indonesia",
"Ireland", "Israel", "Italy", "Kazakhstan", "Latvia", "Lithuania",
"Malaysia", "Mexico", "Moldova", "Netherlands", "Norway", "Pakistan",
"Panama", "Paraguay", "Peru", "Poland", "Portugal", "Romania",
"Serbia", "Slovakia", "Slovenia", "Spain", "Sweden", "Switzerland",
"Thailand", "Ukraine", "United Kingdom", "United States", "Uruguay",
"Vietnam"), Debt.percent.GDP.Z = c(-0.124629294124465, -0.881652686284333,
0.0732425727324507, 0.234333820614628, -0.648071817139399, 0.698717587016083,
-1.189808193211, -0.891279357075537, 2.59589451107479, -1.18562458139787,
0.759139114985335, -0.927030348729619, 0.0178488559365616, -0.218659431299363,
-0.113325293231668, -0.1797159471746, -0.528068192422933, 0.471712089085369,
1.77230728199557, -0.0671086636699418, -1.06132881976772, -0.846209476478951,
2.66530947703202, -0.784241325720259, -1.27705968940722, 0.87962217146127,
-0.322322976182618, -1.71882947547772, 0.500542665863607, -0.601105362317265,
-0.807400085518897, 0.843282550619476, 0.711953810760354, -0.432603905999444,
-1.54478159249432, 2.46284427702149, 0.584223230705835, 1.08199849496694,
-0.405972040242114, -0.0313552549403581, 0.993501598495438, 0.329990120902357,
0.939422947318687, -0.557328887549805, 0.0304344065041883, 0.590373916494153,
1.3671434214963, -0.0110712590395305, -1.03363857344817, 0.0775975502594567,
0.258906061457534, -0.172505766040318, -0.43844141127366, -1.20770878775751,
1.09857333974231, 1.04122309403835, -0.347717499373564, -1.04840064431428,
-1.14412403298648, -1.38742970756526, -0.263187286942041, 1.31959869801759
), Economic.Complexity.Index.Z = c(-1.04740010189708, -0.959787575953646,
-1.10021074875286, -1.19896552417594, 1.65395957971693, -1.40579737753144,
0.634266312200659, -0.573278372048231, -0.200040818680771, -0.0799889953555568,
-0.934912327518399, 0.875673735720604, -0.682471048237961, -0.447713134997391,
-1.44503876598365, 0.200566623626427, -0.603618711537085, 1.63632602191341,
0.503729341336268, -1.44973373347937, -0.83120739283612, -0.653251746501829,
0.321855273316014, 1.21066716186829, 0.922268816601674, -0.813269292206998,
2.16666255913533, -0.672777990968793, -1.28856488757909, 1.39436054412452,
-0.211789100076818, -0.758333944980252, 0.906690414654383, 0.663788321885541,
1.03262546221369, -1.23848632618539, -0.0182114891420542, 0.18732365122894,
0.418806581297545, 0.798949434489707, -0.934912327518399, 0.34932210335748,
-0.326702899255083, -1.29461450563253, -0.984242429231461, -1.21232597024296,
-1.36900876790313, 0.518046614899514, 0.108846704023971, 0.875673735720604,
-0.0554181202716872, 0.985049507346337, 1.32690903772762, 0.147874987440912,
1.46255937870615, 2.31987502983337, 0.619575370122622, -0.404363346611779,
1.14527081123511, 1.21066716186829, -0.758333944980252, -0.643418559337893
), GDP.PC.PPP.Z = c(-0.578667969479209, -0.118697105136812, -0.466045655225293,
0.923409660963611, 0.991864131045693, -2.07560611746282, 0.943813486068404,
-0.637926841076349, 0.247334432561972, 0.798322240846842, 0.0514972081511996,
-0.372848777319968, -0.567817002128468, -0.187306357268071, -2.38313828212365,
0.459334215083589, 0.714009137923153, 0.646528321491845, 1.03238900277504,
-1.0555174676038, -0.778296145048397, -1.46312260085509, 0.536975954072058,
0.858441053205361, 0.790674347012461, -0.365552753858487, 0.926838361173849,
0.326114834872618, -2.54569504297012, 0.46017504777571, -1.8506345887589,
-1.08285649973564, 1.51891118971596, 0.638308637641741, 0.746105976041282,
0.268243356529517, 0.36510918213971, 0.604083691891451, 0.198748322887712,
-0.312389335256651, -0.905419626845786, 1.05266630879902, 1.33659487452438,
-2.87011160848619, 0.279819401728855, -0.897780846910471, -0.969354902742662,
0.548303295805111, 0.531331603917783, 0.510288898105396, -0.215357416726921,
0.425604963572419, 0.670361910662876, 0.624071621015143, 0.940063388699492,
1.22536621020093, -0.442507615305548, -0.848441456160931, 0.748977361996666,
1.09700572898117, 0.082117610439579, -1.12871295583376), Gini.Trend.Since.1980.Z = c(-0.972728762307106,
0.235661284719612, 0.807289419806078, -0.751795055249311, -0.541952384841444,
-1.28590248700461, -0.47757320406769, 0.519460378035315, -2.90579061741576,
-0.580839236047094, 1.32391656291119, -0.943517953562895, -0.113396990343457,
-0.977579788120396, -0.110513365069525, 1.07555192684504, -0.837591225467581,
-0.547633532490872, -1.10444688370133, 0.958189630444226, -0.399329079090007,
1.73208135283011, 0.424333933034159, -1.02755175406314, -0.338475925232408,
0.349294378348429, -1.01522269898644, -0.192613797020086, 0.428382484535192,
-0.479488366985406, -0.722332669474446, -1.3884974770747, 0.409714046813524,
-0.574157893236359, -0.882378704686931, 1.24091761183821, 0.327316356336889,
-0.823345895266208, 0.56832979485888, 0.71099636327897, 2.18026196918706,
-0.181951170444919, -0.62783056629162, 0.0673943434219194, 1.02128358818071,
0.87967773182586, 1.89885833159475, 1.49785597280742, 0.804996869073585,
0.546806173102294, 2.68562715882983, 0.515724944437757, -0.377236741009073,
-0.497430774529734, -1.29008018012335, -0.158135188867061, 0.937139266747765,
0.962410936735222, -0.625900533822269, -0.962683524356137, 0.010890770045687,
-0.404459154376347), Innovation.Index.Z = c(-1.10322330882057,
-0.935968336173481, -0.713955610326503, 0.741138019360247, 0.893866965234242,
-1.60640884664837, 0.713101142066744, -0.40999951704838, 0.042634817811248,
1.07129727204808, -0.418046117493808, 1.2986534534547, -0.69718127228074,
-0.772996888586645, -1.76502471536097, -0.125951616056125, 0.528749831459027,
0.449607907205552, 1.35142291375694, -1.58692470886517, -1.17503582566858,
-1.46200292206743, 1.0306358699988, 1.50167291443738, 1.23896393479529,
-0.59738991083039, 1.41701184255429, -0.133700762958127, -1.84628449835865,
0.125623119839705, 0.0274547879346658, -0.580897035510583, 0.873184066889493,
1.05776201457149, 0.543064234784961, -0.997067607196436, -0.118210153084921,
0.16307010447158, 0.192906757080406, -0.59738991083039, -0.739195452365693,
1.46268237378246, 0.810865461937928, -1.33060671956614, -1.08540282979067,
-1.3398902951567, -0.909961919811973, -0.071921143623544, 0.427900540036248,
-0.353919267294999, -0.442239430757734, -0.282434085697966, 0.170539277259389,
0.514412549706624, 1.82756324902192, 2.01492305972094, -0.0796170992285524,
-0.672097129845375, 1.60496073368948, 1.6945397369331, -0.705563251579159,
-0.133700762958127), Social.Mobility.Index.Z = c(-0.706105386379428,
-0.585641243282725, -0.822942004812496, 0.893527020218731, 1.38056875813782,
-1.63209565951418, 1.38056875813782, -0.942697479653827, -0.0915921450259129,
0.988425032276404, -0.364208334683061, -0.272472087192733, -1.05838609019865,
-0.264745812563682, -1.89934949521022, 0.145937967477542, 0.376575866110667,
0.855919262995838, 1.90967808262855, -0.822942004812496, -1.38668071061443,
-1.23621904621619, 0.744300955584676, 1.74016855022671, 1.04596632070911,
-0.706105386379428, 1.25091923046369, -0.401898315065783, -1.45872956398218,
0.0710920606438463, -1.50201293357616, -1.12090024948761, 0.884106253356048,
0.264385775549944, 0.204854352281649, -0.0108776343313731, 0.341829906108948,
0.473162777503202, -0.233715195153473, -0.909840011156656, -0.416886443795997,
1.6151450695718, 1.74016855022671, -1.80103155544201, -0.988170749389749,
-1.27169409847818, -1.0835423790243, 0.350497568438204, 0.60731979549657,
-0.147345467074108, -0.0915921450259129, 0.298679871236411, 1.01713919390481,
0.42907135941336, 1.72968089300841, 1.58417161065282, -0.720039150890423,
-0.295575599718776, 0.827845238004091, 0.464319390354575, 0.179529162219292,
-0.549520254806064), Social.Progress.Index.Z = c(-0.428475360063824,
0.0414886876042136, -0.361586155556238, 1.08276311464403, 1.10909919005662,
-2.02871021453541, 1.01008493768704, -0.711476953266137, -0.152134692312932,
1.12349208975159, 0.273693774269224, -1.22620730070184, -0.84880253287263,
0.259410021031203, -2.18229569620937, 0.444651638107071, 0.541526337932193,
0.771868863086631, 1.41176816060903, -0.663781035239767, -1.82009726121552,
-1.34315004834607, 0.884996475327265, 1.4019126407544, 0.874446196917741,
-0.397131547235487, 1.18971034024235, 0.458108602317123, -1.6209687111755,
-0.00441722919450905, -1.70192037310337, -1.14239167468768, 1.06603809514852,
0.540394102980479, 0.776508601641629, -0.716325507733983, 0.460353335318132,
0.601729573086324, -0.432510194770465, -0.752099047001046, -0.421409158795353,
1.2199456608804, 1.4364450726259, -2.37515169280453, -0.438558363298878,
-0.930988399538346, -0.765586386592023, 0.206869284152824, 0.720975477786726,
-0.143765564343391, -0.257045674777279, 0.329952179603639, 0.656583204051582,
0.790440884468391, 1.2745836204472, 1.37731195350324, -0.851653679189645,
-0.423428754032018, 0.881478490811943, 0.709445550714834, 0.217789408795679,
-1.00379635776192)), row.names = c(NA, -62L), class = "data.frame")
score_cols <- names(df_z)[grepl("\\.Z$", names(df_z))]
ui <- fluidPage(
# Add custom CSS for theming
tags$head(
tags$style(HTML("
:root {
--bg-color: #ffffff;
--text-color: #000000;
--sidebar-bg: #f5f5f5;
--sidebar-border: #e3e3e3;
--btn-primary-bg: #337ab7;
--btn-primary-border: #2e6da4;
--form-bg: #ffffff;
--form-border: #cccccc;
--table-bg: #ffffff;
--table-header-bg: #f5f5f5;
}
body.dark-theme {
--bg-color: #2b2b2b;
--text-color: #ffffff;
--sidebar-bg: #3c3c3c;
--sidebar-border: #555555;
--btn-primary-bg: #5bc0de;
--btn-primary-border: #46b8da;
--form-bg: #4a4a4a;
--form-border: #666666;
--table-bg: #2b2b2b;
--table-header-bg: #3c3c3c;
}
body {
background-color: var(--bg-color) !important;
color: var(--text-color) !important;
transition: all 0.3s ease;
}
.well {
background-color: var(--sidebar-bg) !important;
border: 1px solid var(--sidebar-border) !important;
color: var(--text-color) !important;
}
.btn-primary {
background-color: var(--btn-primary-bg) !important;
border-color: var(--btn-primary-border) !important;
}
.form-control {
background-color: var(--form-bg) !important;
border-color: var(--form-border) !important;
color: var(--text-color) !important;
}
.checkbox label, h4, h3, h2, h1 {
color: var(--text-color) !important;
}
.theme-toggle {
position: absolute;
top: 10px;
right: 20px;
z-index: 1000;
}
/* DataTable theming */
.dataTables_wrapper {
background-color: var(--bg-color) !important;
color: var(--text-color) !important;
}
.dataTables_wrapper table.dataTable thead th {
background-color: var(--table-header-bg) !important;
color: var(--text-color) !important;
border-bottom: 1px solid var(--sidebar-border) !important;
}
.dataTables_wrapper table.dataTable tbody td {
background-color: var(--table-bg) !important;
color: var(--text-color) !important;
border-top: 1px solid var(--sidebar-border) !important;
}
.dataTables_info, .dataTables_paginate {
color: var(--text-color) !important;
}
.paginate_button {
background-color: var(--form-bg) !important;
color: var(--text-color) !important;
border: 1px solid var(--form-border) !important;
}
")),
tags$script(HTML("
function toggleTheme() {
document.body.classList.toggle('dark-theme');
var btn = document.getElementById('theme_toggle');
if (document.body.classList.contains('dark-theme')) {
btn.innerHTML = '☀️ Light Mode';
btn.className = 'btn btn-sm btn-outline-light action-button';
} else {
btn.innerHTML = '🌙 Dark Mode';
btn.className = 'btn btn-sm btn-outline-secondary action-button';
}
}
"))
),
# Theme toggle button with onclick
div(class = "theme-toggle",
tags$button(id = "theme_toggle",
class = "btn btn-sm btn-outline-secondary",
onclick = "toggleTheme()",
"🌙 Dark Mode")),
titlePanel("Country Dashboard"),
sidebarLayout(
sidebarPanel(
width = 3,
h4("Select Indicators:"),
checkboxGroupInput("selected_cols",
"",
choices = setNames(score_cols, c(
"Debt % of GDP",
"Economic Complexity Index",
"GDP per Capita (PPP)",
"Gini Trend Since 1980",
"Innovation Index",
"Social Mobility Index",
"Social Progress Index"
)),
selected = score_cols),
hr(),
conditionalPanel(
condition = "input.selected_cols.length > 0",
h4("Indicator Weights:"),
uiOutput("weight_inputs")
),
br(),
actionButton("calculate", "Update Rankings",
class = "btn-primary btn-block"),
br(),
div(
style = "font-size: 12px; color: #666;",
)
),
mainPanel(
width = 9,
h4("Country Rankings"),
DT::dataTableOutput("result_table")
)
)
)
server <- function(input, output, session) {
output$weight_inputs <- renderUI({
req(input$selected_cols)
indicator_names <- c(
"Debt.percent.GDP.Z" = "Debt % of GDP",
"Economic.Complexity.Index.Z" = "Economic Complexity",
"GDP.PC.PPP.Z" = "GDP per Capita",
"Gini.Trend.Since.1980.Z" = "Gini Trend",
"Innovation.Index.Z" = "Innovation Index",
"Social.Mobility.Index.Z" = "Social Mobility",
"Social.Progress.Index.Z" = "Social Progress"
)
weight_inputs <- lapply(input$selected_cols, function(col) {
display_name <- indicator_names[col]
div(
style = "margin-bottom: 15px;",
strong(display_name),
br(),
selectInput(paste0("weight_", col),
label = NULL,
choices = c("1.0" = 1.0, "1.25" = 1.25, "1.5" = 1.5, "2.0" = 2.0),
selected = "1.0",
width = "100%")
)
})
do.call(tagList, weight_inputs)
})
scored_data <- eventReactive(input$calculate, {
req(input$selected_cols)
weights <- sapply(input$selected_cols, function(col) {
weight_input <- input[[paste0("weight_", col)]]
if(is.null(weight_input)) 1.0 else as.numeric(weight_input)
})
result_df <- data.frame(Country = df_z$Country)
if(length(input$selected_cols) > 0) {
weighted_scores <- mapply(function(col, weight) {
df_z[[col]] * weight
}, input$selected_cols, weights, SIMPLIFY = FALSE)
result_df$Score <- Reduce(`+`, weighted_scores)
result_df$Rank <- rank(-result_df$Score, ties.method = "min")
result_df <- cbind(result_df, df_z[input$selected_cols])
} else {
result_df$Score <- 0
result_df$Rank <- nrow(df_z)
}
col_order <- c("Rank", "Country", "Score", input$selected_cols)
result_df <- result_df[col_order[col_order %in% names(result_df)]]
result_df[order(result_df$Rank), ]
})
observe({
if(input$calculate == 0) {
weights <- rep(1.0, length(score_cols))
names(weights) <- score_cols
weighted_scores <- mapply(function(col, weight) {
df_z[[col]] * weight
}, score_cols, weights, SIMPLIFY = FALSE)
initial_score <- Reduce(`+`, weighted_scores)
initial_rank <- rank(-initial_score, ties.method = "min")
initial_data <- data.frame(
Rank = initial_rank,
Country = df_z$Country,
Score = initial_score,
df_z[score_cols]
)
initial_data <- initial_data[order(initial_data$Rank), ]
output$result_table <- DT::renderDataTable({
DT::datatable(
initial_data,
options = list(
pageLength = 20,
scrollX = TRUE,
columnDefs = list(
list(className = 'dt-center', targets = c(0, 2))
)
),
rownames = FALSE
) %>%
DT::formatRound(columns = "Score", digits = 2) %>%
DT::formatRound(columns = score_cols, digits = 3)
})
}
})
observeEvent(input$calculate, {
output$result_table <- DT::renderDataTable({
data <- scored_data()
DT::datatable(
data,
options = list(
pageLength = 20,
scrollX = TRUE,
columnDefs = list(
list(className = 'dt-center', targets = c(0, 2))
)
),
rownames = FALSE
) %>%
DT::formatRound(columns = "Score", digits = 2) %>%
DT::formatRound(columns = score_cols[score_cols %in% names(data)], digits = 3)
})
})
}
shinyApp(ui = ui, server = server)