Spaces:
Sleeping
Sleeping
Upload 11 files
Browse files- .RData +3 -0
- .Rhistory +512 -0
- .gitattributes +4 -0
- .gitignore +8 -1
- app.R +1566 -0
- cses-shiny.Rproj +17 -0
- cses_labs.rds +0 -0
- cses_labs_sec.rds +0 -0
- cses_shiny_data.rda +3 -0
- cses_shiny_data.rds +3 -0
- cses_variable_labels.csv +52 -0
- world.rda +3 -0
.RData
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:1f6725ad5536f6ac30971212826b4956210c8ed27a6f18e7a3a37bcb293557ca
|
| 3 |
+
size 34939611
|
.Rhistory
ADDED
|
@@ -0,0 +1,512 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# REMOVING SOME SPECIFIC CATEGORIES FOR SIMPLICITY OF ATA PRESENTATION
|
| 2 |
+
# # -----------------------------------------------------------------------
|
| 3 |
+
#cses_imd$IMD3010[cses_imd$IMD3010==6]<-3;
|
| 4 |
+
#cses_imd$IMD2004[cses_imd$IMD2004==5]<-NA
|
| 5 |
+
#cses_imd$IMD5007[cses_imd$IMD5007==5]<-0; # 4-point
|
| 6 |
+
#cses_imd$IMD5052_2<-round(cses_imd$IMD5052_2, 1)
|
| 7 |
+
# # -----------------------------------------------------------------------
|
| 8 |
+
# WEIGHTS
|
| 9 |
+
# # -----------------------------------------------------------------------
|
| 10 |
+
cses_imd$no_weight <- 1 # UNWEIGHTED (RAW DATA)
|
| 11 |
+
table(cses_imd$no_weight)
|
| 12 |
+
table(cses_imd$IMD1010_1) # SAMPLE (SELECTION BIAS)
|
| 13 |
+
cses_imd$weight_sample <- cses_imd$IMD1010_1
|
| 14 |
+
table(cses_imd$IMD1010_2) # DEMOGRAPHIC (NON-RESPONSE BIAS)
|
| 15 |
+
cses_imd$weight_demographic <- cses_imd$IMD1010_2 # PREFERRED WEIGHT (DEFAULT)
|
| 16 |
+
#table(cses_imd$IMD1010_3) # POLITICAL (VOTING)- NOT USING IT
|
| 17 |
+
#cses_imd$weight_political <- cses_imd$IMD1010_3
|
| 18 |
+
# # -----------------------------------------------------------------------
|
| 19 |
+
# SELECTING VARIABLES FOR DATA PLAYGROUND
|
| 20 |
+
# # -----------------------------------------------------------------------
|
| 21 |
+
vars <- c(
|
| 22 |
+
"gendermc",
|
| 23 |
+
"wealthf",
|
| 24 |
+
"edrerf",
|
| 25 |
+
"age",
|
| 26 |
+
"ur",
|
| 27 |
+
"labor_force",
|
| 28 |
+
"compulsory_vote",
|
| 29 |
+
"pais",
|
| 30 |
+
"wave",
|
| 31 |
+
"pais_num",
|
| 32 |
+
"pais_lab",
|
| 33 |
+
"no_weight",
|
| 34 |
+
"weight_sample",
|
| 35 |
+
"weight_demographic",
|
| 36 |
+
"IMD1008_MOD"
|
| 37 |
+
)
|
| 38 |
+
# --- Variable Labels
|
| 39 |
+
vars_labels <- read.csv("./Data preprocessing/cses_variable_labels_raw.csv",
|
| 40 |
+
encoding = "latin1")
|
| 41 |
+
# Variable Display for Dropdown Menu (category + question + name)
|
| 42 |
+
vars_labels$display_en <- paste0(vars_labels$category_short_en, ": ",
|
| 43 |
+
vars_labels$question_short_en,
|
| 44 |
+
" (", vars_labels$column_name, ")", sep = "")
|
| 45 |
+
vars2 <- vars_labels$column_name
|
| 46 |
+
vars3 <- c(vars2, vars)
|
| 47 |
+
# Check if all variables are present and subset dataset for shinyapp
|
| 48 |
+
vars3 %in% names(cses_imd)
|
| 49 |
+
cses_out <- cses_imd[vars3]
|
| 50 |
+
# CHECKING INDIVIDUAL VARIABLES
|
| 51 |
+
#table(cses_out$IMD2005_1) # remove 7 8 9; 6-point
|
| 52 |
+
#table(cses_out$IMD2005_2) # remove 7 8 9; 4-point
|
| 53 |
+
#table(cses_out$IMD2016) # remove 7:9 values; 5-point
|
| 54 |
+
#table(cses_out$IMD2019_1) # remove 7:9 values; 2-point
|
| 55 |
+
#table(cses_out$IMD3001_PR_1) # remove 9999993 9999995 9999996 9999997 9999998 9999999; 2-point
|
| 56 |
+
#table(cses_out$IMD3001_PR_2) # remove 9999993 9999995 9999996 9999997 9999998 9999999; 2-point
|
| 57 |
+
#table(cses_out$IMD3001_LH) # remove 9999993 9999995 9999996 9999997 9999998 9999999; 2-point
|
| 58 |
+
#table(cses_out$IMD3001_UH) # remove 9999993 9999995 9999996 9999997 9999998 9999999; 2-point
|
| 59 |
+
#table(cses_out$IMD3001_TS) # remove 9; 6-point
|
| 60 |
+
#table(cses_out$IMD3002_OUTGOV) # remove 9999996 9999997 9999998 9999999; 2-point
|
| 61 |
+
#table(cses_out$IMD3002_VS_1) # remove 9; 2-point
|
| 62 |
+
#table(cses_out$IMD3002_LR_CSES) # remove 9; 3-point
|
| 63 |
+
#table(cses_out$IMD3005_1) # remove 7 8 9; 2-point
|
| 64 |
+
#table(cses_out$IMD3010) # remove 7 8 9, 6 should be recoded to 3; 5-point
|
| 65 |
+
#table(cses_out$IMD3011) # remove 7 8 9; 5-point
|
| 66 |
+
#table(cses_out$IMD3012) # remove 7 8 9; 5-point
|
| 67 |
+
#table(cses_out$IMD3013_1) # remove 7 8 9; 3-point with 1,3,5
|
| 68 |
+
#table(cses_out$IMD3013_2) # remove 7 8 9; 2-point with 1,2
|
| 69 |
+
#table(cses_out$IMD3013_3) # remove 7 8 9; 2-point with 4 and 5
|
| 70 |
+
#table(cses_out$IMD3014) # remove 6 7 8 9; 4-point
|
| 71 |
+
#table(cses_out$IMD5006_1) # remove 999; TOO MANY
|
| 72 |
+
#table(cses_out$IMD5006_2) # remove 999; TOO MANY
|
| 73 |
+
#table(cses_out$IMD5013) # ok; 3-point
|
| 74 |
+
#table(cses_out$IMD5014) # remove 7; 5-point
|
| 75 |
+
#table(cses_out$IMD5032_4) # remove 9; 3-point
|
| 76 |
+
#table(cses_out$IMD5033) # remove 9; 3-point
|
| 77 |
+
#table(cses_out$IMD5034_2) # remove 6 9; 2-point
|
| 78 |
+
#table(cses_out$IMD5035) # remove 999; TOO MANY
|
| 79 |
+
#table(cses_out$IMD5048) # ok; 3-point
|
| 80 |
+
#table(cses_out$IMD5049) # remove 999; TOO MANY
|
| 81 |
+
#table(cses_out$IMD5050_1) # ok; 7-point
|
| 82 |
+
#table(cses_out$IMD5051_1) # remove 99 and -88; 10-point
|
| 83 |
+
#table(cses_out$IMD5052_2) # remove 99; TOO MANY
|
| 84 |
+
#table(cses_out$IMD5053_1) # remove 999999; TOO MANY
|
| 85 |
+
#table(cses_out$IMD5054_2) # remove 999; TOO MANY
|
| 86 |
+
#table(cses_out$IMD5055_1) # remove 999; TOO MANY
|
| 87 |
+
#table(cses_out$IMD5056_2) # remove 99999; TOO MANY
|
| 88 |
+
#table(cses_out$IMD5057_1) # remove 9999999999; TOO MANY
|
| 89 |
+
#table(cses_out$IMD5058_1) # remove 997 999; TOO MANY
|
| 90 |
+
#table(cses_imd$IMD3001_TS) # REMOVE 9
|
| 91 |
+
#table(cses_imd$IMD5054_2) # REMOVE 999
|
| 92 |
+
#table(cses_imd$IMD5057_1) # REMOVE 9999999999
|
| 93 |
+
#table(cses_imd$IMD5035) # REMOVE 999
|
| 94 |
+
#table(cses_imd$IMD5056_2) # REMOVE 99999
|
| 95 |
+
#table(cses_imd$IMD5055_1) # remove 999
|
| 96 |
+
#table(cses_imd$IMD5053_1) # remove 999999
|
| 97 |
+
#table(cses_imd$IMD5052_2) # remove 99
|
| 98 |
+
#table(cses_imd$IMD5006_2) # REMOVE 999
|
| 99 |
+
#table(cses_imd$IMD5006_1) # REMOVE 999
|
| 100 |
+
#table(cses_imd$IMD5058_1) # REMOVE 997 999
|
| 101 |
+
#table(cses_imd$IMD5049) # REMOVE 999
|
| 102 |
+
# # -----------------------------------------------------------------------
|
| 103 |
+
# REMOVING NAs/NRs/DKs
|
| 104 |
+
# # -----------------------------------------------------------------------
|
| 105 |
+
# 95. VOLUNTEERED: HAVEN'T HEARD OF LEFT-RIGHT
|
| 106 |
+
# 97. VOLUNTEERED: REFUSED
|
| 107 |
+
# 98. VOLUNTEERED: DON'T KNOW WHERE TO PLACE
|
| 108 |
+
# 99. MISSING
|
| 109 |
+
cses_out <- cses_out %>%
|
| 110 |
+
mutate(across(c(IMD2014, IMD3006,
|
| 111 |
+
IMD5051_1, IMD5052_2,
|
| 112 |
+
IMD2014, IMD3006), ~
|
| 113 |
+
replace(.x, .x %in% c(-88, -77, -66, 95:99), NA)))
|
| 114 |
+
# FIX NEGATIVE VALUES
|
| 115 |
+
#cses_out$IMD5051_1<-(cses_out$IMD5051_1+10)/2
|
| 116 |
+
cses_out <- cses_out %>%
|
| 117 |
+
mutate(across(c(IMD3001, IMD3001_PR_1, IMD3001_PR_2,
|
| 118 |
+
IMD3001_LH, IMD3001_UH, IMD2001_2,
|
| 119 |
+
IMD3002_OUTGOV, IMD3002_LR_CSES), ~
|
| 120 |
+
replace(.x, .x %in% c(9999993:9999999, 9997:9999), NA)))
|
| 121 |
+
cses_out <- cses_out %>%
|
| 122 |
+
mutate(across(c(IMD5006_1, IMD5006_2, IMD5035,
|
| 123 |
+
IMD5049, IMD5053_1, IMD5054_2,
|
| 124 |
+
IMD5045_1, IMD5055_1, IMD5056_2,
|
| 125 |
+
IMD5057_1, IMD5058_1,
|
| 126 |
+
IMD5054_2), ~
|
| 127 |
+
replace(.x, .x %in% c(9999999999, 999999, 99999, 997, 998, 999), NA)))
|
| 128 |
+
cses_out <- cses_out %>%
|
| 129 |
+
mutate(across(c(IMD3001_TS, IMD3002_VS_1, IMD5032_4,
|
| 130 |
+
IMD5033, IMD3002_LR_CSES), ~ replace(.x, .x %in% c(9), NA)))
|
| 131 |
+
cses_out <- cses_out %>%
|
| 132 |
+
mutate(across(c(IMD2005_1, IMD2005_2, IMD2016,
|
| 133 |
+
IMD2019_1, IMD3005_1, IMD3011,
|
| 134 |
+
IMD3012, IMD3013_1, IMD3013_2,
|
| 135 |
+
IMD3010, IMD3013_3, IMD2002, IMD5036_3,
|
| 136 |
+
IMD2004, IMD2007), ~ replace(.x, .x %in% c(7:9), NA)))
|
| 137 |
+
cses_out <- cses_out %>%
|
| 138 |
+
mutate(across(c(IMD3014, IMD5014, IMD5034_2, IMD2003, IMD2006), ~
|
| 139 |
+
replace(.x, .x %in% c(6:9), NA)))
|
| 140 |
+
# # -----------------------------------------------------------------------
|
| 141 |
+
# CONTINUOUS VARIABLES RECODE INTO QUINTILES (~20% per category)
|
| 142 |
+
# # -----------------------------------------------------------------------
|
| 143 |
+
# Quintiles (5 groups) — overwrite the column with a factor that shows pretty labels.
|
| 144 |
+
# The factor's internal codes are 1..5 in order,
|
| 145 |
+
# so as.integer(df[[col]]) returns 1..5.
|
| 146 |
+
quantile_cut_quintiles <- function(
|
| 147 |
+
data, value_col, digits = 0, big_mark = ",",
|
| 148 |
+
include_lowest = TRUE, type = 7) # linear interpolation of the empirical CDF
|
| 149 |
+
{
|
| 150 |
+
stopifnot(is.data.frame(data), value_col %in% names(data))
|
| 151 |
+
x <- data[[value_col]]
|
| 152 |
+
# Breaks at "0,20,40,60,80,100"
|
| 153 |
+
br <- as.numeric(quantile(x, probs = seq(0, 1, 0.2), na.rm = TRUE, type = type))
|
| 154 |
+
br <- br[is.finite(br)]
|
| 155 |
+
br <- unique(br)
|
| 156 |
+
# Edge cases: all-NA or constant
|
| 157 |
+
if (length(br) < 2) {
|
| 158 |
+
rng <- if (all(is.na(x))) c(NA, NA) else range(x, na.rm = TRUE)
|
| 159 |
+
lab <- if (all(is.na(x))) NA_character_ else paste0("[", rng[1], ", ", rng[2], "]")
|
| 160 |
+
f <- factor(ifelse(is.na(x), NA_character_, lab), levels = lab) # single level => code 1
|
| 161 |
+
data[[value_col]] <- f
|
| 162 |
+
attr(data[[value_col]], "breaks") <- br
|
| 163 |
+
return(data)
|
| 164 |
+
}
|
| 165 |
+
# Ensure strictly increasing breaks (avoid duplicate labels)
|
| 166 |
+
for (i in 2:length(br)) {
|
| 167 |
+
if (br[i] <= br[i - 1]) {
|
| 168 |
+
bump <- 1e-8 * max(1, abs(br[i - 1]))
|
| 169 |
+
br[i] <- br[i - 1] + bump
|
| 170 |
+
}
|
| 171 |
+
}
|
| 172 |
+
# Formatting
|
| 173 |
+
fmt <- function(v) formatC(v, format = "f", digits = digits, big.mark = big_mark)
|
| 174 |
+
# Labels: first/last open-ended text
|
| 175 |
+
labs <- c(
|
| 176 |
+
paste0("Less than ", fmt(br[2])),
|
| 177 |
+
paste0(fmt(br[2]), " to ", fmt(br[3])),
|
| 178 |
+
paste0(fmt(br[3]), " to ", fmt(br[4])),
|
| 179 |
+
paste0(fmt(br[4]), " to ", fmt(br[5])),
|
| 180 |
+
paste0("More than ", fmt(br[5]))
|
| 181 |
+
)
|
| 182 |
+
# Get quintile codes 1..5, then map to labels
|
| 183 |
+
codes <- as.integer(findInterval(x, br, rightmost.closed = TRUE, all.inside = TRUE))
|
| 184 |
+
codes[is.na(x)] <- NA_integer_
|
| 185 |
+
# Create factor with levels in the *desired* order so codes 1..5 align
|
| 186 |
+
f <- factor(ifelse(is.na(codes), NA_character_, labs[codes]), levels = labs)
|
| 187 |
+
data[[value_col]] <- f
|
| 188 |
+
attr(data[[value_col]], "breaks") <- br
|
| 189 |
+
data
|
| 190 |
+
}
|
| 191 |
+
# Check the new factor labels (built from raw cutpoints)
|
| 192 |
+
cses_out <- quantile_cut_quintiles(cses_out, "IMD5054_2")
|
| 193 |
+
table(cses_out$IMD5054_2); levels(cses_out$IMD5054_2)
|
| 194 |
+
cses_out <- quantile_cut_quintiles(cses_out, "IMD5057_1")
|
| 195 |
+
table(cses_out$IMD5057_1); levels(cses_out$IMD5057_1)
|
| 196 |
+
cses_out <- quantile_cut_quintiles(cses_out, "IMD5035")
|
| 197 |
+
table(cses_out$IMD5035); levels(cses_out$IMD5035)
|
| 198 |
+
cses_out <- quantile_cut_quintiles(cses_out, "IMD5056_2")
|
| 199 |
+
table(cses_out$IMD5056_2); levels(cses_out$IMD5056_2)
|
| 200 |
+
cses_out <- quantile_cut_quintiles(cses_out, "IMD5055_1", digits=2)
|
| 201 |
+
table(cses_out$IMD5055_1); levels(cses_out$IMD5055_1)
|
| 202 |
+
cses_out <- quantile_cut_quintiles(cses_out, "IMD5053_1")
|
| 203 |
+
table(cses_out$IMD5053_1); levels(cses_out$IMD5053_1)
|
| 204 |
+
cses_out <- quantile_cut_quintiles(cses_out, "IMD5052_2")
|
| 205 |
+
table(cses_out$IMD5052_2); levels(cses_out$IMD5052_2)
|
| 206 |
+
cses_out <- quantile_cut_quintiles(cses_out, "IMD5006_2")
|
| 207 |
+
table(cses_out$IMD5006_2); levels(cses_out$IMD5006_2)
|
| 208 |
+
cses_out <- quantile_cut_quintiles(cses_out, "IMD5006_1")
|
| 209 |
+
table(cses_out$IMD5006_1); levels(cses_out$IMD5006_1)
|
| 210 |
+
cses_out <- quantile_cut_quintiles(cses_out, "IMD5058_1")
|
| 211 |
+
table(cses_out$IMD5058_1); levels(cses_out$IMD5058_1)
|
| 212 |
+
cses_out <- quantile_cut_quintiles(cses_out, "IMD5049")
|
| 213 |
+
table(cses_out$IMD5049); levels(cses_out$IMD5049)
|
| 214 |
+
# Turn factor quintiles into labelled numerics 1..5 using the factor's own levels as value labels
|
| 215 |
+
factor_quintiles_to_labelled <- function(df, vars) {
|
| 216 |
+
stopifnot(is.data.frame(df))
|
| 217 |
+
for (v in vars) {
|
| 218 |
+
if (!v %in% names(df)) next
|
| 219 |
+
f <- df[[v]]
|
| 220 |
+
if (!is.factor(f)) { warning(sprintf("'%s' is not a factor; skipping.", v)); next }
|
| 221 |
+
lv <- levels(f)
|
| 222 |
+
if (length(lv) != 5) { warning(sprintf("'%s' has %d levels (expected 5); skipping.", v, length(lv))); next }
|
| 223 |
+
df[[v]] <- haven::labelled(as.integer(f), labels = stats::setNames(1:5, lv))
|
| 224 |
+
}
|
| 225 |
+
df
|
| 226 |
+
}
|
| 227 |
+
qvars <- c("IMD5054_2","IMD5057_1","IMD5035","IMD5056_2",
|
| 228 |
+
"IMD5055_1","IMD5053_1","IMD5052_2","IMD5006_2","IMD5006_1",
|
| 229 |
+
"IMD5058_1","IMD5049")
|
| 230 |
+
cses_out <- factor_quintiles_to_labelled(cses_out, qvars)
|
| 231 |
+
# REMOVING CONTINUOUS VARS FOR NOW...
|
| 232 |
+
#labs <- labs[!labs %in% c("IMD3001_TS", "IMD5054_2", "IMD5057_1", "IMD5035",
|
| 233 |
+
# "IMD5056_2", "IMD5055_1", "IMD5053_1", "IMD5052_2",
|
| 234 |
+
# "IMD5006_2", "IMD5006_1", "IMD5058_1", "IMD5049")]
|
| 235 |
+
# # -----------------------------------------------------------------------
|
| 236 |
+
# OUTCOME VARIABLES RECODE (NUMERIC TO LABELS)
|
| 237 |
+
# # -----------------------------------------------------------------------
|
| 238 |
+
# Function to convert labels to sentence case
|
| 239 |
+
to_sentence_case <- function(x) {
|
| 240 |
+
sapply(x, function(s) {
|
| 241 |
+
s <- tolower(s)
|
| 242 |
+
# Capitalize first letter after ) or .
|
| 243 |
+
s <- gsub("([).]\\s*)([a-z])", "\\1\\U\\2", s, perl = TRUE)
|
| 244 |
+
# Also capitalize the very first character if needed
|
| 245 |
+
sub("^(\\w)", "\\U\\1", s, perl = TRUE)
|
| 246 |
+
}, USE.NAMES = FALSE)
|
| 247 |
+
}
|
| 248 |
+
# Label output dataset to include the labels through haven package
|
| 249 |
+
# Safer: won't touch your quintile-binned columns
|
| 250 |
+
label_all_for_haven <- function(data, data_out, exclude_vars = NULL) {
|
| 251 |
+
label_table <- attr(data, "label.table", exact = TRUE)
|
| 252 |
+
data_labeled <- data_out
|
| 253 |
+
for (var in names(data_out)) {
|
| 254 |
+
# 1) explicit exclude list
|
| 255 |
+
if (!is.null(exclude_vars) && var %in% exclude_vars) next
|
| 256 |
+
# 2) skip if already a factor (e.g., your quintile pretty labels)
|
| 257 |
+
if (is.factor(data_out[[var]])) next
|
| 258 |
+
# 3) skip if marked as quintiled by attribute you set (optional)
|
| 259 |
+
if (!is.null(attr(data_out[[var]], "q5_breaks"))) next
|
| 260 |
+
if (!is.null(label_table[[var]])) {
|
| 261 |
+
values <- label_table[[var]]
|
| 262 |
+
# Clean labels: remove number prefixes, apply sentence case
|
| 263 |
+
clean_labels <- sub("^\\d+\\.\\s*", "", names(values))
|
| 264 |
+
clean_labels <- to_sentence_case(clean_labels)
|
| 265 |
+
# Build named vector: names = labels, values = numeric codes
|
| 266 |
+
labelled_vec <- setNames(as.numeric(values), clean_labels)
|
| 267 |
+
# Preserve original column's storage; attach labels if numeric/integer
|
| 268 |
+
var_data <- data_out[[var]]
|
| 269 |
+
if (is.numeric(var_data) || is.integer(var_data)) {
|
| 270 |
+
data_labeled[[var]] <- haven::labelled(var_data, labels = labelled_vec)
|
| 271 |
+
} else {
|
| 272 |
+
# Non-numeric targets get skipped to avoid clobbering (e.g., factors/quintiles)
|
| 273 |
+
# Alternatively, you could coerce if you really want:
|
| 274 |
+
# data_labeled[[var]] <- haven::labelled(as.numeric(var_data), labels = labelled_vec)
|
| 275 |
+
}
|
| 276 |
+
}
|
| 277 |
+
}
|
| 278 |
+
data_labeled
|
| 279 |
+
}
|
| 280 |
+
cses_out_labels <- label_all_for_haven(cses_imd, cses_out,
|
| 281 |
+
exclude_vars = c("IMD5054_2","IMD5057_1","IMD5035","IMD5056_2",
|
| 282 |
+
"IMD5055_1","IMD5053_1","IMD5052_2","IMD5006_2",
|
| 283 |
+
"IMD5006_1","IMD5058_1","IMD5049"))
|
| 284 |
+
# # -----------------------------------------------------------------------
|
| 285 |
+
# CUSTOM FIXES FOR NOW
|
| 286 |
+
# # -----------------------------------------------------------------------
|
| 287 |
+
cses_out_labels$IMD2004[cses_out_labels$IMD2004==5]<-NA
|
| 288 |
+
cses_out_labels$IMD3010 <- labelled(
|
| 289 |
+
x = replace(cses_out_labels$IMD3010, cses_out_labels$IMD3010 == 6, 3),
|
| 290 |
+
labels = c(
|
| 291 |
+
"Very satisfied" = 1,
|
| 292 |
+
"Satisfied" = 2,
|
| 293 |
+
"Neither" = 3,
|
| 294 |
+
"Not very satisfied" = 4,
|
| 295 |
+
"Not all satisfied" = 5
|
| 296 |
+
)
|
| 297 |
+
)
|
| 298 |
+
cses_out_labels$IMD5007 <- labelled(
|
| 299 |
+
x = replace(cses_out_labels$IMD5007, cses_out_labels$IMD5007 == 5, 0),
|
| 300 |
+
labels = c(
|
| 301 |
+
"No" = 0,
|
| 302 |
+
"Yes; no sanctions" = 3,
|
| 303 |
+
"Yes; weakly enforced" = 2,
|
| 304 |
+
"Yes; strictly enforced" = 1
|
| 305 |
+
)
|
| 306 |
+
)
|
| 307 |
+
cses_out_labels$IMD5052_2<-round(cses_out_labels$IMD5052_2, 1)
|
| 308 |
+
# # -----------------------------------------------------------------------
|
| 309 |
+
# Exporting DATA (.rds lighter file storage)
|
| 310 |
+
# # -----------------------------------------------------------------------
|
| 311 |
+
# MERGE PAIS_LAB TO CSES_OUT BEFORE EXPORT
|
| 312 |
+
pais_lab_merge<-subset(pais_lab, select=c("pais_lab", "pais_nam"))
|
| 313 |
+
cses_out_labels <- merge(cses_out_labels, pais_lab_merge, by = "pais_lab")
|
| 314 |
+
str(cses_out_labels)
|
| 315 |
+
# REMOVING CONTINUOUS VARS FOR NOW...
|
| 316 |
+
#cses_out_labels<-subset(cses_out_labels, select=-c(IMD3001_TS, IMD5054_2, IMD5057_1, IMD5035,
|
| 317 |
+
# IMD5056_2, IMD5055_1, IMD5053_1, IMD5052_2,
|
| 318 |
+
# IMD5006_2, IMD5006_1, IMD5058_1, IMD5049))
|
| 319 |
+
# EXPORT RDS
|
| 320 |
+
saveRDS(cses_out_labels, "./cses_shiny_data.rds")
|
| 321 |
+
# EXPORT RDA (MAX COMPRESSION)
|
| 322 |
+
cses_shiny_data<-cses_out_labels
|
| 323 |
+
save(cses_shiny_data, file="cses_shiny_data.rda")
|
| 324 |
+
tools::resaveRdaFiles("./cses_shiny_data.rda", compress = "xz")
|
| 325 |
+
tools::checkRdaFiles("./cses_shiny_data.rda")
|
| 326 |
+
# # -----------------------------------------------------------------------
|
| 327 |
+
# EXTRACTING RESPONSE OPTIONS FOR VARS_LABELS DATA
|
| 328 |
+
# # -----------------------------------------------------------------------
|
| 329 |
+
# Define the format_labels function to extract and format ROs
|
| 330 |
+
format_labels <- function(label_table) {
|
| 331 |
+
valid_labels <- label_table[label_table > -15 & label_table < 90] # Exclude special codes / DK / NA / NR / ETC
|
| 332 |
+
formatted <- sapply(seq_along(valid_labels), function(i) {
|
| 333 |
+
paste0("(", valid_labels[i], ") ", names(valid_labels)[i])
|
| 334 |
+
})
|
| 335 |
+
paste(formatted, collapse = " ")
|
| 336 |
+
}
|
| 337 |
+
# Extract Response Options from Dataset Attributes (label.table)
|
| 338 |
+
for (var in names(cses_imd)) {
|
| 339 |
+
if (var %in% vars_labels$column_name) {
|
| 340 |
+
label_table <- attr(cses_imd, "label.table", exact=T)[[var]]
|
| 341 |
+
if (!is.null(label_table)) {
|
| 342 |
+
vars_labels$responses_en[vars_labels$column_name == var] <- format_labels(label_table)
|
| 343 |
+
}
|
| 344 |
+
}
|
| 345 |
+
}
|
| 346 |
+
table(vars_labels$responses_en=="" | is.na(vars_labels$responses_en))
|
| 347 |
+
# # -----------------------------------------------------------------------
|
| 348 |
+
# FILLING "Question Wording" & "Response Options" for Variables.
|
| 349 |
+
# # -----------------------------------------------------------------------
|
| 350 |
+
qword_ro<-read.csv("./Data preprocessing/cses_qwording.csv", header=T)
|
| 351 |
+
vars_labels <- vars_labels %>%
|
| 352 |
+
left_join(qword_ro, by = "column_name", suffix = c(".old", "")) %>%
|
| 353 |
+
# Select columns from qword_ro and non-duplicated columns from vars_labels
|
| 354 |
+
select(-contains(".old"))
|
| 355 |
+
table(vars_labels$responses_en=="" | is.na(vars_labels$responses_en))
|
| 356 |
+
# REMOVING NAs/NRs/DKs from ROs and other CLEANING (mojibake/encoding errors)
|
| 357 |
+
#vars_labels$responses_en<-gsub("–", ":", vars_labels$responses_en)
|
| 358 |
+
#vars_labels$responses_en<-gsub("00.", "0.", vars_labels$responses_en)
|
| 359 |
+
#vars_labels$question_short_en<-gsub("–", ":", vars_labels$question_short_en)
|
| 360 |
+
#vars_labels$question_short_en<-gsub("–", "", vars_labels$question_short_en)
|
| 361 |
+
vars_labels$responses_en<-trimws(vars_labels$responses_en)
|
| 362 |
+
vars_labels$responses_en<-gsub(" \\(7\\) 7. VOLUNTEERED: REFUSED", "", vars_labels$responses_en)
|
| 363 |
+
vars_labels$responses_en<-gsub(" \\(8\\) 8. VOLUNTEERED: DON'T KNOW", "", vars_labels$responses_en)
|
| 364 |
+
vars_labels$responses_en<-gsub(" \\(9\\) 9. MISSING", "", vars_labels$responses_en)
|
| 365 |
+
vars_labels$responses_en<-gsub(" \\(6\\) 6. OTHER", "", vars_labels$responses_en)
|
| 366 |
+
vars_labels$responses_en<-gsub(" \\(6\\) 6. \\[SEE ELECTION STUDY NOTES\\]", "", vars_labels$responses_en)
|
| 367 |
+
vars_labels$responses_en<-gsub(" \\(7\\) 7. NOT APPLICABLE \\[NO ALLIANCES PERMITTED\\]", "", vars_labels$responses_en)
|
| 368 |
+
vars_labels$responses_en<-gsub(" \\(6\\) 6. NO INTERNATIONAL ELECTION OBSERVERS", "", vars_labels$responses_en)
|
| 369 |
+
vars_labels$responses_en<-gsub(" \\(7\\) 7. NOT APPLICABLE", "", vars_labels$responses_en)
|
| 370 |
+
vars_labels$responses_en<-gsub("\\(1\\) 1. VERY SATISFIED \\(2\\) 2. FAIRLY SATISFIED \\(4\\) 4. NOT VERY SATISFIED \\(5\\) 5. NOT AT ALL SATISFIED \\(6\\) 6. NEITHER SATISFIED NOR DISSATISFIED",
|
| 371 |
+
"\\(1\\) 1. VERY SATISFIED \\(2\\) 2. FAIRLY SATISFIED \\(3\\) 3. NEITHER SATISFIED NOR DISSATISFIED \\(4\\) 4. NOT VERY SATISFIED \\(5\\) 5. NOT AT ALL SATISFIED",
|
| 372 |
+
vars_labels$responses_en)
|
| 373 |
+
vars_labels$responses_en<-trimws(vars_labels$responses_en)
|
| 374 |
+
# Remove Extra and WhiteSpaces respectivelly
|
| 375 |
+
vars_labels$responses_en <- gsub("(\\s|^)-?\\d+\\.\\s", "\\1", vars_labels$responses_en)
|
| 376 |
+
vars_labels$responses_en <- gsub("([).])\\s+", "\\1 ", vars_labels$responses_en)
|
| 377 |
+
vars_labels$responses_en<-gsub("\\(5\\) NO", "\\(0\\) NO", vars_labels$responses_en) # COMPULSORY VOTE IMD5007
|
| 378 |
+
# Create responses_en_rec
|
| 379 |
+
vars_labels$responses_en_rec<-to_sentence_case(vars_labels$responses_en)
|
| 380 |
+
vars_labels$question_short_en<-to_sentence_case(vars_labels$question_short_en)
|
| 381 |
+
vars_labels$question_short_en <- gsub("([).-])\\s+", "\\1 ", vars_labels$question_short_en)
|
| 382 |
+
# # -----------------------------------------------------------------------
|
| 383 |
+
# EXPORT CSES LABELS
|
| 384 |
+
# # -----------------------------------------------------------------------
|
| 385 |
+
# REMOVING CONTINUOUS VARS FOR NOW...
|
| 386 |
+
#vars_labels <- vars_labels[!vars_labels$column_name %in%
|
| 387 |
+
# c("IMD3001_TS", "IMD5054_2", "IMD5057_1", "IMD5035",
|
| 388 |
+
# "IMD5056_2", "IMD5055_1", "IMD5053_1", "IMD5052_2",
|
| 389 |
+
# "IMD5006_2", "IMD5006_1", "IMD5058_1", "IMD5049"), ]
|
| 390 |
+
write.csv(vars_labels, "./cses_variable_labels.csv", row.names=F)
|
| 391 |
+
# # -----------------------------------------------------------------------
|
| 392 |
+
# LABS VECTOR
|
| 393 |
+
# # -----------------------------------------------------------------------
|
| 394 |
+
labs <- vars_labels$column_name
|
| 395 |
+
names(labs) <- vars_labels$display_en
|
| 396 |
+
labs[order(names(labs))]
|
| 397 |
+
names(vars_labels$column_name) <- vars_labels$display_en
|
| 398 |
+
vars_labels$labs2 <- labs
|
| 399 |
+
vars_labels$question_en_comp <- paste0(vars_labels$question_en,
|
| 400 |
+
vars_labels$responses_en_rec,
|
| 401 |
+
sep = " ")
|
| 402 |
+
# Dropping MACRO Variables from Outcomes but keeping at secondary vars
|
| 403 |
+
drop_macro <- grep("Macro Data:", names(labs), value = TRUE)
|
| 404 |
+
labs_sec <- labs
|
| 405 |
+
labs <- labs[!(names(labs) %in% drop_macro)]
|
| 406 |
+
# # -----------------------------------------------------------------------
|
| 407 |
+
# FINAL EXPORT OF LABELS
|
| 408 |
+
# # -----------------------------------------------------------------------
|
| 409 |
+
saveRDS(labs, "./cses_labs.rds")
|
| 410 |
+
saveRDS(labs_sec, "./cses_labs_sec.rds")
|
| 411 |
+
# END
|
| 412 |
+
# # -----------------------------------------------------------------------
|
| 413 |
+
message("Code ended succesfully")
|
| 414 |
+
# # -----------------------------------------------------------------------
|
| 415 |
+
### CSES DATA PLAYGROUND
|
| 416 |
+
# Date: November 10th, 2025
|
| 417 |
+
# Author: Robert Vidigal, PhD
|
| 418 |
+
# Purpose: CSES Shiny Data Playground based on LAPOP Lab Data Playground
|
| 419 |
+
# Prev file: ./shiny_preprocessing.R
|
| 420 |
+
# Machine: Windows OS
|
| 421 |
+
# Status: On-going
|
| 422 |
+
# # -----------------------------------------------------------------------
|
| 423 |
+
### Data In:
|
| 424 |
+
# 1. cses_shiny_data.rda
|
| 425 |
+
# 2. cses_variable_labels.csv
|
| 426 |
+
# 3. cses_labs.rds
|
| 427 |
+
# 4. and fonts from /wwww/
|
| 428 |
+
### Data Out: N/A
|
| 429 |
+
# # -----------------------------------------------------------------------
|
| 430 |
+
options(shiny.useragg = TRUE) # speed it up
|
| 431 |
+
# # -----------------------------------------------------------------------
|
| 432 |
+
# Packages loading
|
| 433 |
+
# # -----------------------------------------------------------------------
|
| 434 |
+
library(lapop); library(bslib); library(htmltools); require(bsplus)
|
| 435 |
+
suppressPackageStartupMessages(library(dplyr))
|
| 436 |
+
library(tidyr); library(stringr); library(haven)
|
| 437 |
+
require(shiny); library(shinyWidgets); require(shinyjs)
|
| 438 |
+
suppressPackageStartupMessages(library(Hmisc, exclude = c("src", "summarize", "units", "format.pval")))
|
| 439 |
+
lapop_fonts() # LAPOP GRAPH STYLE
|
| 440 |
+
# IMD CSES Data (only preselected variables)
|
| 441 |
+
# # -----------------------------------------------------------------------
|
| 442 |
+
# RDA FILE BEST COMPRESSION FOR RSHINY
|
| 443 |
+
load(file="./cses_shiny_data.rda"); dstrata<-cses_shiny_data; rm(cses_shiny_data)
|
| 444 |
+
# Labels data (for DP display)
|
| 445 |
+
vars_labels <- read.csv("./cses_variable_labels.csv", encoding = "latin1")
|
| 446 |
+
# Labs vector (for DP display outcomes versus secondary vars that include macro vars)
|
| 447 |
+
labs <- readRDS("./cses_labs.rds")
|
| 448 |
+
labs_sec <- readRDS("./cses_labs_sec.rds")
|
| 449 |
+
# Dropping Demographics (OLD, ALLOW USERS TO USE BOTH RAW AND RECODE DEMOG VARS)
|
| 450 |
+
#drop_demoglabs <- c("IMD2001_2", "IMD2002", "IMD2003", "IMD2006", "IMD2007") # Demographics
|
| 451 |
+
#labs_sec <- labs[ !(unname(labs) %in% drop_demoglabs) ]
|
| 452 |
+
# Error handling function (so app does not break easily)
|
| 453 |
+
Error<-function(x){
|
| 454 |
+
tryCatch(x,error=function(e) return(FALSE))
|
| 455 |
+
}
|
| 456 |
+
# CSES election-year vector for TS
|
| 457 |
+
waves_total = c("1996", "1997", "1998", "1999", "2000", "2001", "2002",
|
| 458 |
+
"2003", "2004", "2005", "2006", "2007", "2008", "2009",
|
| 459 |
+
"2010", "2011", "2012", "2013", "2014", "2015", "2016",
|
| 460 |
+
"2017", "2018", "2019", "2020", "2021")
|
| 461 |
+
# Escape stuff that ggtext/markdown treats specially
|
| 462 |
+
sanitize_for_ggtext <- function(x) {
|
| 463 |
+
x <- as.character(x)
|
| 464 |
+
# HTML specials
|
| 465 |
+
x <- gsub("&", "&", x, fixed = TRUE)
|
| 466 |
+
x <- gsub("<", "<", x, fixed = TRUE)
|
| 467 |
+
x <- gsub(">", ">", x, fixed = TRUE)
|
| 468 |
+
# Markdown link/emphasis/backtick
|
| 469 |
+
x <- gsub("\\[", "[", x)
|
| 470 |
+
x <- gsub("\\]", "]", x)
|
| 471 |
+
x <- gsub("\\(", "(", x)
|
| 472 |
+
x <- gsub("\\)", ")", x)
|
| 473 |
+
x <- gsub("\\*", "*", x)
|
| 474 |
+
x <- gsub("_", "_", x)
|
| 475 |
+
x <- gsub("`", "`", x)
|
| 476 |
+
x
|
| 477 |
+
}
|
| 478 |
+
# # -----------------------------------------------------------------------
|
| 479 |
+
# Helper function for TS (handle missing values at end or middle of series)
|
| 480 |
+
# # -----------------------------------------------------------------------
|
| 481 |
+
omit_na_edges <- function(df) {
|
| 482 |
+
# Find which rows have NA values
|
| 483 |
+
na_rows <- apply(df, 1, function(row) any(is.na(row)))
|
| 484 |
+
# Find the first and last non-NA row
|
| 485 |
+
first_non_na <- which(!na_rows)[1]
|
| 486 |
+
last_non_na <- which(!na_rows)[length(which(!na_rows))]
|
| 487 |
+
# Subset df to only include rows between the first and last non-NA rows
|
| 488 |
+
df_clean <- df[first_non_na:last_non_na, ]
|
| 489 |
+
return(df_clean)
|
| 490 |
+
}
|
| 491 |
+
### MOVE IT TO LAPOP_TS???
|
| 492 |
+
View(dstrata)
|
| 493 |
+
View(vars_labels)
|
| 494 |
+
View(pais_lab_merge)
|
| 495 |
+
View(qword_ro)
|
| 496 |
+
grep("Not at all", qword_ro$responses_en)
|
| 497 |
+
grep("Not at all", qword_ro$responses_en, value = T)
|
| 498 |
+
grep("all", qword_ro$responses_en, value = T)
|
| 499 |
+
grep("not", qword_ro$responses_en, value = T)
|
| 500 |
+
grep("none", qword_ro$responses_en, value = T)
|
| 501 |
+
grep("never", qword_ro$responses_en, value = T)
|
| 502 |
+
grep("never", qword_ro$responses_en, value = T)
|
| 503 |
+
grepl("never", qword_ro$responses_en, value = T)
|
| 504 |
+
grep("(1)", qword_ro$responses_en, value = T)
|
| 505 |
+
grep("no", qword_ro$responses_en, value = T)
|
| 506 |
+
grep("NOT AT ALL", qword_ro$responses_en, value = T)
|
| 507 |
+
grep("NOT AT ALL", qword_ro$responses_en)
|
| 508 |
+
devtools::install_github("lapop-central/lapop",
|
| 509 |
+
force = TRUE,
|
| 510 |
+
build_vignettes = TRUE)
|
| 511 |
+
shiny::runApp()
|
| 512 |
+
runApp()
|
.gitattributes
CHANGED
|
@@ -45,3 +45,7 @@ cses-shiny/www/Roboto-Bold.ttf filter=lfs diff=lfs merge=lfs -text
|
|
| 45 |
cses-shiny/www/Roboto-Light.ttf filter=lfs diff=lfs merge=lfs -text
|
| 46 |
cses-shiny/www/Roboto-Medium.ttf filter=lfs diff=lfs merge=lfs -text
|
| 47 |
cses-shiny/www/Roboto-Regular.ttf filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 45 |
cses-shiny/www/Roboto-Light.ttf filter=lfs diff=lfs merge=lfs -text
|
| 46 |
cses-shiny/www/Roboto-Medium.ttf filter=lfs diff=lfs merge=lfs -text
|
| 47 |
cses-shiny/www/Roboto-Regular.ttf filter=lfs diff=lfs merge=lfs -text
|
| 48 |
+
.RData filter=lfs diff=lfs merge=lfs -text
|
| 49 |
+
cses_shiny_data.rda filter=lfs diff=lfs merge=lfs -text
|
| 50 |
+
cses_shiny_data.rds filter=lfs diff=lfs merge=lfs -text
|
| 51 |
+
world.rda filter=lfs diff=lfs merge=lfs -text
|
.gitignore
CHANGED
|
@@ -1 +1,8 @@
|
|
| 1 |
-
.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
.Rproj.user
|
| 2 |
+
.Rhistory
|
| 3 |
+
.RData
|
| 4 |
+
.Ruserdata
|
| 5 |
+
*.dcf
|
| 6 |
+
rsconnect/shinyapps.io/centerforglobaldemocracy/cses-shiny.dcf
|
| 7 |
+
app_old_warning.R
|
| 8 |
+
app_w_stack.R
|
app.R
ADDED
|
@@ -0,0 +1,1566 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
### CSES DATA PLAYGROUND
|
| 2 |
+
# Date: November 10th, 2025
|
| 3 |
+
# Author: Robert Vidigal, PhD
|
| 4 |
+
# Purpose: CSES Shiny Data Playground based on LAPOP Lab Data Playground
|
| 5 |
+
# Prev file: ./shiny_preprocessing.R
|
| 6 |
+
# Machine: Windows OS
|
| 7 |
+
# Status: On-going
|
| 8 |
+
# # -----------------------------------------------------------------------
|
| 9 |
+
### Data In:
|
| 10 |
+
# 1. cses_shiny_data.rda
|
| 11 |
+
# 2. cses_variable_labels.csv
|
| 12 |
+
# 3. cses_labs.rds
|
| 13 |
+
# 4. and fonts from /wwww/
|
| 14 |
+
### Data Out: N/A
|
| 15 |
+
# # -----------------------------------------------------------------------
|
| 16 |
+
options(shiny.useragg = TRUE) # speed it up
|
| 17 |
+
|
| 18 |
+
# # -----------------------------------------------------------------------
|
| 19 |
+
# Packages loading
|
| 20 |
+
# # -----------------------------------------------------------------------
|
| 21 |
+
library(lapop); library(bslib); library(htmltools); require(bsplus)
|
| 22 |
+
suppressPackageStartupMessages(library(dplyr))
|
| 23 |
+
library(tidyr); library(stringr); library(haven)
|
| 24 |
+
require(shiny); library(shinyWidgets); require(shinyjs); require(ggtext)
|
| 25 |
+
suppressPackageStartupMessages(library(Hmisc, exclude = c("src", "summarize", "units", "format.pval")))
|
| 26 |
+
|
| 27 |
+
lapop_fonts() # LAPOP GRAPH STYLE
|
| 28 |
+
|
| 29 |
+
# IMD CSES Data (only preselected variables)
|
| 30 |
+
# # -----------------------------------------------------------------------
|
| 31 |
+
# RDA FILE BEST COMPRESSION FOR RSHINY
|
| 32 |
+
load(file="./cses_shiny_data.rda");
|
| 33 |
+
|
| 34 |
+
# Labels data (for DP display)
|
| 35 |
+
vars_labels <- read.csv("./cses_variable_labels.csv", encoding = "latin1")
|
| 36 |
+
|
| 37 |
+
# Labs vector (for DP display outcomes versus secondary vars that include macro vars)
|
| 38 |
+
labs <- readRDS("./cses_labs.rds")
|
| 39 |
+
labs_sec <- readRDS("./cses_labs_sec.rds")
|
| 40 |
+
load(file="./world.rda")
|
| 41 |
+
|
| 42 |
+
# Dropping Demographics (OLD, ALLOW USERS TO USE BOTH RAW AND RECODE DEMOG VARS)
|
| 43 |
+
#drop_demoglabs <- c("IMD2001_2", "IMD2002", "IMD2003", "IMD2006", "IMD2007") # Demographics
|
| 44 |
+
#labs_sec <- labs[ !(unname(labs) %in% drop_demoglabs) ]
|
| 45 |
+
|
| 46 |
+
# # -----------------------------------------------------------------------
|
| 47 |
+
# Error handling function (so app does not break easily)
|
| 48 |
+
# # -----------------------------------------------------------------------
|
| 49 |
+
|
| 50 |
+
Error<-function(x){
|
| 51 |
+
tryCatch(x,error=function(e) return(FALSE))
|
| 52 |
+
}
|
| 53 |
+
|
| 54 |
+
# CSES election-year vector for TS
|
| 55 |
+
waves_total = c("1996", "1997", "1998", "1999", "2000", "2001", "2002",
|
| 56 |
+
"2003", "2004", "2005", "2006", "2007", "2008", "2009",
|
| 57 |
+
"2010", "2011", "2012", "2013", "2014", "2015", "2016",
|
| 58 |
+
"2017", "2018", "2019", "2020", "2021")
|
| 59 |
+
|
| 60 |
+
# Escape stuff that ggtext/markdown treats specially
|
| 61 |
+
sanitize_for_ggtext <- function(x) {
|
| 62 |
+
x <- as.character(x)
|
| 63 |
+
# HTML specials
|
| 64 |
+
x <- gsub("&", "&", x, fixed = TRUE)
|
| 65 |
+
x <- gsub("<", "<", x, fixed = TRUE)
|
| 66 |
+
x <- gsub(">", ">", x, fixed = TRUE)
|
| 67 |
+
# Markdown link/emphasis/backtick
|
| 68 |
+
x <- gsub("\\[", "[", x)
|
| 69 |
+
x <- gsub("\\]", "]", x)
|
| 70 |
+
x <- gsub("\\(", "(", x)
|
| 71 |
+
x <- gsub("\\)", ")", x)
|
| 72 |
+
x <- gsub("\\*", "*", x)
|
| 73 |
+
x <- gsub("_", "_", x)
|
| 74 |
+
x <- gsub("`", "`", x)
|
| 75 |
+
x
|
| 76 |
+
}
|
| 77 |
+
|
| 78 |
+
# # -----------------------------------------------------------------------
|
| 79 |
+
# Helper function for TS
|
| 80 |
+
# # -----------------------------------------------------------------------
|
| 81 |
+
# (handle missing values at end or middle of series)
|
| 82 |
+
|
| 83 |
+
omit_na_edges <- function(df) {
|
| 84 |
+
# Find which rows have NA values
|
| 85 |
+
na_rows <- apply(df, 1, function(row) any(is.na(row)))
|
| 86 |
+
|
| 87 |
+
# Find the first and last non-NA row
|
| 88 |
+
first_non_na <- which(!na_rows)[1]
|
| 89 |
+
last_non_na <- which(!na_rows)[length(which(!na_rows))]
|
| 90 |
+
|
| 91 |
+
# Subset df to only include rows between the first and last non-NA rows
|
| 92 |
+
df_clean <- df[first_non_na:last_non_na, ]
|
| 93 |
+
|
| 94 |
+
return(df_clean)
|
| 95 |
+
}
|
| 96 |
+
|
| 97 |
+
# # -----------------------------------------------------------------------
|
| 98 |
+
# Custom weighted averages & CIs, much faster than survey_mean() etc
|
| 99 |
+
# # -----------------------------------------------------------------------
|
| 100 |
+
weighted.ttest.ci <- function(x, weights) {
|
| 101 |
+
nx <- length(x)
|
| 102 |
+
vx <- Hmisc::wtd.var(x, weights, normwt = TRUE, na.rm = TRUE) # Weighted variance
|
| 103 |
+
mx <- weighted.mean(x, weights, na.rm = TRUE) # Weighted mean
|
| 104 |
+
stderr <- sqrt(vx/nx)
|
| 105 |
+
tstat <- mx/stderr ## not mx - mu
|
| 106 |
+
cint <- qt(1 - 0.05/2, nx - 1)
|
| 107 |
+
cint <- tstat + c(-cint, cint)
|
| 108 |
+
confint = cint * stderr
|
| 109 |
+
result = data.frame(prop = mx, lb = confint[1], ub = confint[2])
|
| 110 |
+
return(result)
|
| 111 |
+
}
|
| 112 |
+
|
| 113 |
+
# # -----------------------------------------------------------------------
|
| 114 |
+
# Helper for missing country-year by outcome_var
|
| 115 |
+
# # -----------------------------------------------------------------------
|
| 116 |
+
get_missing_combinations <- function(data, outcome_var, wave_var,
|
| 117 |
+
selected_waves, selected_countries) {
|
| 118 |
+
# Convert wave values to string using haven labels
|
| 119 |
+
data <- data %>%
|
| 120 |
+
mutate(wave_str = as.character(haven::as_factor(.data[[wave_var]])))
|
| 121 |
+
|
| 122 |
+
# Build the full country-wave grid
|
| 123 |
+
all_combos <- expand.grid(
|
| 124 |
+
pais_nam = selected_countries,
|
| 125 |
+
wave = selected_waves,
|
| 126 |
+
stringsAsFactors = FALSE
|
| 127 |
+
)
|
| 128 |
+
|
| 129 |
+
# Subset only relevant countries
|
| 130 |
+
data <- data %>%
|
| 131 |
+
filter(pais_nam %in% selected_countries)
|
| 132 |
+
|
| 133 |
+
# Summarize: how many valid (non-NA and not 0) values exist per combo
|
| 134 |
+
summary <- data %>%
|
| 135 |
+
group_by(pais_nam, wave = wave_str) %>%
|
| 136 |
+
summarise(
|
| 137 |
+
n_valid = sum(!is.na(.data[[outcome_var]]) & .data[[outcome_var]] != 0),
|
| 138 |
+
.groups = "drop"
|
| 139 |
+
)
|
| 140 |
+
|
| 141 |
+
# Merge and detect missing
|
| 142 |
+
missing <- all_combos %>%
|
| 143 |
+
left_join(summary, by = c("pais_nam", "wave")) %>%
|
| 144 |
+
filter(is.na(n_valid) | n_valid == 0) %>%
|
| 145 |
+
select(pais_nam, wave)
|
| 146 |
+
|
| 147 |
+
return(missing)
|
| 148 |
+
}
|
| 149 |
+
|
| 150 |
+
# # -----------------------------------------------------------------------
|
| 151 |
+
# Helper function for mover plot (weighting and handling NAs)
|
| 152 |
+
# # -----------------------------------------------------------------------
|
| 153 |
+
process_data <- function(data, outcome_var, recode_range,
|
| 154 |
+
group_var, var_label, weight_var) {
|
| 155 |
+
|
| 156 |
+
if (is.null(group_var)) {
|
| 157 |
+
return(NULL)
|
| 158 |
+
}
|
| 159 |
+
|
| 160 |
+
processed_data <- data %>%
|
| 161 |
+
drop_na(!!sym(outcome_var)) %>%
|
| 162 |
+
mutate(outcome_rec = case_when(
|
| 163 |
+
is.na(!!sym(outcome_var)) ~ NA_real_,
|
| 164 |
+
!!sym(outcome_var) >= recode_range[1] & !!sym(outcome_var) <= recode_range[2] ~ 100,
|
| 165 |
+
TRUE ~ 0
|
| 166 |
+
)) %>%
|
| 167 |
+
group_by(vallabel = haven::as_factor(haven::zap_missing(!!sym(group_var)))) %>%
|
| 168 |
+
summarise_at(vars("outcome_rec"), list(~weighted.ttest.ci(., !!sym(weight_var)))) %>%
|
| 169 |
+
unnest_wider(col = "outcome_rec") %>%
|
| 170 |
+
mutate(
|
| 171 |
+
varlabel = var_label,
|
| 172 |
+
proplabel = paste0(round(prop), "%")
|
| 173 |
+
) %>%
|
| 174 |
+
drop_na(.)
|
| 175 |
+
|
| 176 |
+
return(processed_data)
|
| 177 |
+
}
|
| 178 |
+
|
| 179 |
+
# # -----------------------------------------------------------------------
|
| 180 |
+
# BOOTSTRAP THEME
|
| 181 |
+
# # -----------------------------------------------------------------------
|
| 182 |
+
cses_theme <- bs_theme(
|
| 183 |
+
version = 5,
|
| 184 |
+
bootswatch = "cosmo",
|
| 185 |
+
bg = "#ffffff",
|
| 186 |
+
fg = "#212529",
|
| 187 |
+
primary = "#C4722A",
|
| 188 |
+
secondary = "#C4722A",
|
| 189 |
+
success = "#28a745",
|
| 190 |
+
info = "#0066cc",
|
| 191 |
+
warning = "#dc3545",
|
| 192 |
+
danger = "#dc3545",
|
| 193 |
+
#base_font = font_google("Open Sans"),
|
| 194 |
+
#heading_font = font_google("Roboto Slab"),
|
| 195 |
+
#code_font = font_google("Fira Mono"),
|
| 196 |
+
#font_scale = 1
|
| 197 |
+
)
|
| 198 |
+
|
| 199 |
+
# # -----------------------------------------------------------------------
|
| 200 |
+
# HOVER POP-UP FOR LEFTSIDE MENU
|
| 201 |
+
# # -----------------------------------------------------------------------
|
| 202 |
+
info_badge <- function(text, title, content) {
|
| 203 |
+
bsplus::bs_embed_popover(
|
| 204 |
+
tags$span(text, tags$span(icon("info-circle"), class = "me-1", style = "color:#C4722A;")),
|
| 205 |
+
title = title,
|
| 206 |
+
content = content,
|
| 207 |
+
placement = "right",
|
| 208 |
+
trigger = "click",
|
| 209 |
+
container = "body"
|
| 210 |
+
)
|
| 211 |
+
}
|
| 212 |
+
|
| 213 |
+
# # -----------------------------------------------------------------------
|
| 214 |
+
# N-SIZE FUNCTION TO PULL COUNTRY-YEAR COMBOS (CHATGPT)
|
| 215 |
+
# # -----------------------------------------------------------------------
|
| 216 |
+
get_sample_counts <- function(
|
| 217 |
+
data, outcome_var,
|
| 218 |
+
wave_var = "wave", country_var = "pais_nam",
|
| 219 |
+
selected_waves = NULL, selected_countries = NULL,
|
| 220 |
+
complete_grid = FALSE
|
| 221 |
+
) {
|
| 222 |
+
df <- data
|
| 223 |
+
if (!is.null(selected_waves)) df <- dplyr::filter(df, .data[[wave_var]] %in% selected_waves)
|
| 224 |
+
if (!is.null(selected_countries)) df <- dplyr::filter(df, .data[[country_var]] %in% selected_countries)
|
| 225 |
+
df <- dplyr::filter(df, !is.na(.data[[outcome_var]]))
|
| 226 |
+
|
| 227 |
+
per_wave <- df |>
|
| 228 |
+
dplyr::count(wave = .data[[wave_var]], name = "n") |>
|
| 229 |
+
dplyr::arrange(wave)
|
| 230 |
+
|
| 231 |
+
per_country <- df |>
|
| 232 |
+
dplyr::count(pais = .data[[country_var]], name = "n") |>
|
| 233 |
+
dplyr::arrange(pais)
|
| 234 |
+
|
| 235 |
+
per_country_wave <- df |>
|
| 236 |
+
dplyr::count(pais = .data[[country_var]], wave = .data[[wave_var]], name = "n") |>
|
| 237 |
+
dplyr::arrange(pais, wave)
|
| 238 |
+
|
| 239 |
+
if (complete_grid) {
|
| 240 |
+
all_waves <- if (!is.null(selected_waves)) selected_waves else sort(unique(data[[wave_var]]))
|
| 241 |
+
all_countries <- if (!is.null(selected_countries)) selected_countries else sort(unique(data[[country_var]]))
|
| 242 |
+
|
| 243 |
+
per_country_wave <- per_country_wave |>
|
| 244 |
+
tidyr::complete(pais = all_countries, wave = all_waves, fill = list(n = 0)) |>
|
| 245 |
+
dplyr::arrange(pais, wave)
|
| 246 |
+
|
| 247 |
+
per_country <- per_country_wave |>
|
| 248 |
+
dplyr::group_by(pais) |>
|
| 249 |
+
dplyr::summarise(n = sum(n), .groups = "drop") |>
|
| 250 |
+
dplyr::arrange(pais)
|
| 251 |
+
|
| 252 |
+
per_wave <- per_country_wave |>
|
| 253 |
+
dplyr::group_by(wave) |>
|
| 254 |
+
dplyr::summarise(n = sum(n), .groups = "drop") |>
|
| 255 |
+
dplyr::arrange(wave)
|
| 256 |
+
}
|
| 257 |
+
|
| 258 |
+
list(
|
| 259 |
+
overall = nrow(df),
|
| 260 |
+
per_wave = per_wave,
|
| 261 |
+
per_country = per_country,
|
| 262 |
+
per_country_wave = per_country_wave
|
| 263 |
+
)
|
| 264 |
+
}
|
| 265 |
+
|
| 266 |
+
# # -----------------------------------------------------------------------
|
| 267 |
+
# # -----------------------------------------------------------------------
|
| 268 |
+
# # -----------------------------------------------------------------------
|
| 269 |
+
|
| 270 |
+
# # -----------------------------------------------------------------------
|
| 271 |
+
# Creating User Interface UI!
|
| 272 |
+
# # -----------------------------------------------------------------------
|
| 273 |
+
ui <- fluidPage(
|
| 274 |
+
|
| 275 |
+
useShinyjs(),
|
| 276 |
+
|
| 277 |
+
theme = cses_theme,
|
| 278 |
+
tags$h2("CSES Data Playground",
|
| 279 |
+
style = "color: #C4722A; font-weight: bold; font-size: 36px;"),
|
| 280 |
+
|
| 281 |
+
sidebarLayout(
|
| 282 |
+
# ----- Sidebar panel for inputs
|
| 283 |
+
sidebarPanel(width = 3,
|
| 284 |
+
selectInput("variable", "Outcome",
|
| 285 |
+
labs[order(names(labs))],
|
| 286 |
+
selected = "IMD3010"),
|
| 287 |
+
# Default picks most recent module
|
| 288 |
+
pickerInput(inputId = "module",
|
| 289 |
+
label = tagList(info_badge("Module",
|
| 290 |
+
HTML("Please select which CSES Modules to be available in the analysis. Then, select which countries and years below."),
|
| 291 |
+
"Module")),
|
| 292 |
+
choices = sort(levels(as_factor(cses_shiny_data$IMD1008_MOD)[!is.na(cses_shiny_data$IMD1008_MOD)])),
|
| 293 |
+
selected = c("MODULE 5"),
|
| 294 |
+
options = list(`actions-box` = TRUE),
|
| 295 |
+
multiple = TRUE),
|
| 296 |
+
|
| 297 |
+
# ----- COUNTRY
|
| 298 |
+
pickerInput(inputId = "pais",
|
| 299 |
+
label = "Countries",
|
| 300 |
+
#tagList(info_badge("Countries",
|
| 301 |
+
#HTML("Please select which countries to be included in the analysis."),
|
| 302 |
+
#"Countries")),
|
| 303 |
+
choices = sort(levels(as_factor(cses_shiny_data$pais)[!is.na(cses_shiny_data$pais)])),
|
| 304 |
+
options = list(`actions-box` = TRUE),
|
| 305 |
+
multiple = TRUE),
|
| 306 |
+
|
| 307 |
+
# ----- WAVE
|
| 308 |
+
pickerInput(inputId = "wave",
|
| 309 |
+
label = "Years",
|
| 310 |
+
#tagList(info_badge("Years",
|
| 311 |
+
# HTML("Please select which years to be included in the analysis."),
|
| 312 |
+
# "Years")),
|
| 313 |
+
choices = c("1996" = "1996", "1997" = "1997", "1998" = "1998",
|
| 314 |
+
"1999" = "1999", "2000" = "2000", "2001" = "2001",
|
| 315 |
+
"2002" = "2002", "2003" = "2003", "2004" = "2004",
|
| 316 |
+
"2005" = "2005", "2006" = "2006", "2007" = "2007",
|
| 317 |
+
"2008" = "2008", "2009" = "2009", "2010" = "2010",
|
| 318 |
+
"2011" = "2011", "2012" = "2012", "2013" = "2013",
|
| 319 |
+
"2014" = "2014", "2015" = "2015", "2016" = "2016",
|
| 320 |
+
"2017" = "2017", "2018" = "2018", "2019" = "2019",
|
| 321 |
+
"2020" = "2020", "2021" = "2021"),
|
| 322 |
+
options = list(`actions-box` = TRUE),
|
| 323 |
+
multiple = TRUE),
|
| 324 |
+
|
| 325 |
+
# ----- WEIGHT selection radio buttons
|
| 326 |
+
bsplus::use_bs_popover(),
|
| 327 |
+
radioButtons(
|
| 328 |
+
inputId = "weight_type",
|
| 329 |
+
label = tagList(info_badge("Weights",
|
| 330 |
+
HTML("Further information on weights is available in <b>Part 6</b> of CSES Module 4."),
|
| 331 |
+
"Weights")),
|
| 332 |
+
# For a link, add:
|
| 333 |
+
# HTML('Further information on weights is available in <b>Part 6</b> of CSES Module 4. <br><a href=\"#\" target=\"_blank\">Open doc</a>')
|
| 334 |
+
choiceValues = c("no_weight", "weight_demographic", "weight_sample"),
|
| 335 |
+
choiceNames = list(
|
| 336 |
+
info_badge("Unweighted", "No weights applied. Raw proportions/percentages.",
|
| 337 |
+
"Unweighted"),
|
| 338 |
+
info_badge("Demographic weight", "Post-stratification targets.",
|
| 339 |
+
"Demographic weight"),
|
| 340 |
+
info_badge("Sample weight", "Design/selection probability weights.",
|
| 341 |
+
"Sample weight")
|
| 342 |
+
),
|
| 343 |
+
selected = "no_weight"),
|
| 344 |
+
|
| 345 |
+
# This fixes a formatting issue with checkboxGroupInput() below
|
| 346 |
+
tags$head(
|
| 347 |
+
tags$style(
|
| 348 |
+
HTML("
|
| 349 |
+
.checkbox-inline {
|
| 350 |
+
margin-left: 0px;
|
| 351 |
+
margin-right: 10px;
|
| 352 |
+
}
|
| 353 |
+
.checkbox-inline+.checkbox-inline {
|
| 354 |
+
margin-left: 0px;
|
| 355 |
+
margin-right: 10px;
|
| 356 |
+
}
|
| 357 |
+
.shiny-notification {
|
| 358 |
+
width: 615px !important; /* max width */
|
| 359 |
+
max-height: 140px; /* max height */
|
| 360 |
+
word-wrap: break-word;
|
| 361 |
+
white-space: normal;
|
| 362 |
+
overflow-y: auto; /* scrollbar */
|
| 363 |
+
right: 330px !important; /* shift away from right edge */
|
| 364 |
+
box-sizing: border-box;
|
| 365 |
+
font-size: 14px;
|
| 366 |
+
}"))),
|
| 367 |
+
|
| 368 |
+
# This triggers the "Generate" button
|
| 369 |
+
tags$script(HTML("
|
| 370 |
+
Shiny.addCustomMessageHandler('clickGenerateButton', function(message) {
|
| 371 |
+
$('#go').click();
|
| 372 |
+
});
|
| 373 |
+
")),
|
| 374 |
+
# This makes the slider input to allow only integers for CSES years
|
| 375 |
+
tags$style(type = "text/css", ".irs-grid-pol.small {height: 0px;}"),
|
| 376 |
+
|
| 377 |
+
# Make popovers white + wire TRUE hover with JavaScript
|
| 378 |
+
tags$style(HTML("
|
| 379 |
+
.popover {
|
| 380 |
+
--bs-popover-bg: #ffffff;
|
| 381 |
+
--bs-popover-border-color: #dddddd;
|
| 382 |
+
--bs-popover-header-bg: #ffffff;
|
| 383 |
+
--bs-popover-header-color: #212529;
|
| 384 |
+
--bs-popover-body-color: #212529;
|
| 385 |
+
border-color: #dddddd;
|
| 386 |
+
}
|
| 387 |
+
.popover .popover-header,
|
| 388 |
+
.popover .popover-body {
|
| 389 |
+
background-color: #ffffff;
|
| 390 |
+
color: #212529;
|
| 391 |
+
}
|
| 392 |
+
")),
|
| 393 |
+
tags$script(HTML("
|
| 394 |
+
(function() {
|
| 395 |
+
function upgradeDataAttr(el){
|
| 396 |
+
// Force manual disable
|
| 397 |
+
if (el.getAttribute('data-toggle') === 'popover') {
|
| 398 |
+
el.setAttribute('data-bs-toggle','popover');
|
| 399 |
+
el.removeAttribute('data-toggle');
|
| 400 |
+
}
|
| 401 |
+
}
|
| 402 |
+
|
| 403 |
+
function getPopover(el){
|
| 404 |
+
// Force manual trigger so we fully control hover behavior
|
| 405 |
+
return bootstrap.Popover.getOrCreateInstance(el, {
|
| 406 |
+
container: 'body',
|
| 407 |
+
trigger: 'manual',
|
| 408 |
+
html: true, sanitize: false
|
| 409 |
+
});
|
| 410 |
+
}
|
| 411 |
+
|
| 412 |
+
function addHoverBehavior(el){
|
| 413 |
+
var timer = null;
|
| 414 |
+
var inst = getPopover(el);
|
| 415 |
+
|
| 416 |
+
function startHide(delay){
|
| 417 |
+
if (timer) clearTimeout(timer);
|
| 418 |
+
timer = setTimeout(function(){
|
| 419 |
+
inst.hide();
|
| 420 |
+
}, delay);
|
| 421 |
+
}
|
| 422 |
+
function cancelHide(){
|
| 423 |
+
if (timer) { clearTimeout(timer); timer = null; }
|
| 424 |
+
}
|
| 425 |
+
|
| 426 |
+
// Show on hover
|
| 427 |
+
el.addEventListener('mouseenter', function(){
|
| 428 |
+
cancelHide();
|
| 429 |
+
inst.show();
|
| 430 |
+
|
| 431 |
+
// Auto-dismiss after 10s (unless user is hovering the popover)
|
| 432 |
+
startHide(10000);
|
| 433 |
+
});
|
| 434 |
+
|
| 435 |
+
// Hide shortly after leaving the icon (unless pointer is on the popover)
|
| 436 |
+
el.addEventListener('mouseleave', function(){
|
| 437 |
+
// small delay to allow moving into the popover
|
| 438 |
+
setTimeout(function(){
|
| 439 |
+
var pop = document.getElementById(el.getAttribute('aria-describedby'));
|
| 440 |
+
if (!pop || !pop.matches(':hover')) inst.hide();
|
| 441 |
+
}, 150);
|
| 442 |
+
});
|
| 443 |
+
|
| 444 |
+
// Keep open while hovering the popover; hide when leaving it
|
| 445 |
+
el.addEventListener('shown.bs.popover', function(){
|
| 446 |
+
var pop = document.getElementById(el.getAttribute('aria-describedby'));
|
| 447 |
+
if (!pop) return;
|
| 448 |
+
pop.addEventListener('mouseenter', cancelHide);
|
| 449 |
+
pop.addEventListener('mouseleave', function(){
|
| 450 |
+
startHide(150); // quick close after leaving the box
|
| 451 |
+
});
|
| 452 |
+
});
|
| 453 |
+
|
| 454 |
+
// Prevent click toggling from fighting our hover logic
|
| 455 |
+
el.addEventListener('click', function(e){ e.preventDefault(); });
|
| 456 |
+
}
|
| 457 |
+
|
| 458 |
+
document.addEventListener('DOMContentLoaded', function(){
|
| 459 |
+
document.querySelectorAll('[data-bs-toggle=\"popover\"], [data-toggle=\"popover\"]').forEach(function(el){
|
| 460 |
+
upgradeDataAttr(el);
|
| 461 |
+
getPopover(el); // ensure BS5 instance exists
|
| 462 |
+
addHoverBehavior(el); // wire hover behavior + auto-dismiss
|
| 463 |
+
});
|
| 464 |
+
});
|
| 465 |
+
})();
|
| 466 |
+
")),
|
| 467 |
+
# Show recode slider only for TS, CC, and mover plots (not for histogram)
|
| 468 |
+
conditionalPanel(
|
| 469 |
+
'input.tabs == "Time Series" |
|
| 470 |
+
input.tabs == "Cross Country" |
|
| 471 |
+
input.tabs == "World Map" |
|
| 472 |
+
input.tabs == "Breakdown"',
|
| 473 |
+
|
| 474 |
+
uiOutput("sliderUI"),
|
| 475 |
+
# Mean Value toggle
|
| 476 |
+
# checkboxInput("use_mean", "Mean value", FALSE),
|
| 477 |
+
),
|
| 478 |
+
|
| 479 |
+
# Add additional breakdown variable in mover plot
|
| 480 |
+
conditionalPanel(
|
| 481 |
+
'input.tabs == "Breakdown"',
|
| 482 |
+
selectInput("variable_sec",
|
| 483 |
+
label = tagList(
|
| 484 |
+
info_badge("Subgroup for analysis",
|
| 485 |
+
HTML("Optionally split the Breakdown plot by another subgroup from the dataset.
|
| 486 |
+
Select <b>None</b> to disable."), "Secondary Variable")),
|
| 487 |
+
c("None" = "None",
|
| 488 |
+
labs_sec[order(names(labs_sec))])),
|
| 489 |
+
checkboxGroupInput("demog", "Demographic Variables",
|
| 490 |
+
c("Gender" = "gendermc",
|
| 491 |
+
"Age" = "age",
|
| 492 |
+
"Income" = "wealth",
|
| 493 |
+
"Education" = "edre",
|
| 494 |
+
"Urban/Rural" = "ur"),
|
| 495 |
+
selected = c("gendermc", "age", "edre"),
|
| 496 |
+
inline = TRUE)),
|
| 497 |
+
# Include button in UI (disabled)
|
| 498 |
+
#actionButton("go", "Generate")
|
| 499 |
+
tags$div(
|
| 500 |
+
style = "display: none;",
|
| 501 |
+
actionButton("go", "Generate"))),
|
| 502 |
+
|
| 503 |
+
# Main panel for displaying outputs ----
|
| 504 |
+
# # -----------------------------------------------------------------------
|
| 505 |
+
mainPanel(
|
| 506 |
+
# Output: Formatted text for caption ----
|
| 507 |
+
h3(textOutput("caption")),
|
| 508 |
+
h5(textOutput("wording")),
|
| 509 |
+
h5(textOutput("response")),
|
| 510 |
+
tabsetPanel(id = "tabs",
|
| 511 |
+
tabPanel("Histogram", plotOutput("hist")),
|
| 512 |
+
tabPanel("Time Series", plotOutput("ts")),
|
| 513 |
+
tabPanel("Cross Country", plotOutput("cc")),
|
| 514 |
+
tabPanel("Breakdown", plotOutput("mover")),
|
| 515 |
+
tabPanel("World Map", plotOutput("map"))),
|
| 516 |
+
|
| 517 |
+
br(),
|
| 518 |
+
fluidRow(column(12,
|
| 519 |
+
tags$div(style = "margin-top:-15px"),
|
| 520 |
+
downloadButton(outputId = "downloadPlot", label = "Download Figure"),
|
| 521 |
+
downloadButton(outputId = "downloadTable", label = "Download Table"),
|
| 522 |
+
tags$div(style = "height:10px"),
|
| 523 |
+
uiOutput("ns_card"),
|
| 524 |
+
#uiOutput("missing_warning_card"),
|
| 525 |
+
)
|
| 526 |
+
)
|
| 527 |
+
)
|
| 528 |
+
)
|
| 529 |
+
)
|
| 530 |
+
|
| 531 |
+
# # -----------------------------------------------------------------------
|
| 532 |
+
# # -----------------------------------------------------------------------
|
| 533 |
+
# # -----------------------------------------------------------------------
|
| 534 |
+
|
| 535 |
+
# # -----------------------------------------------------------------------
|
| 536 |
+
# Define SERVER logic
|
| 537 |
+
# # -----------------------------------------------------------------------
|
| 538 |
+
# The server function will be called when each client (browser) loads the app.
|
| 539 |
+
server <- function(input, output, session) {
|
| 540 |
+
observe({
|
| 541 |
+
req(input$variable)
|
| 542 |
+
if (!input$variable %in% names(cses_shiny_data)) {
|
| 543 |
+
showNotification("Selected variable not found in data!", type = "error")
|
| 544 |
+
}
|
| 545 |
+
if (!input$weight_type %in% names(cses_shiny_data)) {
|
| 546 |
+
showNotification("Selected weight column not found!", type = "error")
|
| 547 |
+
}
|
| 548 |
+
})
|
| 549 |
+
|
| 550 |
+
# Triggers "go" between server and ui to generate default plots
|
| 551 |
+
observe({
|
| 552 |
+
if (!is.null(input$module) && !is.null(input$pais) && !is.null(input$wave)) {
|
| 553 |
+
isolate({
|
| 554 |
+
session$sendCustomMessage("clickGenerateButton", list())
|
| 555 |
+
})
|
| 556 |
+
}
|
| 557 |
+
})
|
| 558 |
+
|
| 559 |
+
# Check the number of selected variables for breakdown
|
| 560 |
+
observeEvent(input$demog, {
|
| 561 |
+
if (length(input$demog) > 3) {
|
| 562 |
+
# Show a warning message
|
| 563 |
+
showNotification(HTML("You should only select a maximum of 3 demographic variables to plot."), type = "warning")
|
| 564 |
+
}
|
| 565 |
+
})
|
| 566 |
+
|
| 567 |
+
# MAKE IT REACTIVE
|
| 568 |
+
# # -----------------------------------------------------------------------
|
| 569 |
+
formulaText <- reactive({
|
| 570 |
+
paste(input$variable)
|
| 571 |
+
})
|
| 572 |
+
|
| 573 |
+
outcome <- reactive({
|
| 574 |
+
input$variable
|
| 575 |
+
})
|
| 576 |
+
|
| 577 |
+
wave <- reactive({
|
| 578 |
+
input$wave
|
| 579 |
+
})
|
| 580 |
+
|
| 581 |
+
outcome_code <- reactive({
|
| 582 |
+
vars_labels$column_name[which(vars_labels$column_name == paste(outcome()))]
|
| 583 |
+
})
|
| 584 |
+
|
| 585 |
+
outcome_lab <- reactive({
|
| 586 |
+
vars_labels$question_short_en[which(vars_labels$column_name == paste(outcome()))]
|
| 587 |
+
})
|
| 588 |
+
|
| 589 |
+
variable_sec <- reactive({
|
| 590 |
+
input$variable_sec
|
| 591 |
+
})
|
| 592 |
+
|
| 593 |
+
variable_sec_lab <- reactive({
|
| 594 |
+
vars_labels$question_short_en[which(vars_labels$column_name == paste(variable_sec()))]
|
| 595 |
+
})
|
| 596 |
+
|
| 597 |
+
sliderParams <- reactiveValues(valuex = c(1, 1))
|
| 598 |
+
|
| 599 |
+
# Reactive: Filter dataset based on selected module(s)
|
| 600 |
+
# # -----------------------------------------------------------------------
|
| 601 |
+
filtered_data <- reactive({
|
| 602 |
+
req(input$module)
|
| 603 |
+
cses_shiny_data %>%
|
| 604 |
+
dplyr::filter(IMD1008_MOD %in% input$module)
|
| 605 |
+
})
|
| 606 |
+
|
| 607 |
+
# OLD CODE THAT WOULD FORCE PRESELECTION, BUT IT BREAKS THE APP WITH FULL DATASET
|
| 608 |
+
# Observe changes in module input to update wave and pais
|
| 609 |
+
observeEvent(filtered_data(), {
|
| 610 |
+
data <- filtered_data()
|
| 611 |
+
|
| 612 |
+
wave_choices <- sort(unique(data$wave))
|
| 613 |
+
pais_choices <- sort(unique(data$pais))
|
| 614 |
+
|
| 615 |
+
updatePickerInput(
|
| 616 |
+
session = session,
|
| 617 |
+
inputId = "wave",
|
| 618 |
+
choices = wave_choices,
|
| 619 |
+
selected = wave_choices # you can leave this empty if no preselection
|
| 620 |
+
)
|
| 621 |
+
#
|
| 622 |
+
# updatePickerInput(
|
| 623 |
+
# session = session,
|
| 624 |
+
# inputId = "pais",
|
| 625 |
+
# choices = pais_choices,
|
| 626 |
+
# selected = pais_choices
|
| 627 |
+
# )
|
| 628 |
+
})
|
| 629 |
+
|
| 630 |
+
all_waves <- sort(unique(cses_shiny_data$wave))
|
| 631 |
+
all_paises <- sort(unique(cses_shiny_data$pais))
|
| 632 |
+
|
| 633 |
+
observeEvent(input$module, {
|
| 634 |
+
req(input$module)
|
| 635 |
+
|
| 636 |
+
# Filter for the selected module
|
| 637 |
+
valid <- dplyr::filter(cses_shiny_data, IMD1008_MOD %in% input$module)
|
| 638 |
+
valid_waves <- sort(unique(valid$wave))
|
| 639 |
+
valid_paises <- sort(unique(valid$pais))
|
| 640 |
+
|
| 641 |
+
wave_disabled <- !(all_waves %in% valid_waves)
|
| 642 |
+
pais_disabled <- !(all_paises %in% valid_paises)
|
| 643 |
+
|
| 644 |
+
# 🔹 Automatically select *all valid waves* when a module is chosen
|
| 645 |
+
shinyWidgets::updatePickerInput(
|
| 646 |
+
session, "wave",
|
| 647 |
+
choices = all_waves,
|
| 648 |
+
selected = valid_waves, # <-- changed line
|
| 649 |
+
choicesOpt = list(
|
| 650 |
+
disabled = wave_disabled,
|
| 651 |
+
style = ifelse(wave_disabled, "color:#999;", "")
|
| 652 |
+
)
|
| 653 |
+
)
|
| 654 |
+
|
| 655 |
+
# 🔹 Automatically select *all valid countries* too (optional)
|
| 656 |
+
shinyWidgets::updatePickerInput(
|
| 657 |
+
session, "pais",
|
| 658 |
+
choices = all_paises,
|
| 659 |
+
selected = valid_paises, # <-- changed line
|
| 660 |
+
choicesOpt = list(
|
| 661 |
+
disabled = pais_disabled,
|
| 662 |
+
style = ifelse(pais_disabled, "color:#999;", "")
|
| 663 |
+
)
|
| 664 |
+
)
|
| 665 |
+
})
|
| 666 |
+
|
| 667 |
+
|
| 668 |
+
# Set default recode slider values:
|
| 669 |
+
# # -----------------------------------------------------------------------
|
| 670 |
+
# 2-point: 1-1
|
| 671 |
+
# 3-point: 3-3
|
| 672 |
+
# 4-point: 1-2
|
| 673 |
+
# 5-point: 4-5
|
| 674 |
+
# 6-point: 3-3
|
| 675 |
+
# 7-point: 5-7
|
| 676 |
+
# 10-point: 8-10
|
| 677 |
+
# ALL OTHER: MEAN
|
| 678 |
+
|
| 679 |
+
# UPDATE SLIDER DEFAULTS AND MEAN BEHAVIOR
|
| 680 |
+
# -----------------------------------------------------------------------
|
| 681 |
+
observeEvent({
|
| 682 |
+
list(input$variable, input$use_mean)
|
| 683 |
+
}, {
|
| 684 |
+
# compute numeric vector safely
|
| 685 |
+
xvals <- suppressWarnings(as.numeric(cses_shiny_data[[formulaText()]]))
|
| 686 |
+
maxval <- max(xvals, na.rm = TRUE)
|
| 687 |
+
|
| 688 |
+
# --- DEFAULT RECODE RANGES ---
|
| 689 |
+
if (maxval == 1) {
|
| 690 |
+
sliderParams$valuex <- c(1, 1)
|
| 691 |
+
} else if (maxval == 2) {
|
| 692 |
+
sliderParams$valuex <- c(1, 1)
|
| 693 |
+
} else if (maxval == 3) {
|
| 694 |
+
sliderParams$valuex <- c(3, 3)
|
| 695 |
+
} else if (maxval == 4) {
|
| 696 |
+
sliderParams$valuex <- c(1, 2)
|
| 697 |
+
} else if (maxval == 5) {
|
| 698 |
+
sliderParams$valuex <- c(4, 5)
|
| 699 |
+
} else if (maxval == 6) {
|
| 700 |
+
sliderParams$valuex <- c(3, 3)
|
| 701 |
+
} else if (maxval == 7) {
|
| 702 |
+
sliderParams$valuex <- c(5, 7)
|
| 703 |
+
} else if (maxval == 10) {
|
| 704 |
+
sliderParams$valuex <- c(8, 10)
|
| 705 |
+
} else {
|
| 706 |
+
mean_val <- mean(xvals, na.rm = TRUE)
|
| 707 |
+
sliderParams$valuex <- c(mean_val, mean_val)
|
| 708 |
+
}
|
| 709 |
+
|
| 710 |
+
# --- IF USER SELECTED "USE MEAN VALUE" ---
|
| 711 |
+
if (isTRUE(input$use_mean)) {
|
| 712 |
+
mean_val <- mean(xvals, na.rm = TRUE)
|
| 713 |
+
sliderParams$valuex <- c(mean_val, mean_val)
|
| 714 |
+
}
|
| 715 |
+
|
| 716 |
+
# force slider update
|
| 717 |
+
updateSliderInput(
|
| 718 |
+
session,
|
| 719 |
+
inputId = "recode",
|
| 720 |
+
value = sliderParams$valuex
|
| 721 |
+
)
|
| 722 |
+
})
|
| 723 |
+
|
| 724 |
+
# RECODE SLIDER
|
| 725 |
+
# # -----------------------------------------------------------------------
|
| 726 |
+
output$sliderUI <- renderUI({
|
| 727 |
+
sliderInput(
|
| 728 |
+
inputId = "recode",
|
| 729 |
+
label = tagList(
|
| 730 |
+
info_badge(
|
| 731 |
+
"Which values do you want to graph?",
|
| 732 |
+
HTML("Please select which outcome values to be displayed."),
|
| 733 |
+
"Which values do you want to graph?"
|
| 734 |
+
)
|
| 735 |
+
),
|
| 736 |
+
min = min(as.numeric(cses_shiny_data[[formulaText()]]), na.rm = TRUE),
|
| 737 |
+
max = max(as.numeric(cses_shiny_data[[formulaText()]]), na.rm = TRUE),
|
| 738 |
+
value = sliderParams$valuex,
|
| 739 |
+
step = 1
|
| 740 |
+
)
|
| 741 |
+
})
|
| 742 |
+
|
| 743 |
+
|
| 744 |
+
# Filtering data based on user's selection (dff)
|
| 745 |
+
dff <- eventReactive(input$go, ignoreNULL = FALSE, {
|
| 746 |
+
cses_shiny_data %>%
|
| 747 |
+
dplyr::filter(as_factor(wave) %in% input$wave) %>% # year
|
| 748 |
+
dplyr::filter(pais_nam %in% input$pais) # country
|
| 749 |
+
})
|
| 750 |
+
|
| 751 |
+
# Rendering var caption based on user's var selection
|
| 752 |
+
cap <- renderText({
|
| 753 |
+
vars_labels$question_short_en[which(vars_labels$column_name == formulaText())]
|
| 754 |
+
})
|
| 755 |
+
output$caption <- renderText({
|
| 756 |
+
cap()
|
| 757 |
+
})
|
| 758 |
+
|
| 759 |
+
# Rendering variable code + wording based on user's var selection
|
| 760 |
+
word <- renderText({
|
| 761 |
+
paste0(toupper(vars_labels$column_name[which(vars_labels$column_name == formulaText())]), ". ",
|
| 762 |
+
vars_labels$question_en[which(vars_labels$column_name == formulaText())])
|
| 763 |
+
})
|
| 764 |
+
output$wording <- renderText({
|
| 765 |
+
word()
|
| 766 |
+
})
|
| 767 |
+
|
| 768 |
+
# Rendering ROs based on user's var selection
|
| 769 |
+
resp <- renderText({
|
| 770 |
+
vars_labels$responses_en_rec[which(vars_labels$column_name == formulaText())]
|
| 771 |
+
})
|
| 772 |
+
output$response <- renderText({
|
| 773 |
+
resp()
|
| 774 |
+
})
|
| 775 |
+
|
| 776 |
+
# Rendering variable_sec ROs
|
| 777 |
+
resp_sec <- renderText({
|
| 778 |
+
vars_labels$responses_en_rec[which(vars_labels$column_name == input$variable_sec)]
|
| 779 |
+
})
|
| 780 |
+
output$response_sec <- renderText({
|
| 781 |
+
resp_sec()
|
| 782 |
+
})
|
| 783 |
+
|
| 784 |
+
# Rendering User selected recode value(s)
|
| 785 |
+
slider_values <- renderText({
|
| 786 |
+
if(input$recode[1] == input$recode[2]) {
|
| 787 |
+
paste0("(value: ", unique(input$recode), ")")
|
| 788 |
+
} else {
|
| 789 |
+
paste0("(range: ", paste(input$recode, collapse = " to "), ")")
|
| 790 |
+
}
|
| 791 |
+
})
|
| 792 |
+
output$selected_values <- renderText({
|
| 793 |
+
slider_values()
|
| 794 |
+
})
|
| 795 |
+
|
| 796 |
+
|
| 797 |
+
# Toggle recode slider
|
| 798 |
+
# # -----------------------------------------------------------------------
|
| 799 |
+
observe({
|
| 800 |
+
if (isTRUE(input$use_mean)) {
|
| 801 |
+
shinyjs::disable("recode")
|
| 802 |
+
} else {
|
| 803 |
+
shinyjs::enable("recode")
|
| 804 |
+
}
|
| 805 |
+
})
|
| 806 |
+
|
| 807 |
+
# # -----------------------------------------------------------------------
|
| 808 |
+
# N-SIZE CARD
|
| 809 |
+
# # -----------------------------------------------------------------------
|
| 810 |
+
output$ns_card <- renderUI({
|
| 811 |
+
req(dff(), outcome(), input$wave, input$pais)
|
| 812 |
+
|
| 813 |
+
selected_waves <- as.character(input$wave)
|
| 814 |
+
selected_countries <- as.character(input$pais)
|
| 815 |
+
|
| 816 |
+
ns <- get_sample_counts(
|
| 817 |
+
data = dff(),
|
| 818 |
+
outcome_var = outcome(),
|
| 819 |
+
wave_var = "wave",
|
| 820 |
+
country_var = "pais_nam", # adjust if your helper uses a different input col
|
| 821 |
+
selected_waves = selected_waves,
|
| 822 |
+
selected_countries = selected_countries
|
| 823 |
+
)
|
| 824 |
+
|
| 825 |
+
# If absolutely no non-missing data, show a gentle note
|
| 826 |
+
if (is.null(ns$overall) || ns$overall == 0) {
|
| 827 |
+
return(tags$div(
|
| 828 |
+
style = "border:2px solid #17a2b8; border-radius:8px; padding:14px; background:#e9f7ff; margin-bottom:20px;",
|
| 829 |
+
HTML(paste0("ℹ️ <b>Ns</b> for <b>", outcome(), "</b>: No non-missing observations in the current selection."))
|
| 830 |
+
))
|
| 831 |
+
}
|
| 832 |
+
|
| 833 |
+
# Expect columns: ns$per_wave (wave, n) and ns$per_country_wave (pais, wave, n)
|
| 834 |
+
pCW <- ns$per_country_wave
|
| 835 |
+
# If your helper names the country column differently, change "pais" below
|
| 836 |
+
|
| 837 |
+
# Control whether to show zeros
|
| 838 |
+
show_zeros <- FALSE
|
| 839 |
+
|
| 840 |
+
# Order waves nicely
|
| 841 |
+
waves <- unique(pCW$wave)
|
| 842 |
+
|
| 843 |
+
# If waves are numeric-like but char, coerce to numeric for sorting (silently)
|
| 844 |
+
suppressWarnings({
|
| 845 |
+
wave_num <- suppressWarnings(as.numeric(as.character(waves)))
|
| 846 |
+
if (all(!is.na(wave_num))) waves <- waves[order(wave_num)] else waves <- sort(waves)
|
| 847 |
+
})
|
| 848 |
+
|
| 849 |
+
# Create a quick lookup for total N per wave
|
| 850 |
+
per_wave_tbl <- ns$per_wave |>
|
| 851 |
+
dplyr::mutate(wave_chr = as.character(wave)) |>
|
| 852 |
+
dplyr::select(wave_chr, n)
|
| 853 |
+
|
| 854 |
+
# Build one <details> block per wave
|
| 855 |
+
blocks <- lapply(seq_along(waves), function(i) {
|
| 856 |
+
w <- waves[i]
|
| 857 |
+
w_chr <- as.character(w)
|
| 858 |
+
|
| 859 |
+
wt <- per_wave_tbl$n[match(w_chr, per_wave_tbl$wave_chr)]
|
| 860 |
+
wt <- ifelse(is.na(wt), 0, wt)
|
| 861 |
+
|
| 862 |
+
rows <- pCW |>
|
| 863 |
+
dplyr::filter(as.character(wave) == w_chr)
|
| 864 |
+
|
| 865 |
+
if (!show_zeros) rows <- dplyr::filter(rows, n > 0)
|
| 866 |
+
|
| 867 |
+
rows <- dplyr::arrange(rows, dplyr::desc(n), .by_group = FALSE)
|
| 868 |
+
|
| 869 |
+
items <- lapply(seq_len(nrow(rows)), function(j) {
|
| 870 |
+
n_j <- format(rows$n[j], big.mark = ",")
|
| 871 |
+
is_zero <- isTRUE(rows$n[j] == 0)
|
| 872 |
+
li_style <- if (is_zero) "color:#6c757d;" else NULL
|
| 873 |
+
# country column is "pais" as returned by the helper
|
| 874 |
+
tags$li(
|
| 875 |
+
tags$span(HTML(paste0("<b>", rows$pais[j], "</b>: N=", n_j))),
|
| 876 |
+
style = li_style
|
| 877 |
+
)
|
| 878 |
+
})
|
| 879 |
+
|
| 880 |
+
tags$details(
|
| 881 |
+
open = (i == 1), # first year open by default
|
| 882 |
+
class = "ns-year",
|
| 883 |
+
tags$summary(
|
| 884 |
+
HTML(paste0("<b>", w_chr, "</b> — Total N=", format(wt, big.mark=",")))
|
| 885 |
+
),
|
| 886 |
+
tags$ul(items)
|
| 887 |
+
)
|
| 888 |
+
})
|
| 889 |
+
|
| 890 |
+
tags$div(
|
| 891 |
+
style = "border:2px solid #17a2b8;
|
| 892 |
+
border-radius:8px;
|
| 893 |
+
padding:14px;
|
| 894 |
+
background:#e9f7ff;
|
| 895 |
+
margin-bottom:20px;
|
| 896 |
+
max-height:180px;
|
| 897 |
+
overflow-y:auto;",
|
| 898 |
+
# Title + grand total
|
| 899 |
+
tags$div(
|
| 900 |
+
HTML(paste0(
|
| 901 |
+
"📊 <b>Sample sizes</b> (non-missing <b>", outcome(), "</b>)<br>",
|
| 902 |
+
"<b>Total across selection:</b> ", format(ns$overall, big.mark = ",")
|
| 903 |
+
)),
|
| 904 |
+
style = "margin-bottom:6px;"
|
| 905 |
+
),
|
| 906 |
+
tags$hr(style="margin:8px 0;"),
|
| 907 |
+
# Small CSS polish for the dropdowns
|
| 908 |
+
tags$style(HTML("
|
| 909 |
+
details.ns-year { margin-bottom: 8px; }
|
| 910 |
+
details > summary { cursor: pointer; list-style: none; }
|
| 911 |
+
details > summary::-webkit-details-marker { display: none; }
|
| 912 |
+
")),
|
| 913 |
+
blocks
|
| 914 |
+
)
|
| 915 |
+
})
|
| 916 |
+
|
| 917 |
+
# -----------------------------------------------------------------------
|
| 918 |
+
# SOURCE INFO WITH ACTUAL DATA AVAILABILITY (not just user selections)
|
| 919 |
+
# -----------------------------------------------------------------------
|
| 920 |
+
source_info_both <- reactive({
|
| 921 |
+
req(dff(), outcome(), input$wave, input$pais, input$module)
|
| 922 |
+
|
| 923 |
+
# Reuse your helper to get Ns
|
| 924 |
+
ns <- get_sample_counts(
|
| 925 |
+
data = dff(),
|
| 926 |
+
outcome_var = outcome(),
|
| 927 |
+
wave_var = "wave",
|
| 928 |
+
country_var = "pais_nam",
|
| 929 |
+
selected_waves = input$wave,
|
| 930 |
+
selected_countries = input$pais
|
| 931 |
+
)
|
| 932 |
+
|
| 933 |
+
# Extract actual combinations with nonzero N
|
| 934 |
+
valid_combos <- ns$per_country_wave %>%
|
| 935 |
+
dplyr::filter(n > 0)
|
| 936 |
+
|
| 937 |
+
# Actual waves and countries that have data
|
| 938 |
+
valid_waves <- sort(unique(valid_combos$wave))
|
| 939 |
+
valid_countries <- sort(unique(valid_combos$pais))
|
| 940 |
+
selected_module = input$module
|
| 941 |
+
|
| 942 |
+
# Get abbreviations for these countries (match order)
|
| 943 |
+
pais_abbr <- cses_shiny_data %>%
|
| 944 |
+
dplyr::filter(pais_nam %in% valid_countries) %>%
|
| 945 |
+
distinct(pais_nam, pais_lab) %>%
|
| 946 |
+
arrange(match(pais_nam, valid_countries)) %>%
|
| 947 |
+
pull(pais_lab)
|
| 948 |
+
|
| 949 |
+
paste0(
|
| 950 |
+
"Source: CSES Data Playground\n\n",
|
| 951 |
+
str_wrap(paste0("CSES ", selected_module,
|
| 952 |
+
" - Years: ", paste(valid_waves, collapse = ", "),
|
| 953 |
+
". Countries: ", paste(pais_abbr, collapse = ", ")
|
| 954 |
+
), 130),
|
| 955 |
+
"\n\n",
|
| 956 |
+
str_wrap(paste0(word(), " ", resp()), 130)
|
| 957 |
+
)
|
| 958 |
+
})
|
| 959 |
+
|
| 960 |
+
# -----------------------------------------------------------------------
|
| 961 |
+
source_info_pais <- reactive({
|
| 962 |
+
req(dff(), outcome(), input$wave, input$pais)
|
| 963 |
+
|
| 964 |
+
ns <- get_sample_counts(
|
| 965 |
+
data = dff(),
|
| 966 |
+
outcome_var = outcome(),
|
| 967 |
+
wave_var = "wave",
|
| 968 |
+
country_var = "pais_nam",
|
| 969 |
+
selected_waves = input$wave,
|
| 970 |
+
selected_countries = input$pais
|
| 971 |
+
)
|
| 972 |
+
|
| 973 |
+
valid_combos <- ns$per_country_wave %>%
|
| 974 |
+
dplyr::filter(n > 0)
|
| 975 |
+
|
| 976 |
+
valid_countries <- sort(unique(valid_combos$pais))
|
| 977 |
+
|
| 978 |
+
pais_abbr <- cses_shiny_data %>%
|
| 979 |
+
dplyr::filter(pais_nam %in% valid_countries) %>%
|
| 980 |
+
distinct(pais_nam, pais_lab) %>%
|
| 981 |
+
arrange(match(pais_nam, valid_countries)) %>%
|
| 982 |
+
pull(pais_lab)
|
| 983 |
+
|
| 984 |
+
paste0(
|
| 985 |
+
"Source: CSES Data Playground\n",
|
| 986 |
+
"Countries: ", str_wrap(paste(pais_abbr, collapse = ", "), 130),
|
| 987 |
+
"\n\n",
|
| 988 |
+
str_wrap(paste0(word(), " ", resp()), 130)
|
| 989 |
+
)
|
| 990 |
+
})
|
| 991 |
+
|
| 992 |
+
# -----------------------------------------------------------------------
|
| 993 |
+
source_info_wave <- reactive({
|
| 994 |
+
req(dff(), outcome(), input$wave, input$pais, input$module)
|
| 995 |
+
|
| 996 |
+
ns <- get_sample_counts(
|
| 997 |
+
data = dff(),
|
| 998 |
+
outcome_var = outcome(),
|
| 999 |
+
wave_var = "wave",
|
| 1000 |
+
country_var = "pais_nam",
|
| 1001 |
+
selected_waves = input$wave,
|
| 1002 |
+
selected_countries = input$pais
|
| 1003 |
+
)
|
| 1004 |
+
|
| 1005 |
+
valid_combos <- ns$per_country_wave %>%
|
| 1006 |
+
dplyr::filter(n > 0)
|
| 1007 |
+
valid_waves <- sort(unique(valid_combos$wave))
|
| 1008 |
+
selected_module = input$module
|
| 1009 |
+
|
| 1010 |
+
paste0(
|
| 1011 |
+
"Source: CSES Data Playground\n", "CSES ", selected_module,
|
| 1012 |
+
" - Years: ", str_wrap(paste(valid_waves, collapse = ", "), 130),
|
| 1013 |
+
"\n\n",
|
| 1014 |
+
str_wrap(paste0(word(), " ", resp()), 130)
|
| 1015 |
+
)
|
| 1016 |
+
})
|
| 1017 |
+
# # -----------------------------------------------------------------------
|
| 1018 |
+
# PLOTS
|
| 1019 |
+
# # -----------------------------------------------------------------------
|
| 1020 |
+
|
| 1021 |
+
# Histogram
|
| 1022 |
+
# # -----------------------------------------------------------------------
|
| 1023 |
+
# must break into data event, graph event, and renderPlot to get download to work
|
| 1024 |
+
histd <- reactive({
|
| 1025 |
+
req(dff(), input$variable, input$weight_type)
|
| 1026 |
+
|
| 1027 |
+
if (!input$variable %in% names(dff()) ||
|
| 1028 |
+
!input$weight_type %in% names(dff())) {
|
| 1029 |
+
return(NULL)
|
| 1030 |
+
}
|
| 1031 |
+
|
| 1032 |
+
tryCatch({
|
| 1033 |
+
dff() %>%
|
| 1034 |
+
drop_na(!!sym(input$variable), !!sym(input$weight_type)) %>%
|
| 1035 |
+
group_by(cat = haven::as_factor(!!sym(input$variable))) %>%
|
| 1036 |
+
summarise(w = sum(!!sym(input$weight_type), na.rm = TRUE)) %>%
|
| 1037 |
+
mutate(
|
| 1038 |
+
prop = w / sum(w) * 100,
|
| 1039 |
+
proplabel = paste0(round(prop), "%"),
|
| 1040 |
+
cat = str_wrap(as.character(cat), width = 25)
|
| 1041 |
+
)
|
| 1042 |
+
}, error = function(e) {
|
| 1043 |
+
NULL
|
| 1044 |
+
})
|
| 1045 |
+
})
|
| 1046 |
+
|
| 1047 |
+
histg <- reactive({lapop_hist(histd(),
|
| 1048 |
+
ymax = ifelse(any(histd()$prop > 90), 110, 100),
|
| 1049 |
+
source_info = "Source: CSES Data Playground")})
|
| 1050 |
+
|
| 1051 |
+
output$hist <- renderPlot({
|
| 1052 |
+
req(dff(), nrow(dff()) > 0, input$variable, input$variable %in% names(dff()))
|
| 1053 |
+
return(histg())
|
| 1054 |
+
})
|
| 1055 |
+
|
| 1056 |
+
# Time-series
|
| 1057 |
+
# # -----------------------------------------------------------------------
|
| 1058 |
+
tsd <- reactive({
|
| 1059 |
+
dta_ts <- Error(
|
| 1060 |
+
dff() %>%
|
| 1061 |
+
drop_na(!!sym(outcome()), !!sym(input$weight_type)) %>%
|
| 1062 |
+
mutate(outcome_rec = case_when(
|
| 1063 |
+
!!sym(outcome()) >= input$recode[1] &
|
| 1064 |
+
!!sym(outcome()) <= input$recode[2] ~ 100,
|
| 1065 |
+
TRUE ~ 0
|
| 1066 |
+
)) %>%
|
| 1067 |
+
group_by(wave = as.character(as_factor(wave))) %>%
|
| 1068 |
+
summarise_at(
|
| 1069 |
+
vars("outcome_rec"),
|
| 1070 |
+
list(~weighted.ttest.ci(., !!sym(input$weight_type)))
|
| 1071 |
+
) %>%
|
| 1072 |
+
unnest_wider(col = "outcome_rec") %>%
|
| 1073 |
+
mutate(proplabel = paste0(round(prop), "%")) %>%
|
| 1074 |
+
dplyr::filter(prop != 0)
|
| 1075 |
+
)
|
| 1076 |
+
|
| 1077 |
+
validate(
|
| 1078 |
+
need(dta_ts, "Error: no data available. Please verify that this question was asked in this country/year combination.")
|
| 1079 |
+
)
|
| 1080 |
+
|
| 1081 |
+
dta_ts <- merge(dta_ts,
|
| 1082 |
+
data.frame(wave = as.character(waves_total), empty = 1),
|
| 1083 |
+
by = "wave", all.y = TRUE) %>%
|
| 1084 |
+
dplyr::filter(!is.na(prop)) # TO EXCLUDE YEARS NOT IN THE SELECTION
|
| 1085 |
+
# THEN YEARS ARE NOT SEQUENTIAL
|
| 1086 |
+
|
| 1087 |
+
return(omit_na_edges(dta_ts))
|
| 1088 |
+
})
|
| 1089 |
+
|
| 1090 |
+
tsg <- reactive({lapop_ts(tsd(),
|
| 1091 |
+
ymax = ifelse(any(tsd()$prop > 85, na.rm = TRUE), 110, 100),
|
| 1092 |
+
#label_vjust = -1.5,
|
| 1093 |
+
label_vjust = ifelse(any(tsd()$prop > 80, na.rm = TRUE), -1.1, -1.5),
|
| 1094 |
+
source_info = "Source: CSES Data Playground",
|
| 1095 |
+
subtitle = "% in selected category",
|
| 1096 |
+
ci_type = "errorbar")
|
| 1097 |
+
})
|
| 1098 |
+
|
| 1099 |
+
output$ts <- renderPlot({
|
| 1100 |
+
return(tsg())
|
| 1101 |
+
})
|
| 1102 |
+
|
| 1103 |
+
# Cross Country
|
| 1104 |
+
# # -----------------------------------------------------------------------
|
| 1105 |
+
# define macro (aggregate-level) variables
|
| 1106 |
+
continuous_vars <- c("IMD3001_TS", "IMD5054_2", "IMD5057_1", "IMD5035",
|
| 1107 |
+
"IMD5056_2", "IMD5055_1", "IMD5053_1", "IMD5052_2")
|
| 1108 |
+
|
| 1109 |
+
ccd <- reactive({
|
| 1110 |
+
var_sel <- outcome()
|
| 1111 |
+
rec_min <- input$recode[1]
|
| 1112 |
+
rec_max <- input$recode[2]
|
| 1113 |
+
|
| 1114 |
+
# CASE 1: Continuous macro-level variable (mean = TRUE)
|
| 1115 |
+
if (var_sel %in% continuous_vars) {
|
| 1116 |
+
curr_outcome <- sym(var_sel)
|
| 1117 |
+
|
| 1118 |
+
dta_cc <- dff() %>%
|
| 1119 |
+
# Apply recode range first
|
| 1120 |
+
mutate(
|
| 1121 |
+
tmp_val = as.numeric(!!curr_outcome),
|
| 1122 |
+
tmp_val = ifelse(tmp_val >= rec_min & tmp_val <= rec_max, tmp_val, NA_real_)
|
| 1123 |
+
) %>%
|
| 1124 |
+
group_by(vallabel = pais_lab) %>%
|
| 1125 |
+
summarise(
|
| 1126 |
+
prop = mean(tmp_val, na.rm = TRUE)
|
| 1127 |
+
) %>%
|
| 1128 |
+
ungroup() %>%
|
| 1129 |
+
mutate(
|
| 1130 |
+
lb = NA_real_,
|
| 1131 |
+
ub = NA_real_,
|
| 1132 |
+
proplabel = ifelse(is.na(prop), "", sprintf("%.2f", prop))
|
| 1133 |
+
) %>%
|
| 1134 |
+
filter(!is.na(prop))
|
| 1135 |
+
|
| 1136 |
+
# CASE 2: Regular individual-level variable (weighted proportion)
|
| 1137 |
+
} else {
|
| 1138 |
+
|
| 1139 |
+
dta_cc <- dff() %>%
|
| 1140 |
+
drop_na(!!sym(var_sel), !!sym(input$weight_type)) %>%
|
| 1141 |
+
mutate(outcome_rec = case_when(
|
| 1142 |
+
!!sym(var_sel) >= rec_min & !!sym(var_sel) <= rec_max ~ 100,
|
| 1143 |
+
TRUE ~ 0
|
| 1144 |
+
)) %>%
|
| 1145 |
+
group_by(vallabel = pais_lab) %>%
|
| 1146 |
+
summarise_at(
|
| 1147 |
+
vars("outcome_rec"),
|
| 1148 |
+
list(~weighted.ttest.ci(., !!sym(input$weight_type)))
|
| 1149 |
+
) %>%
|
| 1150 |
+
unnest_wider(col = "outcome_rec") %>%
|
| 1151 |
+
filter(prop != 0) %>%
|
| 1152 |
+
mutate(proplabel = paste0(round(prop), "%"))
|
| 1153 |
+
}
|
| 1154 |
+
|
| 1155 |
+
validate(
|
| 1156 |
+
need(nrow(dta_cc) > 0,
|
| 1157 |
+
"Error: no data available. Please verify that this question was asked in this country/year combination.")
|
| 1158 |
+
)
|
| 1159 |
+
|
| 1160 |
+
return(dta_cc)
|
| 1161 |
+
})
|
| 1162 |
+
|
| 1163 |
+
ccg <- reactive({
|
| 1164 |
+
lapop_cc(
|
| 1165 |
+
ccd(),
|
| 1166 |
+
sort = "hi-lo",
|
| 1167 |
+
subtitle = ifelse(outcome() %in% continuous_vars,
|
| 1168 |
+
"Countries (within selected range)",
|
| 1169 |
+
"% in selected category"),
|
| 1170 |
+
ymax = ifelse(outcome() %in% continuous_vars, 6,
|
| 1171 |
+
ifelse(any(ccd()$prop > 90, na.rm = TRUE), 110, 100)),
|
| 1172 |
+
source_info = "Source: CSES Data Playground"
|
| 1173 |
+
)
|
| 1174 |
+
})
|
| 1175 |
+
|
| 1176 |
+
output$cc <- renderPlot({
|
| 1177 |
+
ccg()
|
| 1178 |
+
})
|
| 1179 |
+
|
| 1180 |
+
# World Map
|
| 1181 |
+
# # -----------------------------------------------------------------------
|
| 1182 |
+
mapd <- reactive({
|
| 1183 |
+
|
| 1184 |
+
var_sel <- outcome()
|
| 1185 |
+
rec_min <- input$recode[1]
|
| 1186 |
+
rec_max <- input$recode[2]
|
| 1187 |
+
|
| 1188 |
+
continuous_vars <- c("IMD3001_TS", "IMD5054_2", "IMD5057_1", "IMD5035",
|
| 1189 |
+
"IMD5056_2", "IMD5055_1", "IMD5053_1", "IMD5052_2")
|
| 1190 |
+
|
| 1191 |
+
req(input$module)
|
| 1192 |
+
|
| 1193 |
+
# --- NEW: allow only one module at a time ---
|
| 1194 |
+
validate(
|
| 1195 |
+
need(
|
| 1196 |
+
length(input$module) == 1,
|
| 1197 |
+
"Please select only ONE module to display a map."
|
| 1198 |
+
)
|
| 1199 |
+
)
|
| 1200 |
+
|
| 1201 |
+
# CASE 1: Continuous macro variable (mean values)
|
| 1202 |
+
if (var_sel %in% continuous_vars) {
|
| 1203 |
+
|
| 1204 |
+
dta_map <- dff() %>%
|
| 1205 |
+
mutate(
|
| 1206 |
+
tmp_val = as.numeric(.data[[var_sel]]),
|
| 1207 |
+
tmp_val = ifelse(tmp_val >= rec_min & tmp_val <= rec_max, tmp_val, NA_real_)
|
| 1208 |
+
) %>%
|
| 1209 |
+
group_by(pais_lab = pais_lab) %>% # IMPORTANT: must exist in your dataset
|
| 1210 |
+
summarise(
|
| 1211 |
+
value = mean(tmp_val, na.rm = TRUE),
|
| 1212 |
+
.groups = "drop"
|
| 1213 |
+
) %>%
|
| 1214 |
+
filter(!is.na(value))
|
| 1215 |
+
|
| 1216 |
+
} else {
|
| 1217 |
+
|
| 1218 |
+
# CASE 2: Categorical / proportion variables
|
| 1219 |
+
dta_map <- dff() %>%
|
| 1220 |
+
drop_na(.data[[var_sel]], .data[[input$weight_type]]) %>%
|
| 1221 |
+
mutate(outcome_rec = case_when(
|
| 1222 |
+
.data[[var_sel]] >= rec_min & .data[[var_sel]] <= rec_max ~ 100,
|
| 1223 |
+
TRUE ~ 0
|
| 1224 |
+
)) %>%
|
| 1225 |
+
group_by(pais_lab = pais_lab) %>%
|
| 1226 |
+
summarise_at(
|
| 1227 |
+
vars("outcome_rec"),
|
| 1228 |
+
list(~weighted.ttest.ci(., .data[[input$weight_type]]))
|
| 1229 |
+
) %>%
|
| 1230 |
+
unnest_wider(col = "outcome_rec") %>%
|
| 1231 |
+
filter(prop > 0) %>%
|
| 1232 |
+
rename(value = prop)
|
| 1233 |
+
}
|
| 1234 |
+
|
| 1235 |
+
validate(
|
| 1236 |
+
need(nrow(dta_map) > 0,
|
| 1237 |
+
"Error: no map data available for this country/year/variable selection.")
|
| 1238 |
+
)
|
| 1239 |
+
|
| 1240 |
+
return(dta_map)
|
| 1241 |
+
})
|
| 1242 |
+
|
| 1243 |
+
mapg <- reactive({
|
| 1244 |
+
lapop_map(
|
| 1245 |
+
mapd(), survey = "CSES",
|
| 1246 |
+
source_info = "\nSource: CSES Data Playground"
|
| 1247 |
+
)
|
| 1248 |
+
})
|
| 1249 |
+
|
| 1250 |
+
output$map <- renderPlot({
|
| 1251 |
+
mapg()
|
| 1252 |
+
})
|
| 1253 |
+
|
| 1254 |
+
# Breakdown
|
| 1255 |
+
# # -----------------------------------------------------------------------
|
| 1256 |
+
# Use function for each demographic breakdown variable
|
| 1257 |
+
|
| 1258 |
+
secdf <- eventReactive(input$go, ignoreNULL = FALSE, {
|
| 1259 |
+
if (input$variable_sec == "None") {
|
| 1260 |
+
NULL
|
| 1261 |
+
} else if (variable_sec() == outcome()) {
|
| 1262 |
+
showNotification(HTML("You cannot break the outcome variable by itself."), type = "error")
|
| 1263 |
+
NULL
|
| 1264 |
+
} else {
|
| 1265 |
+
|
| 1266 |
+
process_data(
|
| 1267 |
+
data = dff(),
|
| 1268 |
+
outcome_var = outcome(),
|
| 1269 |
+
recode_range = input$recode,
|
| 1270 |
+
group_var = input$variable_sec,
|
| 1271 |
+
weight_var = input$weight_type,
|
| 1272 |
+
var_label = stringr::str_wrap(variable_sec_lab(), width = 25)
|
| 1273 |
+
)
|
| 1274 |
+
}
|
| 1275 |
+
})
|
| 1276 |
+
|
| 1277 |
+
genderdf <- eventReactive(input$go, ignoreNULL = FALSE, {
|
| 1278 |
+
if ("gendermc" %in% input$demog) {
|
| 1279 |
+
process_data(
|
| 1280 |
+
data = dff(),
|
| 1281 |
+
outcome_var = outcome(),
|
| 1282 |
+
recode_range = input$recode,
|
| 1283 |
+
weight_var = input$weight_type,
|
| 1284 |
+
group_var = "gendermc",
|
| 1285 |
+
var_label = "Gender"
|
| 1286 |
+
)
|
| 1287 |
+
} else {
|
| 1288 |
+
NULL
|
| 1289 |
+
}
|
| 1290 |
+
})
|
| 1291 |
+
|
| 1292 |
+
wealthdf <- eventReactive(input$go, ignoreNULL = FALSE, {
|
| 1293 |
+
if ("wealth" %in% input$demog) {
|
| 1294 |
+
process_data(
|
| 1295 |
+
data = dff(),
|
| 1296 |
+
outcome_var = outcome(),
|
| 1297 |
+
recode_range = input$recode,
|
| 1298 |
+
weight_var = input$weight_type,
|
| 1299 |
+
group_var = "wealthf",
|
| 1300 |
+
var_label = "Wealth"
|
| 1301 |
+
)
|
| 1302 |
+
} else {
|
| 1303 |
+
NULL
|
| 1304 |
+
}
|
| 1305 |
+
})
|
| 1306 |
+
|
| 1307 |
+
eddf <- eventReactive(input$go, ignoreNULL = FALSE, {
|
| 1308 |
+
if ("edre" %in% input$demog) {
|
| 1309 |
+
process_data(
|
| 1310 |
+
data = dff(),
|
| 1311 |
+
outcome_var = outcome(),
|
| 1312 |
+
recode_range = input$recode,
|
| 1313 |
+
weight_var = input$weight_type,
|
| 1314 |
+
group_var = "edrerf",
|
| 1315 |
+
var_label = "Education"
|
| 1316 |
+
)
|
| 1317 |
+
} else {
|
| 1318 |
+
NULL
|
| 1319 |
+
}
|
| 1320 |
+
})
|
| 1321 |
+
|
| 1322 |
+
agedf <- eventReactive(input$go, ignoreNULL = FALSE, {
|
| 1323 |
+
if ("age" %in% input$demog) {
|
| 1324 |
+
process_data(
|
| 1325 |
+
data = dff(),
|
| 1326 |
+
outcome_var = outcome(),
|
| 1327 |
+
recode_range = input$recode,
|
| 1328 |
+
weight_var = input$weight_type,
|
| 1329 |
+
group_var = "age",
|
| 1330 |
+
var_label = "Age"
|
| 1331 |
+
)
|
| 1332 |
+
} else {
|
| 1333 |
+
NULL
|
| 1334 |
+
}
|
| 1335 |
+
})
|
| 1336 |
+
|
| 1337 |
+
urdf <- eventReactive(input$go, ignoreNULL = FALSE, {
|
| 1338 |
+
if ("ur" %in% input$demog) {
|
| 1339 |
+
process_data(
|
| 1340 |
+
data = dff(),
|
| 1341 |
+
outcome_var = outcome(),
|
| 1342 |
+
recode_range = input$recode,
|
| 1343 |
+
weight_var = input$weight_type,
|
| 1344 |
+
group_var = "ur",
|
| 1345 |
+
var_label = "Place of\nResidence"
|
| 1346 |
+
)
|
| 1347 |
+
} else {
|
| 1348 |
+
NULL
|
| 1349 |
+
}
|
| 1350 |
+
})
|
| 1351 |
+
|
| 1352 |
+
# Combine demographic data frames into one df
|
| 1353 |
+
moverd <- reactive({
|
| 1354 |
+
req(dff(), input$recode, input$weight_type)
|
| 1355 |
+
|
| 1356 |
+
dta_mover <- Error(rbind(
|
| 1357 |
+
if (input$variable_sec != "None" && input$variable_sec != outcome()) {
|
| 1358 |
+
process_data(
|
| 1359 |
+
data = dff(),
|
| 1360 |
+
outcome_var = outcome(),
|
| 1361 |
+
recode_range = input$recode,
|
| 1362 |
+
group_var = input$variable_sec,
|
| 1363 |
+
weight_var = input$weight_type,
|
| 1364 |
+
var_label = str_wrap(variable_sec_lab(), width = 25)
|
| 1365 |
+
)
|
| 1366 |
+
},
|
| 1367 |
+
if ("gendermc" %in% input$demog) {
|
| 1368 |
+
process_data(dff(), outcome(), input$recode, "gendermc", "Gender",
|
| 1369 |
+
input$weight_type)
|
| 1370 |
+
},
|
| 1371 |
+
if ("age" %in% input$demog) {
|
| 1372 |
+
process_data(dff(), outcome(), input$recode, "age", "Age",
|
| 1373 |
+
input$weight_type)
|
| 1374 |
+
},
|
| 1375 |
+
if ("wealth" %in% input$demog) {
|
| 1376 |
+
process_data(dff(), outcome(), input$recode, "wealthf", "Wealth",
|
| 1377 |
+
input$weight_type)
|
| 1378 |
+
},
|
| 1379 |
+
if ("edre" %in% input$demog) {
|
| 1380 |
+
process_data(dff(), outcome(), input$recode, "edrerf", "Education",
|
| 1381 |
+
input$weight_type)
|
| 1382 |
+
},
|
| 1383 |
+
if ("ur" %in% input$demog) {
|
| 1384 |
+
process_data(dff(), outcome(), input$recode, "ur", "Place of\nResidence",
|
| 1385 |
+
input$weight_type)
|
| 1386 |
+
}
|
| 1387 |
+
))
|
| 1388 |
+
|
| 1389 |
+
validate(
|
| 1390 |
+
need(dta_mover, "Error: no data available. Please verify that this question was asked in this country/year combination")
|
| 1391 |
+
)
|
| 1392 |
+
|
| 1393 |
+
dta_mover$vallabel <- as.character(dta_mover$vallabel)
|
| 1394 |
+
return(dta_mover)
|
| 1395 |
+
})
|
| 1396 |
+
|
| 1397 |
+
|
| 1398 |
+
moverg <- reactive({
|
| 1399 |
+
moverg <- lapop_mover(moverd(),
|
| 1400 |
+
subtitle = "% in selected category",
|
| 1401 |
+
ymax = ifelse(any(moverd()$prop > 90, na.rm = TRUE), 119,
|
| 1402 |
+
ifelse(any(moverd()$prop > 80, na.rm = TRUE), 109, 100)),
|
| 1403 |
+
source_info = "Source: CSES Data Playground")
|
| 1404 |
+
return(moverg)
|
| 1405 |
+
})
|
| 1406 |
+
|
| 1407 |
+
output$mover <- renderPlot({
|
| 1408 |
+
return(moverg())
|
| 1409 |
+
})
|
| 1410 |
+
|
| 1411 |
+
# # -----------------------------------------------------------------------
|
| 1412 |
+
# DOWNLOAD SECTION
|
| 1413 |
+
# # -----------------------------------------------------------------------
|
| 1414 |
+
|
| 1415 |
+
# Download Plot
|
| 1416 |
+
# # -----------------------------------------------------------------------
|
| 1417 |
+
output$downloadPlot <- downloadHandler(
|
| 1418 |
+
filename = function(file) {
|
| 1419 |
+
|
| 1420 |
+
weight_suffix <- switch(input$weight_type, # Add weight type to plot export
|
| 1421 |
+
"no_weight" = "unweighted",
|
| 1422 |
+
"weight_demographic" = "demogweighted",
|
| 1423 |
+
"weight_sample" = "sampleweighted")
|
| 1424 |
+
|
| 1425 |
+
ifelse(input$tabs == "Histogram", paste0("hist_", outcome(), "_", weight_suffix, ".svg"),
|
| 1426 |
+
ifelse(input$tabs == "Time Series", paste0("ts_", outcome(), "_", weight_suffix, ".svg"),
|
| 1427 |
+
ifelse(input$tabs == "Cross Country", paste0("cc_", outcome(), "_", weight_suffix, ".svg"),
|
| 1428 |
+
ifelse(input$tabs == "World Map", paste0("map_", outcome(), "_", weight_suffix, ".svg"),
|
| 1429 |
+
paste0("mover_", outcome(), "_", weight_suffix, ".svg"))))) # Add plot type to file export
|
| 1430 |
+
},
|
| 1431 |
+
|
| 1432 |
+
content = function(file) {
|
| 1433 |
+
|
| 1434 |
+
if(input$tabs == "Histogram") {
|
| 1435 |
+
title_text <- isolate(cap())
|
| 1436 |
+
word_text <- isolate(word())
|
| 1437 |
+
|
| 1438 |
+
hist_to_save <- lapop_hist(histd(),
|
| 1439 |
+
main_title = title_text,
|
| 1440 |
+
subtitle = "% in selected category ",
|
| 1441 |
+
ymax = ifelse(any(histd()$prop > 90), 110, 100),
|
| 1442 |
+
source_info = source_info_both()
|
| 1443 |
+
)
|
| 1444 |
+
|
| 1445 |
+
lapop_save(hist_to_save, file)
|
| 1446 |
+
showNotification(HTML("Histogram plot download complete ✓ "), type = "message")
|
| 1447 |
+
|
| 1448 |
+
} else if (input$tabs == "Time Series") {
|
| 1449 |
+
title_text <- isolate(cap())
|
| 1450 |
+
subtitle_text <- slider_values()
|
| 1451 |
+
|
| 1452 |
+
ts_to_save <- lapop_ts(tsd(),
|
| 1453 |
+
main_title = title_text,
|
| 1454 |
+
subtitle = paste0("% in selected category ", subtitle_text),
|
| 1455 |
+
ymax = ifelse(any(tsd()$prop > 88, na.rm = TRUE), 110, 100),
|
| 1456 |
+
label_vjust = ifelse(any(tsd()$prop > 80, na.rm = TRUE), -1.1, -1.5),
|
| 1457 |
+
source_info = source_info_pais()
|
| 1458 |
+
)
|
| 1459 |
+
|
| 1460 |
+
lapop_save(ts_to_save, file)
|
| 1461 |
+
showNotification(HTML("Time series plot download complete ✓ "), type = "message")
|
| 1462 |
+
|
| 1463 |
+
} else if (input$tabs == "Cross Country") {
|
| 1464 |
+
title_text <- isolate(cap())
|
| 1465 |
+
subtitle_text <- slider_values()
|
| 1466 |
+
|
| 1467 |
+
cc_to_save <- lapop_cc(ccd(), sort = "hi-lo",
|
| 1468 |
+
main_title = title_text,
|
| 1469 |
+
subtitle = paste0("% in selected category ", subtitle_text),
|
| 1470 |
+
ymax = ifelse(any(ccd()$prop > 90, na.rm = TRUE), 110, 100),
|
| 1471 |
+
label_angle = 90,
|
| 1472 |
+
source_info = source_info_wave()
|
| 1473 |
+
)
|
| 1474 |
+
|
| 1475 |
+
lapop_save(cc_to_save, file)
|
| 1476 |
+
showNotification(HTML("Cross country plot download complete ✓ "), type = "message")
|
| 1477 |
+
|
| 1478 |
+
} else if (input$tabs == "World Map") {
|
| 1479 |
+
title_text <- isolate(cap())
|
| 1480 |
+
subtitle_text <- slider_values()
|
| 1481 |
+
|
| 1482 |
+
map_to_save <- lapop_map(mapd(),
|
| 1483 |
+
main_title = title_text,
|
| 1484 |
+
subtitle = paste0("% in selected category ", subtitle_text),
|
| 1485 |
+
source_info = paste0("\n", source_info_both()),
|
| 1486 |
+
survey = "CSES"
|
| 1487 |
+
)
|
| 1488 |
+
|
| 1489 |
+
lapop_save(map_to_save, file)
|
| 1490 |
+
showNotification(HTML("Map plot download complete ✓ "), type = "message")
|
| 1491 |
+
|
| 1492 |
+
} else {
|
| 1493 |
+
title_text <- isolate(cap())
|
| 1494 |
+
subtitle_text <- slider_values()
|
| 1495 |
+
word_text <- isolate(word())
|
| 1496 |
+
|
| 1497 |
+
mover_to_save <- lapop_mover(
|
| 1498 |
+
moverd(),
|
| 1499 |
+
main_title = title_text,
|
| 1500 |
+
subtitle = paste0("% in selected category ", subtitle_text),
|
| 1501 |
+
ymax = ifelse(any(moverd()$prop > 90, na.rm = TRUE), 119,
|
| 1502 |
+
ifelse(any(moverd()$prop > 80, na.rm = TRUE), 109, 100)),
|
| 1503 |
+
source_info = source_info_both()
|
| 1504 |
+
)
|
| 1505 |
+
|
| 1506 |
+
lapop_save(mover_to_save, file)
|
| 1507 |
+
showNotification(HTML("Break down plot download complete ✓ "), type = "message")
|
| 1508 |
+
|
| 1509 |
+
}
|
| 1510 |
+
}
|
| 1511 |
+
)
|
| 1512 |
+
|
| 1513 |
+
# DOWNLOAD TABLE
|
| 1514 |
+
# -----------------------------------------------------------------------
|
| 1515 |
+
output$downloadTable <- downloadHandler(
|
| 1516 |
+
filename = function(file) {
|
| 1517 |
+
|
| 1518 |
+
weight_suffix <- switch(input$weight_type, # Add weight type to file export
|
| 1519 |
+
"no_weight" = "unweighted",
|
| 1520 |
+
"weight_demographic" = "demogweighted",
|
| 1521 |
+
"weight_sample" = "sampleweighted")
|
| 1522 |
+
|
| 1523 |
+
ifelse(input$tabs == "Histogram", paste0("hist_", outcome(), "_", weight_suffix, ".csv"),
|
| 1524 |
+
ifelse(input$tabs == "Time Series", paste0("ts_", outcome(), "_", weight_suffix,".csv"),
|
| 1525 |
+
ifelse(input$tabs == "Cross Country", paste0("cc_", outcome(), "_", weight_suffix, ".csv"),
|
| 1526 |
+
ifelse(input$tabs == "World Map", paste0("map_", outcome(), "_", weight_suffix, ".csv"),
|
| 1527 |
+
paste0("mover_", outcome(), "_", weight_suffix, ".csv")))))
|
| 1528 |
+
},
|
| 1529 |
+
content = function(file) {
|
| 1530 |
+
if(input$tabs == "Histogram") {
|
| 1531 |
+
write.csv(histd(), file, row.names=F)
|
| 1532 |
+
showNotification(HTML("Histogram file download complete ✓ "),
|
| 1533 |
+
type = "message")
|
| 1534 |
+
|
| 1535 |
+
} else if (input$tabs == "Time Series") {
|
| 1536 |
+
write.csv(tsd(), file, row.names=F)
|
| 1537 |
+
showNotification(HTML("Time series file download complete ✓ "),
|
| 1538 |
+
type = "message")
|
| 1539 |
+
|
| 1540 |
+
} else if (input$tabs == "Cross Country") {
|
| 1541 |
+
write.csv(ccd(), file, row.names=F)
|
| 1542 |
+
showNotification(HTML("Cross country file download complete ✓ "),
|
| 1543 |
+
type = "message")
|
| 1544 |
+
|
| 1545 |
+
} else if (input$tabs == "World Map") {
|
| 1546 |
+
write.csv(mapd(), file, row.names=F)
|
| 1547 |
+
showNotification(HTML("Map file download complete ✓ "),
|
| 1548 |
+
type = "message")
|
| 1549 |
+
|
| 1550 |
+
} else {
|
| 1551 |
+
write.csv(moverd(), file, row.names=F)
|
| 1552 |
+
showNotification(HTML("Break down file download complete ✓ "),
|
| 1553 |
+
type = "message")
|
| 1554 |
+
|
| 1555 |
+
}
|
| 1556 |
+
}
|
| 1557 |
+
)
|
| 1558 |
+
}
|
| 1559 |
+
|
| 1560 |
+
# RUN APP
|
| 1561 |
+
# # -----------------------------------------------------------------------
|
| 1562 |
+
shinyApp(ui, server)
|
| 1563 |
+
|
| 1564 |
+
# # -----------------------------------------------------------------------
|
| 1565 |
+
# END
|
| 1566 |
+
# # -----------------------------------------------------------------------
|
cses-shiny.Rproj
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Version: 1.0
|
| 2 |
+
ProjectId: 77d77540-4c5c-492a-b2c3-65b0f1499688
|
| 3 |
+
|
| 4 |
+
RestoreWorkspace: Default
|
| 5 |
+
SaveWorkspace: Default
|
| 6 |
+
AlwaysSaveHistory: Default
|
| 7 |
+
|
| 8 |
+
EnableCodeIndexing: Yes
|
| 9 |
+
UseSpacesForTab: Yes
|
| 10 |
+
NumSpacesForTab: 2
|
| 11 |
+
Encoding: UTF-8
|
| 12 |
+
|
| 13 |
+
RnwWeave: Sweave
|
| 14 |
+
LaTeX: pdfLaTeX
|
| 15 |
+
|
| 16 |
+
AutoAppendNewline: Yes
|
| 17 |
+
StripTrailingWhitespace: Yes
|
cses_labs.rds
ADDED
|
Binary file (864 Bytes). View file
|
|
|
cses_labs_sec.rds
ADDED
|
Binary file (1.44 kB). View file
|
|
|
cses_shiny_data.rda
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:e3cad18ac3c75feeb3b170223f2ba6e11bfa690c08bb3bfa53e1ed9379381692
|
| 3 |
+
size 3058556
|
cses_shiny_data.rds
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:698677f42d76fca9de837613d8b303e192033d81f1db33fd1f30f011244ed906
|
| 3 |
+
size 4950894
|
cses_variable_labels.csv
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"column_name","question_short_en","category_short_en","display_en","question_en","responses_en","responses_en_rec"
|
| 2 |
+
"IMD2001_2","Age of respondent (in categories)","Demographic Data","Demographic Data: AGE OF RESPONDENT (IN CATEGORIES) (IMD2001_2)","Age of respondent (in categories listed below).","(1) YOUNGEST - 24 YEARS (2) 25-34 YEARS (3) 35-44 YEARS (4) 45-54 YEARS (5) 55-64 YEARS (6) 65 - OLDEST","(1) Youngest - 24 years (2) 25-34 years (3) 35-44 years (4) 45-54 years (5) 55-64 years (6) 65 - oldest"
|
| 3 |
+
"IMD2002","Gender","Demographic Data","Demographic Data: GENDER (IMD2002)","Gender of Respondent.","(1) MALE (2) FEMALE (3) OTHER","(1) Male (2) Female (3) Other"
|
| 4 |
+
"IMD2003","Education","Demographic Data","Demographic Data: EDUCATION (IMD2003)","Education of Respondent.","(0) NONE (NO EDUCATION) / ILLITERATE (1) PRIMARY EDUCATION/LOWER SECONDARY EDUCATION (2) HIGHER SECONDARY EDUCATION (3) POST-SECONDARY (NON-UNIVERSITY) EDUCATION (4) UNIVERSITY EDUCATION","(0) None (no education) / illiterate (1) Primary education/lower secondary education (2) Higher secondary education (3) Post-secondary (non-university) Education (4) University education"
|
| 5 |
+
"IMD2004","Marital status","Demographic Data","Demographic Data: MARITAL STATUS (IMD2004)","Respondent's marital or civil union status.","(1) MARRIED OR LIVING TOGETHER AS MARRIED (2) WIDOWED (3) DIVORCED OR SEPARATED (MARRIED BUT SEPARATED/NOT LIVING WITH LEGAL SPOUSE) (4) SINGLE, NEVER MARRIED (5) [SEE ELECTION STUDY NOTES]","(1) Married or living together as married (2) Widowed (3) Divorced or separated (married but separated/not living with legal spouse) (4) Single, never married (5) [see election study notes]"
|
| 6 |
+
"IMD2005_1","Religious services attendance","Demographic Data","Demographic Data: RELIGIOUS SERVICES ATTENDANCE (IMD2005_1)","Religious denomination.","(1) NEVER (2) ONCE A YEAR (3) TWO TO ELEVEN TIMES A YEAR (4) ONCE A MONTH (5) TWO OR MORE TIMES A MONTH (6) ONCE A WEEK/MORE THAN ONCE A WEEK","(1) Never (2) Once a year (3) Two to eleven times a year (4) Once a month (5) Two or more times a month (6) Once a week/more than once a week"
|
| 7 |
+
"IMD2005_2","Religiosity","Demographic Data","Demographic Data: RELIGIOSITY (IMD2005_2)","Religiosity.","(1) HAVE NO RELIGIOUS BELIEFS (2) NOT VERY RELIGIOUS (3) SOMEWHAT RELIGIOUS (4) VERY RELIGIOUS","(1) Have no religious beliefs (2) Not very religious (3) Somewhat religious (4) Very religious"
|
| 8 |
+
"IMD2006","Household income","Demographic Data","Demographic Data: HOUSEHOLD INCOME (IMD2006)","Household income quintile appropriate to the respondent.","(1) LOWEST HOUSEHOLD INCOME QUINTILE (2) SECOND HOUSEHOLD INCOME QUINTILE (3) THIRD HOUSEHOLD INCOME QUINTILE (4) FOURTH HOUSEHOLD INCOME QUINTILE (5) HIGHEST HOUSEHOLD INCOME QUINTILE","(1) Lowest household income quintile (2) Second household income quintile (3) Third household income quintile (4) Fourth household income quintile (5) Highest household income quintile"
|
| 9 |
+
"IMD2007","Rural or urban residence","Demographic Data","Demographic Data: RURAL OR URBAN RESIDENCE (IMD2007)","Rural/Urban Residence.","(1) RURAL AREA OR VILLAGE (2) SMALL OR MIDDLE-SIZED TOWN (3) SUBURBS OF LARGE TOWN OR CITY (4) LARGE TOWN OR CITY","(1) Rural area or village (2) Small or middle-sized town (3) Suburbs of large town or city (4) Large town or city"
|
| 10 |
+
"IMD2014","Current employment status","Demographic Data","Demographic Data: CURRENT EMPLOYMENT STATUS (IMD2014)","Current employment status of respondent.","(0) EMPLOYED : NO HOURS SPECIFIED (1) EMPLOYED : FULL-TIME (32 OR MORE HOURS WEEKLY) (2) EMPLOYED : PART-TIME (15-32 HOURS WEEKLY) (3) EMPLOYED : LESS THAN 15 HOURS (4) HELPING FAMILY MEMBER (5) UNEMPLOYED (6) STUDENT, IN SCHOOL, IN VOCATIONAL TRAINING (7) RETIRED (8) HOMEMAKER, HOME DUTIES (9) PERMANENTLY DISABLED (10) OTHERS, NOT IN LABOR FORCE (11) ON TEMPORARY JOB LEAVE (MATERNITY LEAVE, SICK LEAVE, ETC.) (12) CIVIL / MILITARY SERVICE","(0) Employed : no hours specified (1) Employed : full-time (32 or more hours weekly) (2) Employed : part-time (15-32 hours weekly) (3) Employed : less than 15 hours (4) Helping family member (5) Unemployed (6) Student, in school, in vocational training (7) Retired (8) Homemaker, home duties (9) Permanently disabled (10) Others, not in labor force (11) On temporary job leave (maternity leave, sick leave, etc.) (12) Civil / military service"
|
| 11 |
+
"IMD2016","Socio economic status","Demographic Data","Demographic Data: SOCIO ECONOMIC STATUS (IMD2016)","Respondent's socio-economic status.","(1) WHITE COLLAR (2) WORKER (3) FARMER (4) SELF-EMPLOYED (5) OTHER","(1) White collar (2) Worker (3) Farmer (4) Self-employed (5) Other"
|
| 12 |
+
"IMD2019_1","Union membership of respondent","Demographic Data","Demographic Data: UNION MEMBERSHIP OF RESPONDENT (IMD2019_1)","Union membership of respondent.","(0) R IS NOT A MEMBER OF A UNION (1) R IS MEMBER OF A UNION","(0) R is not a member of a union (1) R is member of a union"
|
| 13 |
+
"IMD3001","Turnout: main election","Survey Data","Survey Data: TURNOUT: MAIN ELECTION (IMD3001)","Whether or not the respondent cast a ballot in the main election.","(0) RESPONDENT DID NOT CAST A BALLOT (1) RESPONDENT CAST A BALLOT","(0) Respondent did not cast a ballot (1) Respondent cast a ballot"
|
| 14 |
+
"IMD3001_PR_1","Turnout: current presidential election - round 1","Survey Data","Survey Data: TURNOUT: CURRENT PRESIDENTIAL ELECTION - ROUND 1 (IMD3001_PR_1)","Whether or not the respondent cast a ballot in the first/second round of the Presidential elections.","(0) RESPONDENT DID NOT CAST A BALLOT (1) RESPONDENT CAST A BALLOT","(0) Respondent did not cast a ballot (1) Respondent cast a ballot"
|
| 15 |
+
"IMD3001_PR_2","Turnout: current presidential election - round 2","Survey Data","Survey Data: TURNOUT: CURRENT PRESIDENTIAL ELECTION - ROUND 2 (IMD3001_PR_2)","Whether or not the respondent cast a ballot in the first/second round of the Presidential elections.","(0) RESPONDENT DID NOT CAST A BALLOT (1) RESPONDENT CAST A BALLOT","(0) Respondent did not cast a ballot (1) Respondent cast a ballot"
|
| 16 |
+
"IMD3001_LH","Turnout: current lower house election","Survey Data","Survey Data: TURNOUT: CURRENT LOWER HOUSE ELECTION (IMD3001_LH)","Whether or not the respondent cast a ballot in the lower house election.","(0) RESPONDENT DID NOT CAST A BALLOT (1) RESPONDENT CAST A BALLOT","(0) Respondent did not cast a ballot (1) Respondent cast a ballot"
|
| 17 |
+
"IMD3001_UH","Turnout: current upper house election","Survey Data","Survey Data: TURNOUT: CURRENT UPPER HOUSE ELECTION (IMD3001_UH)","Whether or not the respondent cast a ballot in the upper house election.","(0) RESPONDENT DID NOT CAST A BALLOT (1) RESPONDENT CAST A BALLOT","(0) Respondent did not cast a ballot (1) Respondent cast a ballot"
|
| 18 |
+
"IMD3001_TS","Turnout: turnout switcher between current","Survey Data","Survey Data: TURNOUT: TURNOUT SWITCHER BETWEEN CURRENT (IMD3001_TS)","Whether or not the respondent reports voting in the current and previous election.","(0) RESPONDENT ABSTAINED IN BOTH ELECTIONS (1) RESPONDENT ABSTAINED IN CURRENT ELECTION BUT VOTED IN PREVIOUS ELECTION (2) RESPONDENT VOTED IN CURRENT ELECTION BUT ABSTAINED IN PREVIOUS ELECTION (3) RESPONDENT VOTED IN BOTH CURRENT AND PREVIOUS ELECTION (5) RESPONDENT ABSTAINED IN CURRENT ELECTION BUT INELIGIBLE TO VOTE IN PREVIOUS ELECTION (6) RESPONDENT VOTED IN CURRENT ELECTION BUT INELIGIBLE TO VOTE IN PREVIOUS ELECTION","(0) Respondent abstained in both elections (1) Respondent abstained in current election but voted in previous election (2) Respondent voted in current election but abstained in previous election (3) Respondent voted in both current and previous election (5) Respondent abstained in current election but ineligible to vote in previous election (6) Respondent voted in current election but ineligible to vote in previous election"
|
| 19 |
+
"IMD3002_OUTGOV","Vote choice: current main election - vote for outgoing government (incumbent)","Survey Data","Survey Data: VOTE CHOICE: CURRENT MAIN ELECTION - VOTE FOR OUTGOING GOVERNMENT (INCUMBENT) (IMD3002_OUTGOV)","Whether or not the respondent cast a ballot for the outgoing incumbent.","(0) DID NOT VOTE FOR THE OUTGOING GOVERNMENT (INCUMBENT) (1) VOTED FOR THE OUTGOING GOVERNMENT (INCUMBENT)","(0) Did not vote for the outgoing government (incumbent) (1) Voted for the outgoing government (incumbent)"
|
| 20 |
+
"IMD3002_VS_1","Vote choice: vote switcher between current election and previous election","Survey Data","Survey Data: VOTE CHOICE: VOTE SWITCHER BETWEEN CURRENT ELECTION AND PREVIOUS ELECTION (IMD3002_VS_1)"," Whether or not the respondent reports voting for the same party/coalition in the current and previous main election.","(0) DID NOT SWITCH (VOTED FOR SAME PARTY/COALITION IN CURRENT & PREVIOUS ELECTION) (1) SWITCHER (CHANGED VOTE IN CURRENT ELECTION FROM PREVIOUS ELECTION)","(0) Did not switch (voted for same party/coalition in current & previous election) (1) Switcher (changed vote in current election from previous election)"
|
| 21 |
+
"IMD3002_LR_CSES","Vote choice: current main election - vote for leftist/center/rightist - cses","Survey Data","Survey Data: VOTE CHOICE: CURRENT MAIN ELECTION - VOTE FOR LEFTIST/CENTER/RIGHTIST - CSES (IMD3002_LR_CSES)","Whether or not the respondent reports voting for a leftist/center/rightist party/candidate of the party, based on CSES Collaborators experts' judgment of parties' ideology.","(1) VOTED FOR LEFTIST PARTY/CANDIDATE (2) VOTED FOR CENTER PARTY/CANDIDATE (3) VOTED FOR RIGHTIST PARTY/CANDIDATE","(1) Voted for leftist party/candidate (2) Voted for center party/candidate (3) Voted for rightist party/candidate"
|
| 22 |
+
"IMD3005_1","Party identification: are you close to any political party","Survey Data","Survey Data: PARTY IDENTIFICATION: ARE YOU CLOSE TO ANY POLITICAL PARTY (IMD3005_1)","Do you usually think of yourself as close to any particular party?","(0) NO (1) YES","(0) No (1) Yes"
|
| 23 |
+
"IMD3006","Ideology: left-right - self","Survey Data","Survey Data: IDEOLOGY: LEFT-RIGHT - SELF (IMD3006)","Respondents' self-placement on a 0-10 left-right scale.","(0) LEFT (10) RIGHT","(0) Left (10) Right"
|
| 24 |
+
"IMD3010","Satisfaction with democracy","Survey Data","Survey Data: SATISFACTION WITH DEMOCRACY (IMD3010)","On the whole, are you very satisfied, fairly satisfied, not very satisfied, or not at all satisfied with the way democracy works in [COUNTRY]?","(1) VERY SATISFIED (2) FAIRLY SATISFIED (3) NEITHER SATISFIED NOR DISSATISFIED (4) NOT VERY SATISFIED (0) NOT AT ALL SATISFIED","(1) Very satisfied (2) Fairly satisfied (3) Neither satisfied nor dissatisfied (4) Not very satisfied (0) Not at all satisfied"
|
| 25 |
+
"IMD3011","Efficacy: who is in power can make a difference","Survey Data","Survey Data: EFFICACY: WHO IS IN POWER CAN MAKE A DIFFERENCE (IMD3011)","Some people say that it doesn't make any difference who is in power. Others say that it makes a big difference who is in power. Using the scale on this card, (where ONE means that it doesn't make any difference who is in power and FIVE means that it makes a big difference who is in power), where would you place yourself?","(1) IT DOESN'T MAKE ANY DIFFERENCE WHO IS IN POWER (5) IT MAKES A BIG DIFFERENCE WHO IS IN POWER","(1) It doesn't make any difference who is in power (5) It makes a big difference who is in power"
|
| 26 |
+
"IMD3012","Efficacy: who people vote for makes a difference","Survey Data","Survey Data: EFFICACY: WHO PEOPLE VOTE FOR MAKES A DIFFERENCE (IMD3012)","Some people say that no matter who people vote for, it won't make any difference to what happens. Others say that who people vote for can make a big difference to what happens. Using the scale on this card, (where ONE means that voting won't make any difference to what happens and FIVE means that voting can make a big difference), where would you place yourself?","(1) WHO PEOPLE VOTE FOR WON'T MAKE ANY DIFFERENCE (5) WHO PEOPLE VOTE FOR CAN MAKE A BIG DIFFERENCE","(1) Who people vote for won't make any difference (5) Who people vote for can make a big difference"
|
| 27 |
+
"IMD3013_1","State of economy (over past 12 months)","Survey Data","Survey Data: STATE OF ECONOMY (OVER PAST 12 MONTHS) (IMD3013_1)","Would you say that over the past twelve months, the state of the economy in [COUNTRY] has gotten better, stayed about the same, or gotten worse?","(1) GOTTEN BETTER (3) STAYED THE SAME (5) GOTTEN WORSE","(1) Gotten better (3) Stayed the same (5) Gotten worse"
|
| 28 |
+
"IMD3013_2","State of economy - better","Survey Data","Survey Data: STATE OF ECONOMY - BETTER (IMD3013_2)","Would you say much better or somewhat better?","(1) MUCH BETTER (2) SOMEWHAT BETTER","(1) Much better (2) Somewhat better"
|
| 29 |
+
"IMD3013_3","State of economy - worse","Survey Data","Survey Data: STATE OF ECONOMY - WORSE (IMD3013_3)","Would you say much worse or somewhat worse?","(4) SOMEWHAT WORSE (5) MUCH WORSE","(4) Somewhat worse (5) Much worse"
|
| 30 |
+
"IMD3014","Government performance: general","Survey Data","Survey Data: GOVERNMENT PERFORMANCE: GENERAL (IMD3014)","Now thinking about the performance of the [government in [CAPITAL]/President] in general, how good or bad a job do you think the [government/President in [CAPITAL]] has done over the past [NUMBER OF YEARS SINCE LAST GOVERNMENT TOOK OFFICE, BEFORE THE CURRENT ELECTION] years? Has [it/he/she] done a very good job? A good job? A bad job? A very bad job?","(1) VERY GOOD JOB (2) GOOD JOB (3) BAD JOB (4) VERY BAD JOB","(1) Very good job (2) Good job (3) Bad job (4) Very bad job"
|
| 31 |
+
"IMD5006_1","Electoral turnout - turnout as a percentage of registered voters (er)","Macro Data","Macro Data: ELECTORAL TURNOUT - TURNOUT AS A PERCENTAGE OF REGISTERED VOTERS (ER) (IMD5006_1)","Official voter turnout - Percentage of the registered voters (ER).","(0 to 100). PERCENT OF REGISTERED VOTERS (ER) WHO VOTED","(0 to 100). Percent of registered voters (er) Who voted"
|
| 32 |
+
"IMD5006_2","Electoral turnout - turnout as a percentage of the voting age population (vap)","Macro Data","Macro Data: ELECTORAL TURNOUT - TURNOUT AS A PERCENTAGE OF THE VOTING AGE POPULATION (VAP) (IMD5006_2)","Official voter turnout - Percentage of Voting Age Population (VAP).","(0 to 100). PERCENT OF VOTING AGE POPULATION (VAP) WHO VOTED","(0 to 100). Percent of voting age population (vap) Who voted"
|
| 33 |
+
"IMD5007","Compulsory voting","Macro Data","Macro Data: COMPULSORY VOTING (IMD5007)","Is voting compulsory?","(1) YES; STRICTLY ENFORCED SANCTIONS (2) YES; WEAKLY ENFORCED SANCTIONS (3) YES; WITHOUT SANCTION FOR VIOLATION (0) NO","(1) Yes; strictly enforced sanctions (2) Yes; weakly enforced sanctions (3) Yes; without sanction for violation (0) No"
|
| 34 |
+
"IMD5013","Electoral formula in all segments: lower house","Macro Data","Macro Data: ELECTORAL FORMULA IN ALL SEGMENTS: LOWER HOUSE (IMD5013)","Whether the country uses a majoritarian formula, a proportional formula, or a mixed formula in all of its electoral segments/tiers.","(1) MAJORITARIAN (2) PROPORTIONAL (3) MIXED","(1) Majoritarian (2) Proportional (3) Mixed"
|
| 35 |
+
"IMD5014","Electoral formula: presidential election","Macro Data","Macro Data: ELECTORAL FORMULA: PRESIDENTIAL ELECTION (IMD5014)","The electoral formula used to elect the President that is elected by popular vote.","(1) PLURALITY (2) ABSOLUTE MAJORITY RULE (3) QUALIFIED MAJORITY RULE (4) ELECTORAL COLLEGE (5) ALTERNATIVE VOTE","(1) Plurality (2) Absolute majority rule (3) Qualified majority rule (4) Electoral college (5) Alternative vote"
|
| 36 |
+
"IMD5032_4","Post-election protest","Macro Data","Macro Data: POST-ELECTION PROTEST (IMD5032_4)","To what extent was there protest following the election?","(1) NO PROTEST AT ALL (2) SPORADIC PROTEST (3) SIGNIFICANT PROTEST","(1) No protest at all (2) Sporadic protest (3) Significant protest"
|
| 37 |
+
"IMD5033","Fairness of the election","Macro Data","Macro Data: FAIRNESS OF THE ELECTION (IMD5033)","How impartial was the body that administered the election law?","(1) VERY IMPARTIAL (2) MOSTLY IMPARTIAL (3) NOT VERY IMPARTIAL (4) NOT IMPARTIAL AT ALL","(1) Very impartial (2) Mostly impartial (3) Not very impartial (4) Not impartial at all"
|
| 38 |
+
"IMD5034_2","Election irregularities reported","Macro Data","Macro Data: ELECTION IRREGULARITIES REPORTED (IMD5034_2)","Were there irregularities reported by international election observers?","(0) NO (1) YES","(0) No (1) Yes"
|
| 39 |
+
"IMD5035","Number of parties participating in election","Macro Data","Macro Data: NUMBER OF PARTIES PARTICIPATING IN ELECTION (IMD5035)","How many political parties received votes in the election?","(1 to 900). NUMBER OF PARTIES","(1 to 900). Number of parties"
|
| 40 |
+
"IMD5036_3","Did any electoral alliance form?","Macro Data","Macro Data: DID ANY ELECTORAL ALLIANCE FORM? (IMD5036_3)","(If yes to IMD5036_1 or IMD5036_2) Did any electoral alliances form?","(0) NO (1) YES","(0) No (1) Yes"
|
| 41 |
+
"IMD5045_1","Average district magnitude - lowest segment (tier) - lower house","Macro Data","Macro Data: AVERAGE DISTRICT MAGNITUDE - LOWEST SEGMENT (TIER) - LOWER HOUSE (IMD5045_1)","Average district magnitude in the first or lowest electoral segment (tier).","(1 to 900). NUMBER OF SEATS ELECTED PER DISTRICT","(1 to 900). Number of seats elected per district"
|
| 42 |
+
"IMD5048","Regime: type of executive","Macro Data","Macro Data: REGIME: TYPE OF EXECUTIVE (IMD5048)","Classification of political regimes. ","(1) PARLIAMENTARY REGIME (2) MIXED REGIME (3) PRESIDENTIAL REGIME","(1) Parliamentary regime (2) Mixed regime (3) Presidential regime"
|
| 43 |
+
"IMD5049","Age of current regime","Macro Data","Macro Data: AGE OF CURRENT REGIME (IMD5049)","The number of years since the most recent regime change.","(0 to 500). AGE OF THE REGIME (YEARS)","(0 to 500). Age of the regime (years)"
|
| 44 |
+
"IMD5050_1","Freedom house rating - time t","Macro Data","Macro Data: FREEDOM HOUSE RATING - TIME T (IMD5050_1)","Freedom House's rating at three time periods (average of the ""Political Rights"" and ""Civil Liberties"" scores).","(1 to 7). FREEDOM HOUSE RATING SCORE","(1 to 7). Freedom house rating score"
|
| 45 |
+
"IMD5051_1","Democracy-autocracy - polity iv rating - time t","Macro Data","Macro Data: DEMOCRACY-AUTOCRACY - POLITY IV RATING - TIME T (IMD5051_1)","The POLITY IV ratings of institutionalized democracy versus autocracy in a country at three time periods.","(-10) AUTOCRATIC (10) DEMOCRATIC","(-10) Autocratic (10) Democratic"
|
| 46 |
+
"IMD5052_2","Gdp growth annual % (world bank) - time t-1 year","Macro Data","Macro Data: GDP GROWTH ANNUAL % (WORLD BANK) - TIME T-1 YEAR (IMD5052_2)","World Bank estimate of the annual GDP growth at three time periods.","(-20 to +25). PERCENT ANNUAL GROWTH","(-20 to +25). Percent annual growth"
|
| 47 |
+
"IMD5053_1","Gdp per capita, ppp (world bank) - time t","Macro Data","Macro Data: GDP PER CAPITA, PPP (WORLD BANK) - TIME T (IMD5053_1)","World Bank estimate of the GDP per capita at three time periods.","(0 to 899,999). GDP PER CAPITA","(0 to 899,999). Gdp per capita"
|
| 48 |
+
"IMD5054_2","Unemployment, total (world bank) - time t-1 year","Macro Data","Macro Data: UNEMPLOYMENT, TOTAL (WORLD BANK) - TIME T-1 YEAR (IMD5054_2)","World Bank estimates of the unemployment rate (% of total labor force) at three time periods.","(0 to 100). UNEMPLOYMENT RATE (% OF TOTAL LABOR FORCE)","(0 to 100). Unemployment rate (% of total labor force)"
|
| 49 |
+
"IMD5055_1","Human development index (unpd) - time t","Macro Data","Macro Data: HUMAN DEVELOPMENT INDEX (UNPD) - TIME T (IMD5055_1)","UNDP Human Development Index (HDI) at three time periods.","(0 to 1). HUMAN DEVELOPMENT INDEX","(0 to 1). Human development index"
|
| 50 |
+
"IMD5056_2","Inflation, gdp deflator (annual %) (world bank) - time t-1 year","Macro Data","Macro Data: INFLATION, GDP DEFLATOR (ANNUAL %) (WORLD BANK) - TIME T-1 YEAR (IMD5056_2)","World Bank estimate of Inflation at three time periods.","(-100 to 10000). INFLATION (ANNUAL %)","(-100 to 10000). Inflation (annual %)"
|
| 51 |
+
"IMD5057_1","Population, total (world bank) - time t","Macro Data","Macro Data: POPULATION, TOTAL (WORLD BANK) - TIME T (IMD5057_1)","World Bank estimates of the total population size, at three time periods.","(1000 to 9,999,999,999). POPULATION SIZE","(1000 to 9,999,999,999). Population size"
|
| 52 |
+
"IMD5058_1","Effective number of electoral parties","Macro Data","Macro Data: EFFECTIVE NUMBER OF ELECTORAL PARTIES (IMD5058_1)","Effective Number of Electoral Parties (ENEP).","(0 to 1500). EFFECTIVE NUMBER OF ELECTORAL PARTIES","(0 to 1500). Effective number of electoral parties"
|
world.rda
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:16b3c603996b162bed809ca5a638e7e8baca0bdabe08b9dea4f52b21557d5742
|
| 3 |
+
size 504800
|