Appunti esplorativi sull’export SoSciSurvey del 2026-04-22. Compila con File -> Compile Report (Ctrl+Shift+K). Il codice e’ nascosto di default: tasto Code in alto per vederlo.
Tre file in input:
data_*.xlsx - dati grezzicodebook_*.xlsx - etichette di valorevariables_*.csv - descrizione variabili (UTF-16)La prima riga del foglio dati ripete le etichette: si scarta.
f_data <- "data_Equityesplorativo_2026-04-22_19-25.xlsx"
f_codebook <- "codebook_Equityesplorativo_2026-04-22_19-25.xlsx"
f_vars <- "variables_Equityesplorativo_2026-04-22_19-25.csv"
raw <- read_excel(f_data, sheet = 1, col_types = "text")
dati <- raw[-1, ]
codebook <- read_excel(f_codebook, sheet = 1)
variables <- read_tsv(f_vars, locale = locale(encoding = "UTF-16LE"),
show_col_types = FALSE)
tibble::tibble(n_record = nrow(dati), n_variabili = ncol(dati)) |> kable()| n_record | n_variabili |
|---|---|
| 137 | 53 |
as.numeric-9 (“Not answered” di SoSciSurvey) ->
NANA, non crashanum_vars <- c("CASE","FA02","FI01","NE01","OR02","SD01",
"SD02_01","SD02_02","SD02_03","SD02_04","SD02_05","SD02_06",
"PR02_01","SD03_01","SD04","SD05","SD06","SD07","SD08",
paste0("TIME00", 1:7), "TIME_SUM",
"LASTPAGE","MAXPAGE","MISSING","MISSREL","TIME_RSI",
"FINISHED","Q_VIEWER")
num_vars <- intersect(num_vars, names(dati))
dati[num_vars] <- lapply(dati[num_vars], function(x) suppressWarnings(as.numeric(x)))
parse_dt <- function(x) {
x <- trimws(as.character(x)); x[x %in% c("", "NA")] <- NA_character_
out <- suppressWarnings(as.POSIXct(x, format = "%Y-%m-%d %H:%M:%S", tz = "Europe/Rome"))
miss <- is.na(out) & !is.na(x)
if (any(miss)) out[miss] <- suppressWarnings(as.POSIXct(x[miss], format = "%Y-%m-%d %H:%M", tz = "Europe/Rome"))
out
}
if ("STARTED" %in% names(dati)) dati$STARTED <- parse_dt(dati$STARTED)
if ("LASTDATA" %in% names(dati)) dati$LASTDATA <- parse_dt(dati$LASTDATA)
dati <- dati |> mutate(across(all_of(num_vars), ~ ifelse(.x == -9, NA_real_, .x)))Ogni variabile categoriale prende una copia *_lab con le
etichette testuali. Codici non numerici vengono saltati.
label_map <- codebook |>
filter(!is.na(`Response Code`)) |>
transmute(var = Variable,
code = suppressWarnings(as.numeric(`Response Code`)),
label = `Response Label`) |>
filter(!is.na(code), !is.na(label))
apply_labels <- function(x, var_name) {
m <- label_map |> filter(var == var_name)
m <- m[!duplicated(m$code), ]
if (nrow(m) == 0) return(x)
factor(x, levels = m$code, labels = m$label)
}
for (v in intersect(unique(label_map$var), names(dati))) {
dati[[paste0(v, "_lab")]] <- apply_labels(dati[[v]], v)
}Tengo solo chi ha FINISHED == 1 e ha dato consenso
(PR02_01 == 2). Questo e’ il dataset usato d’ora in
poi.
dati_ok <- dati |>
filter(is.na(FINISHED) | FINISHED == 1,
is.na(PR02_01) | PR02_01 == 2)
tibble::tibble(
record_totali = nrow(dati),
finiti_con_consenso = nrow(dati_ok),
pct = scales::percent(nrow(dati_ok) / nrow(dati), accuracy = 0.1)
) |> kable()| record_totali | finiti_con_consenso | pct |
|---|---|---|
| 137 | 70 | 51.1% |
Chi ha risposto. Numeri bassi -> percentuali da prendere con le pinze.
Anni compiuti dichiarati (SD03_01).
## Min. 1st Qu. Median Mean 3rd Qu. Max. NA's
## 23.00 35.00 42.00 41.67 48.00 63.00 1
ggplot(dati_ok, aes(SD03_01)) +
geom_histogram(binwidth = 5, fill = "#4C78A8", color = "white") +
labs(title = "Distribuzione dell'eta'", x = "Eta' (anni)", y = "Frequenza")SD04, dichiarato.
if ("SD04_lab" %in% names(dati_ok)) {
print(
dati_ok |> filter(!is.na(SD04_lab)) |>
tabyl(SD04_lab) |> adorn_pct_formatting(digits = 1) |>
kable(caption = "SD04 - Genere")
)
p_g <- dati_ok |> filter(!is.na(SD04_lab)) |> count(SD04_lab) |>
mutate(SD04_lab = fct_reorder(SD04_lab, n)) |>
ggplot(aes(n, SD04_lab)) + geom_col(fill = "#AF7AA1") +
labs(title = "Genere", x = "N.", y = NULL)
print(p_g)
}##
##
## Table: SD04 - Genere
##
## |SD04_lab | n|percent |
## |:----------------------------------------------|--:|:-------|
## |Donna | 15|21.4% |
## |Uomo | 53|75.7% |
## |Persona non binaria / altra identità di genere | 0|0.0% |
## |Preferisco non descrivermi | 1|1.4% |
## |Preferisco non rispondere | 1|1.4% |
## |Not answered | 0|0.0% |
SD07 e SD08.
plot_cat <- function(v, titolo, fill = "#4E79A7") {
dati_ok |> filter(!is.na(.data[[v]])) |> count(.data[[v]]) |> rename(cat = 1) |>
mutate(cat = fct_reorder(cat, n)) |>
ggplot(aes(n, cat)) + geom_col(fill = fill) +
labs(title = titolo, x = "N.", y = NULL)
}
if ("SD07_lab" %in% names(dati_ok)) print(plot_cat("SD07_lab", "Titolo di studio"))Chi facilita, quanto spesso, in che ruolo.
Scala ordinale da “Mai” a “Sempre”.
lvl_fa02 <- c("Mai","Quasi mai","Raramente","A volte",
"Spesso","Quasi sempre","Sempre")
if ("FA02_lab" %in% names(dati_ok)) {
dati_ok |>
filter(!is.na(FA02_lab)) |>
mutate(FA02_lab = factor(FA02_lab, levels = lvl_fa02)) |>
count(FA02_lab) |>
ggplot(aes(FA02_lab, n)) +
geom_col(fill = "#59A14F") +
labs(title = "FA02 - Frequenza iniziale", x = NULL, y = "N. rispondenti") +
theme(axis.text.x = element_text(angle = 30, hjust = 1))
}Organizza, facilita, entrambi o nessuno.
if ("FI01_lab" %in% names(dati_ok)) {
dati_ok |> filter(!is.na(FI01_lab)) |> count(FI01_lab) |>
mutate(FI01_lab = fct_reorder(FI01_lab, n)) |>
ggplot(aes(n, FI01_lab)) +
geom_col(fill = "#E15759") +
labs(title = "FI01 - Ruolo", x = "N. rispondenti", y = NULL)
}Domande dedicate a chi organizza.
Da “Nessuna” a “Moltissime”. Quante persone - secondo il rispondente - farebbero fatica ai suoi eventi.
lvl_or02 <- c("Nessuna persona","Quasi nessuna persona","Poche persone",
"Alcune persone","Parecchie persone","Molte persone",
"Moltissime persone")
if ("OR02_lab" %in% names(dati_ok)) {
dati_ok |>
filter(!is.na(OR02_lab)) |>
mutate(OR02_lab = factor(OR02_lab, levels = lvl_or02)) |>
count(OR02_lab) |>
ggplot(aes(OR02_lab, n)) +
geom_col(fill = "#F28E2B") +
labs(title = "OR02 - Persone percepite in difficolta'",
x = NULL, y = "N. rispondenti") +
theme(axis.text.x = element_text(angle = 30, hjust = 1))
}Con quali famiglie di gioco e in quali ambiti professionali.
if ("SD01_lab" %in% names(dati_ok)) {
dati_ok |> filter(!is.na(SD01_lab)) |> count(SD01_lab) |>
mutate(SD01_lab = fct_reorder(SD01_lab, n)) |>
ggplot(aes(n, SD01_lab)) + geom_col(fill = "#76B7B2") +
labs(title = "SD01 - Tipo di gioco", x = "N.", y = NULL)
}Percentuale di rispondenti che ha selezionato ciascun ambito.
ambito_vars <- grep("^SD02_0[1-6]$", names(dati_ok), value = TRUE)
ambito_labels <- c(SD02_01 = "Formazione",
SD02_02 = "Educazione",
SD02_03 = "Ricerca",
SD02_04 = "Ambiti sociali e terzo settore",
SD02_05 = "Animazione",
SD02_06 = "Nessuna delle precedenti")
amb <- dati_ok |>
select(all_of(ambito_vars)) |>
summarise(across(everything(), ~ mean(.x == 2, na.rm = TRUE) * 100)) |>
pivot_longer(everything(), names_to = "var", values_to = "pct") |>
mutate(ambito = ambito_labels[var],
ambito = fct_reorder(ambito, pct))
amb |> mutate(pct = round(pct, 1)) |> kable(caption = "% di selezione per ambito")| var | pct | ambito |
|---|---|---|
| SD02_01 | 35.7 | Formazione |
| SD02_02 | 32.9 | Educazione |
| SD02_03 | 24.3 | Ricerca |
| SD02_04 | 37.1 | Ambiti sociali e terzo settore |
| SD02_05 | 11.4 | Animazione |
| SD02_06 | 28.6 | Nessuna delle precedenti |
ggplot(amb, aes(pct, ambito)) +
geom_col(fill = "#B07AA1") +
geom_text(aes(label = paste0(round(pct, 1), "%")), hjust = -0.1) +
scale_x_continuous(limits = c(0, NA), expand = expansion(mult = c(0, .15))) +
labs(title = "Ambiti (% di selezione)", x = "% rispondenti", y = NULL)Tempi, missing, speed index.
tibble::tibble(
indicatore = c("Tempo medio questionario (sec)",
"Tempo mediano (sec)",
"% missing medio",
"Speed index medio (TIME_RSI)"),
valore = c(round(mean(dati_ok$TIME_SUM, na.rm = TRUE), 1),
round(median(dati_ok$TIME_SUM, na.rm = TRUE), 1),
paste0(round(mean(dati_ok$MISSING, na.rm = TRUE), 1), "%"),
round(mean(dati_ok$TIME_RSI, na.rm = TRUE), 2))
) |> kable()| indicatore | valore |
|---|---|
| Tempo medio questionario (sec) | 438.3 |
| Tempo mediano (sec) | 338 |
| % missing medio | 4.9% |
| Speed index medio (TIME_RSI) | 1.15 |
Per ogni domanda aperta:
Domande incluse:
open_defs <- tibble::tribble(
~var, ~titolo,
"FA01_01", "Difficolta' o disagi incontrati (facilitatori)",
"FA01_02", "Cosa hanno fatto in risposta (facilitatori)",
"FA01_03", "Pratiche di accoglienza (facilitatori)",
"NE02_01", "Perche' non ho mai organizzato/facilitato",
"OR01_01", "A chi si rivolgono gli organizzatori",
"OR01_02", "Accortezze specifiche (organizzatori)",
"OR03_01", "Chi farebbe fatica a partecipare (organizzatori)"
) |> filter(var %in% names(dati_ok))
# stopwords italiane
stop_it <- unique(c(
"a","ad","ai","al","alla","alle","allo","anche","avere","aveva","avevo","cerco","ce","che",
"chi","ci","cio","co","come","con","contro","cosa","cosi","cui","da","dai","dal","dalla",
"dalle","dallo","dei","del","della","delle","dello","di","dopo","dove","e","ed","egli",
"era","erano","essere","essi","fa","fare","fatto","fino","fra","fu","gia","gli","ha",
"hai","hanno","ho","i","il","in","io","la","le","lei","li","lo","loro","lui","ma","magari",
"me","mi","mia","mie","miei","mio","molto","ne","nei","nel","nella","nelle","nello","noi",
"non","nostra","nostre","nostri","nostro","o","od","oppure","ogni","perche","perc","piu",
"poco","poi","posso","puo","pure","qualche","quale","quali","quando","quanto","quasi",
"quel","quella","quelle","quelli","quello","questa","queste","questi","questo","sara",
"se","senza","si","sia","sono","sta","stata","stato","su","sua","sue","sui","sul","sulla",
"sulle","sullo","suo","suoi","tra","tu","tua","tue","tuo","tuoi","un","una","uno","va",
"vi","voi","vostro","fosse","fossi","tutto","tutti","tutte","tutta","verso","alcuni",
"alcune","mai","sempre","spesso","volta","volte","solo","anzi","pero","invece",
"ora","subito","davvero","proprio","dire","detto","messo","dare","dato","puoi","deve",
"devo","devono","dovrebbe","meno","tanto","allora","cioe","ecc","ecco"
))
# normalizzatore: minuscole, accenti rimossi, solo alfanumerici
normalize <- function(x) {
x <- tolower(x)
x <- stringi::stri_trans_general(x, "Latin-ASCII")
x <- str_replace_all(x, "[^a-z0-9\\s]", " ")
x <- str_squish(x)
x
}
aperte_df <- dati_ok |>
select(CASE, any_of(open_defs$var)) |>
pivot_longer(-CASE, names_to = "var", values_to = "testo") |>
filter(!is.na(testo), nchar(trimws(testo)) > 0) |>
left_join(open_defs, by = "var") |>
mutate(testo_norm = normalize(testo),
n_char = nchar(testo),
n_word = str_count(testo, "\\S+"))N risposte non vuote e parole per domanda.
aperte_df |>
group_by(Domanda = paste0(var, " - ", titolo)) |>
summarise(n_risposte = n(),
parole_medie = round(mean(n_word), 1),
parole_mediana = median(n_word),
caratteri_max = max(n_char),
.groups = "drop") |>
arrange(desc(n_risposte)) |>
kable()| Domanda | n_risposte | parole_medie | parole_mediana | caratteri_max |
|---|---|---|---|---|
| FA01_01 - Difficolta’ o disagi incontrati (facilitatori) | 51 | 20.8 | 15.0 | 606 |
| FA01_02 - Cosa hanno fatto in risposta (facilitatori) | 51 | 33.9 | 29.0 | 878 |
| FA01_03 - Pratiche di accoglienza (facilitatori) | 51 | 32.8 | 25.0 | 603 |
| OR01_01 - A chi si rivolgono gli organizzatori | 47 | 13.7 | 8.0 | 400 |
| OR01_02 - Accortezze specifiche (organizzatori) | 47 | 28.0 | 24.0 | 1073 |
| OR03_01 - Chi farebbe fatica a partecipare (organizzatori) | 42 | 26.7 | 22.5 | 582 |
| NE02_01 - Perche’ non ho mai organizzato/facilitato | 1 | 7.0 | 7.0 | 43 |
aperte_df |>
ggplot(aes(x = fct_rev(fct_infreq(var)), y = n_word)) +
geom_boxplot(fill = "#9ECAE1", outlier.alpha = .4) +
coord_flip() +
labs(title = "Distribuzione lunghezza risposte (parole)",
x = NULL, y = "N. parole per risposta")Ricerca full-text in alto a destra, filtri per colonna sotto le intestazioni. Utile per trovare velocemente casi specifici.
Definizioni dei tre helper usati nelle sottosezioni:
top_words, top_bigrams, wordcloud.
top_words <- function(df, var_nome, n = 20) {
df |> filter(var == var_nome) |>
unnest_tokens(word, testo_norm) |>
filter(!word %in% stop_it, nchar(word) > 2) |>
count(word, sort = TRUE) |> slice_head(n = n)
}
top_bigrams <- function(df, var_nome, n = 15) {
df |> filter(var == var_nome) |>
unnest_tokens(bg, testo_norm, token = "ngrams", n = 2) |>
filter(!is.na(bg)) |>
separate(bg, c("w1", "w2"), sep = " ", remove = FALSE) |>
filter(!w1 %in% stop_it, !w2 %in% stop_it,
nchar(w1) > 2, nchar(w2) > 2) |>
count(bg, sort = TRUE) |> slice_head(n = n)
}
plot_words <- function(tw, titolo, fill = "#4C78A8") {
ggplot(tw, aes(x = n, y = fct_reorder(word, n))) +
geom_col(fill = fill) +
labs(title = titolo, x = "Occorrenze", y = NULL)
}
plot_bigrams <- function(tb, titolo, fill = "#E15759") {
ggplot(tb, aes(x = n, y = fct_reorder(bg, n))) +
geom_col(fill = fill) +
labs(title = titolo, x = "Occorrenze", y = NULL)
}
plot_cloud <- function(tw, titolo) {
ggplot(tw, aes(label = word, size = n, color = n)) +
ggwordcloud::geom_text_wordcloud_area(rm_outside = TRUE) +
scale_size_area(max_size = 14) +
scale_color_viridis_c(option = "D", end = 0.85) +
labs(title = titolo) + theme_void()
}Parole, bigrammi e nube per “Raccontaci un tipo di difficolta’ o disagio con cui hai avuto a che fare”.
if ("FA01_01" %in% open_defs$var) {
tw <- top_words(aperte_df, "FA01_01", 25)
tb <- top_bigrams(aperte_df, "FA01_01", 15)
print(plot_words(tw, "FA01_01 - Top parole"))
print(plot_bigrams(tb, "FA01_01 - Top bigrammi"))
print(plot_cloud(tw, "FA01_01 - Nube"))
}if ("FA01_02" %in% open_defs$var) {
tw <- top_words(aperte_df, "FA01_02", 25)
tb <- top_bigrams(aperte_df, "FA01_02", 15)
print(plot_words(tw, "FA01_02 - Top parole", "#59A14F"))
print(plot_bigrams(tb, "FA01_02 - Top bigrammi", "#59A14F"))
print(plot_cloud(tw, "FA01_02 - Nube"))
}if ("FA01_03" %in% open_defs$var) {
tw <- top_words(aperte_df, "FA01_03", 25)
tb <- top_bigrams(aperte_df, "FA01_03", 15)
print(plot_words(tw, "FA01_03 - Top parole", "#F28E2B"))
print(plot_bigrams(tb, "FA01_03 - Top bigrammi", "#F28E2B"))
print(plot_cloud(tw, "FA01_03 - Nube"))
}if ("OR01_01" %in% open_defs$var) {
tw <- top_words(aperte_df, "OR01_01", 25)
tb <- top_bigrams(aperte_df, "OR01_01", 15)
print(plot_words(tw, "OR01_01 - Top parole", "#76B7B2"))
print(plot_bigrams(tb, "OR01_01 - Top bigrammi", "#76B7B2"))
print(plot_cloud(tw, "OR01_01 - Nube"))
}if ("OR01_02" %in% open_defs$var) {
tw <- top_words(aperte_df, "OR01_02", 25)
tb <- top_bigrams(aperte_df, "OR01_02", 15)
print(plot_words(tw, "OR01_02 - Top parole", "#B07AA1"))
print(plot_bigrams(tb, "OR01_02 - Top bigrammi", "#B07AA1"))
print(plot_cloud(tw, "OR01_02 - Nube"))
}if ("OR03_01" %in% open_defs$var) {
tw <- top_words(aperte_df, "OR03_01", 25)
tb <- top_bigrams(aperte_df, "OR03_01", 15)
print(plot_words(tw, "OR03_01 - Top parole", "#E15759"))
print(plot_bigrams(tb, "OR03_01 - Top bigrammi", "#E15759"))
print(plot_cloud(tw, "OR03_01 - Nube"))
}Lettura iterativa delle risposte, raggruppate per somiglianza semantica. Non sostituisce una codifica formale (Atlas.ti, NVivo, Taguette). Serve a orientare le analisi successive.
Cinque cluster:
Sotto-temi minori: barriere linguistiche (italiano, stranieri), incongruenza di genere come disagio dichiarato.
Sette mosse, non esclusive:
Minoranza: “non sono all’altezza, delego” (lingua, problematiche oltre le competenze).
Due poli:
Strumenti ricorrenti nel secondo polo:
Nota critica: parte del campione non distingue l’accoglienza professionale da educazione sociale generica -> possibile bisogno formativo.
Target piu’ dichiarato: “chiunque” / “tutti”. Segmenti ricorrenti:
Accortezze (riprendono FA01_03 con taglio organizzativo):
Tensione esplicita in una risposta: passione volontaria vs professionalita’ retribuita.
Sette aree:
Non-risposte (“non so”, “non mi e’ mai successo”) = dato interessante: parte degli organizzatori non ha una mappa esplicita delle barriere del proprio evento. Coerente con uno studio esplorativo.
Tensioni che attraversano tutte le aperte:
Ambiente, pacchetti, versioni.
## R version 4.4.1 (2024-06-14 ucrt)
## Platform: x86_64-w64-mingw32/x64
## Running under: Windows 11 x64 (build 26200)
##
## Matrix products: default
##
##
## locale:
## [1] LC_COLLATE=Italian_Italy.utf8 LC_CTYPE=Italian_Italy.utf8
## [3] LC_MONETARY=Italian_Italy.utf8 LC_NUMERIC=C
## [5] LC_TIME=Italian_Italy.utf8
##
## time zone: Europe/Rome
## tzcode source: internal
##
## attached base packages:
## [1] stats graphics grDevices utils datasets methods base
##
## other attached packages:
## [1] ggwordcloud_0.6.2 tidytext_0.4.3 scales_1.4.0 DT_0.33
## [5] knitr_1.51 janitor_2.2.1 readr_2.1.5 forcats_1.0.0
## [9] ggplot2_4.0.0 stringr_1.5.1 tidyr_1.3.1 dplyr_1.1.4
## [13] readxl_1.4.5
##
## loaded via a namespace (and not attached):
## [1] gtable_0.3.6 xfun_0.57 bslib_0.8.0 htmlwidgets_1.6.4
## [5] lattice_0.22-6 tzdb_0.4.0 crosstalk_1.2.1 vctrs_0.6.5
## [9] tools_4.4.1 generics_0.1.3 parallel_4.4.1 tibble_3.2.1
## [13] janeaustenr_1.0.0 pkgconfig_2.0.3 tokenizers_0.3.0 Matrix_1.7-0
## [17] RColorBrewer_1.1-3 S7_0.2.0 lifecycle_1.0.4 compiler_4.4.1
## [21] farver_2.1.2 snakecase_0.11.1 litedown_0.9 htmltools_0.5.8.1
## [25] SnowballC_0.7.1 sass_0.4.9 yaml_2.3.10 pillar_1.10.1
## [29] crayon_1.5.3 jquerylib_0.1.4 cachem_1.1.0 commonmark_2.0.0
## [33] tidyselect_1.2.1 digest_0.6.39 stringi_1.8.4 purrr_1.0.2
## [37] labeling_0.4.3 fastmap_1.2.0 grid_4.4.1 colorspace_2.1-1
## [41] cli_3.6.3 magrittr_2.0.3 withr_3.0.2 bit64_4.0.5
## [45] lubridate_1.9.4 timechange_0.3.0 rmarkdown_2.28 bit_4.0.5
## [49] cellranger_1.1.0 png_0.1-9 hms_1.1.3 evaluate_1.0.4
## [53] viridisLite_0.4.2 markdown_2.0 rlang_1.1.4 gridtext_0.1.6
## [57] Rcpp_1.0.14 glue_1.7.0 xml2_1.3.6 rstudioapi_0.16.0
## [61] vroom_1.6.5 jsonlite_1.8.8 R6_2.6.1