Dokumen ini menyajikan analisis Survei Kepuasan Pengguna terhadap kinerja lulusan S2 Statistika Terapan FMIPA UNPAD (data tahun 2025). Fokus analisis mencakup:
Catatan privasi: bila
params$anonymize = TRUE, kolom identitas (mis. email/nama) tidak ditampilkan pada lampiran data.
Data dibaca dari berkas Excel:
Apabila path default tidak ditemukan, laporan akan mencoba membaca dari file alternatif di working directory.
Item kepuasan menggunakan kategori kualitatif:
Untuk perhitungan indeks, kategori dipetakan ke skala numerik 1-4:
pkgs <- c(
"tidyverse","readxl","janitor","stringr","kableExtra",
"DT","psych","scales","glue","forcats","ggrepel"
)
to_install <- pkgs[!pkgs %in% rownames(installed.packages())]
if(length(to_install) > 0) install.packages(to_install, repos = "https://cloud.r-project.org")
invisible(lapply(pkgs, library, character.only = TRUE))
resolve_excel_path <- function(p){
if (file.exists(p)) return(p)
candidates <- c(
basename(p),
"SurveyKepPengguna.xlsx",
"SurveyKepPenggguna.xlsx"
)
hit <- candidates[file.exists(candidates)][1]
if (!is.na(hit)) return(hit)
stop("File Excel tidak ditemukan. Sesuaikan params$excel_path atau letakkan file di working directory.")
}
excel_path <- resolve_excel_path(params$excel_path)
raw <- readxl::read_excel(excel_path, sheet = params$sheet_name) |>
janitor::clean_names()
tibble(
n_respon = nrow(raw),
n_kolom = ncol(raw),
file_aktif = excel_path
) |>
kableExtra::kbl(caption = "Ringkasan ukuran data") |>
kableExtra::kable_styling(full_width = FALSE, bootstrap_options = c("striped","hover","condensed"))
| n_respon | n_kolom | file_aktif |
|---|---|---|
| 51 | 14 | /Users/mindra/@SurveyPengguna/2025/SurveyKepPenggguna.xlsx |
id_cols <- c("timestamp","email_address","nama_penilai",
"nama_staf_karyawan_lulusan_statistika_unpad_yang_dinilai")
item_cols <- raw |>
select(where(is.character)) |>
select(matches("^bagaimana_")) |>
names()
if(length(item_cols) == 0) stop("Kolom item penilaian tidak ditemukan (pola '^bagaimana_').")
item_cols
[1] "bagaimana_kualitas_etika_lulusan_tersebut"
[2] "bagaimana_tingkat_keahlian_statistika_yang_dimiliki_oleh_lulusan_tersebut"
[3] "bagaimana_tingkat_penguasaan_bahasa_asing_oleh_lulusan_tersebut"
[4] "bagaimana_tingkat_penguasaan_teknologi_informasi_oleh_lulusan_terkait"
[5] "bagaimana_tingkat_kemampuan_berkomunikasi_dari_lulusan_tersebut"
[6] "bagaimana_kemampuan_lulusan_dalam_bekerja_sama_dengan_rekan_rekan_kerjanya"
[7] "bagaimana_kemampuan_lulusan_tersebut_dalam_pengembangan_diri"
std_text <- function(x){
x <- as.character(x)
x <- stringr::str_squish(x)
x <- stringr::str_to_sentence(x)
x
}
df <- raw |>
mutate(across(all_of(item_cols), std_text))
likert_levels <- c("Kurang","Cukup","Baik","Sangat baik")
likert_scores <- c("Kurang" = 1, "Cukup" = 2, "Baik" = 3, "Sangat baik" = 4)
df <- df |>
mutate(across(all_of(item_cols), ~ factor(.x, levels = likert_levels, ordered = TRUE))) |>
mutate(across(all_of(item_cols), ~ unname(likert_scores[as.character(.x)]), .names = "{.col}_score"))
score_cols <- paste0(item_cols, "_score")
missing_tbl <- tibble(
item = item_cols,
missing_n = map_int(item_cols, ~ sum(is.na(df[[.x]]))),
missing_pct = map_dbl(item_cols, ~ mean(is.na(df[[.x]])))
) |>
mutate(item = str_to_sentence(str_replace_all(str_remove(item, "^bagaimana_"), "_", " ")),
missing_pct = scales::percent(missing_pct, accuracy = 0.1)) |>
arrange(desc(missing_n))
missing_tbl |>
kableExtra::kbl(caption = "Jumlah missing per item", col.names = c("Item","Jumlah Missing","Persentase")) |>
kableExtra::kable_styling(full_width = FALSE, bootstrap_options = c("striped","hover"))
| Item | Jumlah Missing | Persentase |
|---|---|---|
| Kualitas etika lulusan tersebut | 0 | 0.0% |
| Tingkat keahlian statistika yang dimiliki oleh lulusan tersebut | 0 | 0.0% |
| Tingkat penguasaan bahasa asing oleh lulusan tersebut | 0 | 0.0% |
| Tingkat penguasaan teknologi informasi oleh lulusan terkait | 0 | 0.0% |
| Tingkat kemampuan berkomunikasi dari lulusan tersebut | 0 | 0.0% |
| Kemampuan lulusan dalam bekerja sama dengan rekan rekan kerjanya | 0 | 0.0% |
| Kemampuan lulusan tersebut dalam pengembangan diri | 0 | 0.0% |
profile_candidates <- c(
"institusi_tempat_bekerja_penilai",
"jabatan_penilai_boleh_tidak_diisi",
"tahun_lulus_staf_yang_dinilai_dari_statistika_unpad_boleh_tidak_diisi"
)
profile_cols <- profile_candidates[profile_candidates %in% names(df)]
profile_summary <- function(data, col){
data |>
mutate(val = as.character(.data[[col]])) |>
mutate(val = ifelse(is.na(val) | val == "", "(Tidak diisi)", val)) |>
count(val, sort = TRUE) |>
mutate(persen = scales::percent(n/sum(n), accuracy = 0.1)) |>
rename(Kategori = val, Jumlah = n, Persentase = persen)
}
if(length(profile_cols) == 0){
cat("Kolom profil responden tidak ditemukan pada data.")
} else {
for(col in profile_cols){
tab <- profile_summary(df, col)
tbl_html <- tab |>
kableExtra::kbl(caption = paste0("Distribusi: ", str_to_sentence(str_replace_all(col, "_", " ")))) |>
kableExtra::kable_styling(full_width = FALSE, bootstrap_options = c("striped","hover")) |>
as.character()
cat(tbl_html, "\n")
}
}
| Kategori | Jumlah | Persentase |
|---|---|---|
| Bank Indonesia | 3 | 5.9% |
| Tokopedia | 3 | 5.9% |
| Jabar Digital Service | 2 | 3.9% |
| PT Bank DBS Indonesia | 2 | 3.9% |
| BAN | 1 | 2.0% |
| BAPPEDA KAB. PANGAMDARAN | 1 | 2.0% |
| BNI | 1 | 2.0% |
| Badan Pusat Statistik | 1 | 2.0% |
| Bank Woori Saudara | 1 | 2.0% |
| Bappeda Kota Jambi | 1 | 2.0% |
| Bappeda Padang Panjang | 1 | 2.0% |
| DISKOP UKM BID. KELEMBAGAAN DAN PEMBERDAYAAN KOPERASI | 1 | 2.0% |
| DPMD JABAR | 1 | 2.0% |
| Dinas Komunikasi Informatika Statistik dan Persandian | 1 | 2.0% |
| Dinas Komunikasi Informatika Statitik dan Persandian | 1 | 2.0% |
| Dinas Koperasi dan Usaha Kecil dan Menengah Kabupaten Bandung | 1 | 2.0% |
| Dinas Perdagangan dan Perindustrian Kabupaten Bandung | 1 | 2.0% |
| Fazz Financial Group | 1 | 2.0% |
| Frontier Market Research | 1 | 2.0% |
| Idx partners | 1 | 2.0% |
| JABAR DIGITAL SERVICE | 1 | 2.0% |
| KG Media | 1 | 2.0% |
| Kementerian Dalam Negeri | 1 | 2.0% |
| Kementerian Pertanian | 1 | 2.0% |
| MNC Channels | 1 | 2.0% |
| Mandiri Tunas Finance | 1 | 2.0% |
| PT Bank Central Asia, Tbk | 1 | 2.0% |
| PT Naturalva Herba Indonesia | 1 | 2.0% |
| PT Nipro Indonesia Jaya | 1 | 2.0% |
| PT SUCOFINDO | 1 | 2.0% |
| PT Saji Bersama Jaya (KINSEI) | 1 | 2.0% |
| PT Sinbad Karya Perdagangan | 1 | 2.0% |
| PT Sucofindo | 1 | 2.0% |
| PT. BANK SYARIAH INDONESIA Tbk. | 1 | 2.0% |
| PT. Bank Maybank Indonesia | 1 | 2.0% |
| PT. Cipta kridatama | 1 | 2.0% |
| PT. GoTo Aplikasi Karya Anak Bangsa | 1 | 2.0% |
| PT. Propan Raya | 1 | 2.0% |
| PT. Sendok Garpu Internasional | 1 | 2.0% |
| Perwakilan BKKBN Provinsi Jawa Barat | 1 | 2.0% |
| Pt. Cakrawala andalas televisi | 1 | 2.0% |
| Pusat Analis Kebijakan Obat dan Makanan BPOM | 1 | 2.0% |
| RCTI | 1 | 2.0% |
| Riau Andalan Pulp & Paper | 1 | 2.0% |
| Universitas Kuningan | 1 | 2.0% |
| Kategori | Jumlah | Persentase |
|---|---|---|
| (Tidak diisi) | 12 | 23.5% |
| Analis/Manajer | 2 | 3.9% |
| Business Analytics | 2 | 3.9% |
| Data Analytics Manager | 2 | 3.9% |
| Kepala Bidang Statistik | 2 | 3.9% |
|
|
1 | 2.0% |
| Analis Kepegawaian Ahli Muda | 1 | 2.0% |
| Analis kebijakan Ahli Muda | 1 | 2.0% |
| Analisis Perencana Muda | 1 | 2.0% |
| Analytics Manager | 1 | 2.0% |
| CEO | 1 | 2.0% |
| Data Analyst Lead | 1 | 2.0% |
| Data Lead | 1 | 2.0% |
| Data Product Manager | 1 | 2.0% |
| Dekan | 1 | 2.0% |
| Departement Head Research and Development | 1 | 2.0% |
| Department Head QA/QC | 1 | 2.0% |
| Fungsional Perencana Ahli Muda | 1 | 2.0% |
| General Manager | 1 | 2.0% |
| Geospatial Engineering Lead | 1 | 2.0% |
| JFT Perencana Ahli Muda | 1 | 2.0% |
| Junior Associate | 1 | 2.0% |
| Kepala Bagian Pengembangan Jasa | 1 | 2.0% |
| Kepala Bidang | 1 | 2.0% |
| Kepala Bidang PPEPD | 1 | 2.0% |
| Kepala Unit Branch Support | 1 | 2.0% |
| Ketua | 1 | 2.0% |
| Koordinator fungsi statistik produksi/Statistisi ahli muda | 1 | 2.0% |
| MANAGER MARKETING CPRP | 1 | 2.0% |
| Operational Contol Center Engineering | 1 | 2.0% |
| Regional Manager Field Research | 1 | 2.0% |
| Sekretaris | 1 | 2.0% |
| Senior Manager, Data Analytics | 1 | 2.0% |
| Sr consultant | 1 | 2.0% |
| Subkoordinator DPLHP I | 1 | 2.0% |
| Supervisor | 1 | 2.0% |
| Kategori | Jumlah | Persentase |
|---|---|---|
| 2017 | 9 | 17.6% |
| 2020 | 9 | 17.6% |
| 2021 | 9 | 17.6% |
| (Tidak diisi) | 8 | 15.7% |
| 2018 | 7 | 13.7% |
| 2019 | 7 | 13.7% |
| 2016 | 2 | 3.9% |
nice_item <- function(x){
x |>
str_replace("^bagaimana_", "") |>
str_replace_all("_", " ") |>
str_squish() |>
str_to_sentence()
}
dist_all <- map_dfr(item_cols, function(col){
df |>
mutate(val = as.character(.data[[col]])) |>
mutate(val = ifelse(is.na(val) | val=="", "(Missing)", val)) |>
count(val) |>
mutate(
persen = n/sum(n),
item = col
)
}) |>
mutate(
item_label = nice_item(item),
val = factor(val, levels = c("Kurang","Cukup","Baik","Sangat baik","(Missing)"), ordered = TRUE)
)
palette_likert <- c(
"Kurang" = "#d7263d",
"Cukup" = "#ffb703",
"Baik" = "#219ebc",
"Sangat baik" = "#2a9d8f",
"(Missing)" = "#adb5bd"
)
p_dist <- dist_all |>
filter(val != "(Missing)") |>
ggplot(aes(x = fct_reorder(item_label, persen, .fun = max), y = persen, fill = val)) +
geom_col(position = "fill", color = "white", width = 0.8) +
coord_flip() +
scale_y_continuous(labels = scales::percent_format(accuracy = 1)) +
scale_fill_manual(values = palette_likert) +
labs(
title = "Komposisi Penilaian per Dimensi",
subtitle = "Proporsi kategori jawaban pada tiap dimensi kompetensi",
x = NULL,
y = "Persentase",
fill = "Kategori"
) +
theme_minimal(base_size = 12) +
theme(
panel.grid.major.y = element_blank(),
plot.title = element_text(face = "bold", color = "#0b3954"),
legend.position = "bottom"
)
p_dist
item_summary <- tibble(item = item_cols) |>
mutate(
item_label = nice_item(item),
mean_score = map_dbl(item, ~ mean(df[[paste0(.x, "_score")]], na.rm = TRUE)),
sd_score = map_dbl(item, ~ sd(df[[paste0(.x, "_score")]], na.rm = TRUE)),
n_valid = map_int(item, ~ sum(!is.na(df[[paste0(.x, "_score")]]))),
top2 = map_dbl(item, ~ {
x <- df[[.x]]
mean(x %in% c("Baik","Sangat baik"), na.rm = TRUE)
}),
low2 = map_dbl(item, ~ {
x <- df[[.x]]
mean(x %in% c("Kurang","Cukup"), na.rm = TRUE)
})
) |>
arrange(desc(mean_score))
item_summary |>
mutate(
`Rata-rata (1-4)` = round(mean_score, 3),
SD = round(sd_score, 3),
`Top-2 Box` = scales::percent(top2, accuracy = 0.1),
`Low-2 Box` = scales::percent(low2, accuracy = 0.1)
) |>
select(Dimensi = item_label, `N Valid` = n_valid, `Rata-rata (1-4)`, SD, `Top-2 Box`, `Low-2 Box`) |>
kableExtra::kbl(caption = "Ringkasan skor per dimensi") |>
kableExtra::kable_styling(full_width = TRUE, bootstrap_options = c("striped","hover","condensed"))
| Dimensi | N Valid | Rata-rata (1-4) | SD | Top-2 Box | Low-2 Box |
|---|---|---|---|---|---|
| Kualitas etika lulusan tersebut | 51 | 3.804 | 0.448 | 98.0% | 2.0% |
| Kemampuan lulusan dalam bekerja sama dengan rekan rekan kerjanya | 51 | 3.608 | 0.532 | 98.0% | 2.0% |
| Kemampuan lulusan tersebut dalam pengembangan diri | 51 | 3.588 | 0.606 | 98.0% | 2.0% |
| Tingkat keahlian statistika yang dimiliki oleh lulusan tersebut | 51 | 3.471 | 0.703 | 92.2% | 7.8% |
| Tingkat penguasaan teknologi informasi oleh lulusan terkait | 51 | 3.471 | 0.612 | 98.0% | 2.0% |
| Tingkat kemampuan berkomunikasi dari lulusan tersebut | 51 | 3.392 | 0.666 | 94.1% | 5.9% |
| Tingkat penguasaan bahasa asing oleh lulusan tersebut | 51 | 2.961 | 0.692 | 82.4% | 17.6% |
item_summary |>
mutate(item_label = fct_reorder(item_label, mean_score)) |>
ggplot(aes(x = item_label, y = mean_score, fill = mean_score)) +
geom_col(width = 0.75) +
geom_text(aes(label = round(mean_score, 2)), hjust = -0.15, size = 3.5) +
coord_flip() +
scale_fill_gradient(low = "#8ecae6", high = "#023047") +
scale_y_continuous(limits = c(1, 4.2), breaks = 1:4) +
labs(
title = "Rata-rata Skor Kepuasan per Dimensi",
subtitle = "Skala 1-4 (semakin tinggi semakin baik)",
x = NULL,
y = "Rata-rata skor"
) +
theme_minimal(base_size = 12) +
theme(legend.position = "none", plot.title = element_text(face = "bold", color = "#0b3954"))
| n_respon | mean_index | sd_index | min_index | max_index |
|---|---|---|---|---|
| 51 | 3.471 | 0.461 | 1.429 | 4 |
ggplot(df_scores, aes(x = indeks_kepuasan)) +
geom_histogram(bins = 12, fill = "#2a9d8f", color = "white", alpha = 0.95) +
geom_vline(xintercept = overall$mean_index, linetype = "dashed", color = "#d62828", linewidth = 1) +
annotate("text", x = overall$mean_index, y = Inf, label = paste0("Mean = ", round(overall$mean_index, 2)),
vjust = 1.8, hjust = -0.05, color = "#d62828", size = 3.8) +
scale_x_continuous(limits = c(1,4), breaks = 1:4) +
labs(
title = "Distribusi Indeks Kepuasan Pengguna",
subtitle = "Garis putus-putus menunjukkan nilai rata-rata indeks",
x = "Indeks Kepuasan (1-4)",
y = "Jumlah responden"
) +
theme_minimal(base_size = 12) +
theme(plot.title = element_text(face = "bold", color = "#0b3954"))
mean_cut <- mean(item_summary$mean_score, na.rm = TRUE)
sd_cut <- mean(item_summary$sd_score, na.rm = TRUE)
item_summary |>
mutate(
prioritas = case_when(
mean_score < mean_cut & sd_score > sd_cut ~ "Prioritas Tinggi",
mean_score < mean_cut & sd_score <= sd_cut ~ "Perlu Penguatan",
mean_score >= mean_cut & sd_score > sd_cut ~ "Jaga Konsistensi",
TRUE ~ "Pertahankan Kinerja"
)
) |>
ggplot(aes(x = sd_score, y = mean_score, color = prioritas, label = item_label)) +
geom_hline(yintercept = mean_cut, linetype = "dashed", color = "#6c757d") +
geom_vline(xintercept = sd_cut, linetype = "dashed", color = "#6c757d") +
geom_point(size = 3.8, alpha = 0.95) +
ggrepel::geom_text_repel(size = 3.2, max.overlaps = 20) +
scale_color_manual(values = c(
"Prioritas Tinggi" = "#d62828",
"Perlu Penguatan" = "#f77f00",
"Jaga Konsistensi" = "#277da1",
"Pertahankan Kinerja" = "#2a9d8f"
)) +
labs(
title = "Peta Prioritas Dimensi",
subtitle = "Sumbu Y: rata-rata skor, Sumbu X: variasi penilaian (SD)",
x = "Standar deviasi",
y = "Rata-rata skor",
color = "Status"
) +
theme_minimal(base_size = 12) +
theme(plot.title = element_text(face = "bold", color = "#0b3954"), legend.position = "bottom")
score_mat <- df |> select(all_of(score_cols))
if(ncol(score_mat) >= 2){
a <- psych::alpha(score_mat)
alpha_val <- a$total$raw_alpha
interpretasi_alpha <- case_when(
alpha_val >= 0.9 ~ "Sangat baik (excellent)",
alpha_val >= 0.8 ~ "Baik (good)",
alpha_val >= 0.7 ~ "Cukup (acceptable)",
alpha_val >= 0.6 ~ "Marginal",
TRUE ~ "Rendah"
)
tibble(
`Jumlah item` = ncol(score_mat),
`Cronbach's alpha` = round(alpha_val, 3),
Interpretasi = interpretasi_alpha
) |>
kableExtra::kbl(caption = "Reliabilitas internal instrumen") |>
kableExtra::kable_styling(full_width = FALSE, bootstrap_options = c("striped","hover"))
a$alpha.drop |>
as.data.frame() |>
rownames_to_column("Item") |>
mutate(Item = nice_item(str_remove(Item, "_score$"))) |>
select(Item, raw_alpha) |>
rename(`Alpha jika item dihapus` = raw_alpha) |>
mutate(`Alpha jika item dihapus` = round(`Alpha jika item dihapus`, 3)) |>
kableExtra::kbl(caption = "Diagnostik: alpha jika item dihapus") |>
kableExtra::kable_styling(full_width = FALSE, bootstrap_options = c("striped","hover"))
} else {
cat("Tidak cukup item untuk mengukur reliabilitas.")
}
| Item | Alpha jika item dihapus |
|---|---|
| Kualitas etika lulusan tersebut | 0.860 |
| Tingkat keahlian statistika yang dimiliki oleh lulusan tersebut | 0.853 |
| Tingkat penguasaan bahasa asing oleh lulusan tersebut | 0.858 |
| Tingkat penguasaan teknologi informasi oleh lulusan terkait | 0.853 |
| Tingkat kemampuan berkomunikasi dari lulusan tersebut | 0.848 |
| Kemampuan lulusan dalam bekerja sama dengan rekan rekan kerjanya | 0.861 |
| Kemampuan lulusan tersebut dalam pengembangan diri | 0.832 |
top_items <- item_summary |> slice_max(mean_score, n = min(3, nrow(item_summary)))
bottom_items <- item_summary |> slice_min(mean_score, n = min(3, nrow(item_summary)))
cat('<div class="callout"><strong>Temuan utama:</strong><br/>')
Temuan utama:
cat(glue::glue('- Rata-rata indeks kepuasan agregat: <strong>{round(overall$mean_index,3)}</strong> ({kategori_indeks}).<br/>'))
cat(glue::glue('- Dimensi terkuat: <strong>{paste(top_items$item_label, collapse = "; ")}</strong>.<br/>'))
cat(glue::glue('- Dimensi prioritas perbaikan: <strong>{paste(bottom_items$item_label, collapse = "; ")}</strong>.<br/>'))
cat('- Fokus perbaikan diarahkan pada dimensi skor rendah dengan proporsi Low-2 Box tertinggi.</div>')
Top-2 Box, Low-2 Box, dan
Indeks Kepuasan sebagai KPI semesteran untuk evaluasi
kurikulum berbasis bukti.Laporan ini dirancang untuk mendukung siklus peningkatan mutu berkelanjutan secara terukur. Hasil analisis menampilkan gambaran komprehensif mengenai tingkat kepuasan pengguna, kekuatan utama lulusan, serta area prioritas perbaikan yang dapat langsung ditindaklanjuti oleh program studi.