Analisis ini menggunakan data kualitas kopi yang bersumber dari Coffee Quality Institute (CQI), lembaga sertifikasi mutu kopi internasional. Setiap baris data merepresentasikan satu sampel kopi yang dinilai melalui proses cupping oleh panel Q-grader bersertifikat. Variabel yang tercatat meliputi skor sensorik (aroma, rasa, keasaman, body, dan lainnya), karakteristik asal (negara produsen, ketinggian lahan, metode pengolahan), serta jumlah cacat biji. Data dikompilasi oleh James LeDoux dari basis data resmi CQI dan dipublikasikan secara terbuka melalui proyek TidyTuesday, sehingga tersedia dalam format CSV yang dapat diunduh dan diolah langsung di R.
URL <- "https://raw.githubusercontent.com/rfordatascience/tidytuesday/master/data/2020/2020-07-07/coffee_ratings.csv"
d <- read.csv(URL, stringsAsFactors = FALSE)
cat("Jumlah baris mentah:", nrow(d))## Jumlah baris mentah: 1339
d <- subset(d, total_cup_points > 0) # buang skor 0 (galat input)
d$altitude_mean_meters[d$altitude_mean_meters < 1 |
d$altitude_mean_meters > 3000] <- NA # altitude tak masuk akal -> NA
d$alt100 <- d$altitude_mean_meters / 100 # altitude per 100 meter
d$moist_pct <- d$moisture * 100 # kelembapan dalam persen
# Negara: 5 produsen terbanyak + "Other" (referensi = Mexico)
top <- names(sort(table(d$country_of_origin), decreasing = TRUE))[1:5]
d$country <- factor(ifelse(d$country_of_origin %in% top, d$country_of_origin, "Other"))
d$country <- relevel(d$country, ref = "Mexico")
# Metode proses (respons NOMINAL): 3 level utama
keep <- c("Washed / Wet", "Natural / Dry", "Semi-washed / Semi-pulped")
d$proc <- factor(ifelse(d$processing_method %in% keep, d$processing_method, NA),
levels = keep)
# Grade kualitas (respons ORDINAL) -> ambang mengacu skala mutu kopi (skor 0-100)
d$grade <- factor(cut(d$total_cup_points, c(-Inf, 82, 84, Inf),
labels = c("Standard (<82)", "Premium (82-83.9)", "Excellent (>=84)")),
ordered = TRUE)| Model | Respons | Tipe | Variabel |
|---|---|---|---|
| Multinomial | metode proses | Nominal (3) | Washed/Wet, Natural/Dry, Semi-washed |
| Ordinal | grade mutu | Ordinal (3) | Standard < Premium < Excellent |
| Poisson | jumlah cacat | Count | category_two_defects |
Pertanyaan: apakah ketinggian lahan, kelembapan, dan jumlah cacat memengaruhi metode pengolahan yang dipakai? Respons nominal; referensi = Washed / Wet.
Catatan pemodelan:
countrysengaja tidak dipakai di model multinomial karena negara hampir menentukan metode proses (mis. Brasil ⇒ natural), sehingga timbul complete separation yang membuat galat baku meledak. Prediktor kontinu dipakai agar estimasi stabil.
a <- droplevels(na.omit(d[, c("proc", "alt100", "moist_pct", "category_two_defects")]))
a$proc <- relevel(a$proc, ref = "Washed / Wet")
m1 <- multinom(proc ~ alt100 + moist_pct + category_two_defects, data = a, trace = FALSE)
co <- summary(m1)$coefficients; se <- summary(m1)$standard.errors
p <- 2 * (1 - pnorm(abs(co / se)))
tab1 <- data.frame(
Kontras = rep(rownames(co), each = ncol(co)),
Variabel = rep(colnames(co), times = nrow(co)),
b = round(as.vector(t(co)), 4),
RRR = round(exp(as.vector(t(co))), 3),
p_value = round(as.vector(t(p)), 4))
kable(tab1, caption = sprintf("Multinomial (n = %d, ref outcome = Washed/Wet)", nrow(a)))| Kontras | Variabel | b | RRR | p_value |
|---|---|---|---|---|
| Natural / Dry | (Intercept) | 0.7272 | 2.069 | 0.0146 |
| Natural / Dry | alt100 | -0.1185 | 0.888 | 0.0000 |
| Natural / Dry | moist_pct | -0.0705 | 0.932 | 0.0001 |
| Natural / Dry | category_two_defects | 0.0057 | 1.006 | 0.7116 |
| Semi-washed / Semi-pulped | (Intercept) | -1.3786 | 0.252 | 0.0161 |
| Semi-washed / Semi-pulped | alt100 | -0.1456 | 0.864 | 0.0000 |
| Semi-washed / Semi-pulped | moist_pct | 0.0609 | 1.063 | 0.1564 |
| Semi-washed / Semi-pulped | category_two_defects | -0.0213 | 0.979 | 0.4632 |
Interpretasi. Tiap kenaikan ketinggian 100 m menurunkan kecenderungan biji diproses Natural/Dry (RRR 0,89; p < 0,001) maupun Semi-washed (RRR 0,86; p < 0,001) dibanding Washed/Wet — artinya kopi dataran tinggi condong diproses basah (washed), sedangkan natural/dry lebih lazim di lahan lebih rendah dan berkelembapan lebih rendah (RRR 0,93 per 1% kelembapan; p < 0,001). Jumlah cacat tidak berkaitan signifikan dengan pilihan proses.
Pertanyaan: apa yang mendorong kopi naik ke grade mutu lebih tinggi? Respons berurut: Standard < Premium < Excellent. Koefisien positif ⇒ peluang lebih besar ke grade lebih tinggi.
b <- droplevels(na.omit(d[, c("grade", "proc", "alt100", "country", "moist_pct")]))
m2 <- polr(grade ~ proc + alt100 + country + moist_pct, data = b, Hess = TRUE)
ct <- coef(summary(m2))
ct <- cbind(ct, p = 2 * (1 - pnorm(abs(ct[, "t value"]))), OR = exp(ct[, "Value"]))
kable(round(ct, 4),
caption = sprintf("Ordinal proportional-odds (n = %d). Dua baris akhir = ambang.", nrow(b)))| Value | Std. Error | t value | p | OR | |
|---|---|---|---|---|---|
| procNatural / Dry | 0.4970 | 0.1867 | 2.6623 | 0.0078 | 1.6438 |
| procSemi-washed / Semi-pulped | 0.4533 | 0.3100 | 1.4623 | 0.1437 | 1.5735 |
| alt100 | 0.1094 | 0.0188 | 5.8216 | 0.0000 | 1.1156 |
| countryBrazil | 0.8620 | 0.2838 | 3.0371 | 0.0024 | 2.3680 |
| countryColombia | 1.4983 | 0.2356 | 6.3601 | 0.0000 | 4.4740 |
| countryGuatemala | 0.6796 | 0.2120 | 3.2059 | 0.0013 | 1.9731 |
| countryOther | 0.9745 | 0.1823 | 5.3446 | 0.0000 | 2.6499 |
| countryTaiwan | 0.6432 | 0.3204 | 2.0079 | 0.0447 | 1.9026 |
| moist_pct | -0.0207 | 0.0155 | -1.3428 | 0.1794 | 0.9795 |
| Standard (<82)|Premium (82-83.9) | 1.6282 | 0.3201 | 5.0861 | 0.0000 | 5.0945 |
| Premium (82-83.9)|Excellent (>=84) | 3.8829 | 0.3424 | 11.3416 | 0.0000 | 48.5639 |
Interpretasi. Ketinggian adalah pendorong mutu yang kuat: tiap +100 m menaikkan odds naik grade ~12% (OR 1,12; p < 0,001) — sesuai pengetahuan umum bahwa kopi dataran tinggi cenderung lebih bermutu. Pengolahan Natural/Dry berasosiasi dengan grade lebih tinggi (OR 1,64; p = 0,008). Asal negara sangat berpengaruh dibanding Meksiko (referensi): Kolombia OR 4,47; “Other” 2,65; Brasil 2,37; Guatemala 1,97; Taiwan 1,90 (semua signifikan). Kelembapan tidak signifikan.
Pertanyaan: faktor apa yang menaikkan jumlah
cacat tipe-2 (category_two_defects, data
cacah)?
cc <- droplevels(na.omit(d[, c("category_two_defects", "proc", "alt100",
"country", "moist_pct")]))
c(mean = mean(cc$category_two_defects), varians = var(cc$category_two_defects))## mean varians
## 3.719626 29.734199
m3 <- glm(category_two_defects ~ proc + alt100 + country + moist_pct,
data = cc, family = poisson)
s3 <- summary(m3)$coefficients
kable(round(cbind(s3, IRR = exp(s3[, "Estimate"])), 4),
caption = sprintf("Poisson (n = %d). IRR = Incidence Rate Ratio.", nrow(cc)))| Estimate | Std. Error | z value | Pr(>|z|) | IRR | |
|---|---|---|---|---|---|
| (Intercept) | 1.8399 | 0.0876 | 21.0028 | 0.0000 | 6.2956 |
| procNatural / Dry | 0.2636 | 0.0491 | 5.3703 | 0.0000 | 1.3016 |
| procSemi-washed / Semi-pulped | 0.0054 | 0.0818 | 0.0664 | 0.9471 | 1.0054 |
| alt100 | -0.0101 | 0.0050 | -2.0338 | 0.0420 | 0.9899 |
| countryBrazil | -1.0456 | 0.0795 | -13.1595 | 0.0000 | 0.3515 |
| countryColombia | -1.2714 | 0.0772 | -16.4666 | 0.0000 | 0.2804 |
| countryGuatemala | -0.7194 | 0.0542 | -13.2724 | 0.0000 | 0.4870 |
| countryOther | -0.7236 | 0.0427 | -16.9658 | 0.0000 | 0.4850 |
| countryTaiwan | -2.8487 | 0.2084 | -13.6665 | 0.0000 | 0.0579 |
| moist_pct | 0.0170 | 0.0049 | 3.4405 | 0.0006 | 1.0171 |
disp <- deviance(m3) / df.residual(m3)
m3nb <- glm.nb(category_two_defects ~ proc + alt100 + country + moist_pct, data = cc)
data.frame(dispersi_dev_df = round(disp, 2),
AIC_Poisson = round(AIC(m3), 1),
AIC_NegBin = round(AIC(m3nb), 1))| dispersi_dev_df | AIC_Poisson | AIC_NegBin |
|---|---|---|
| 4.52 | 6526.3 | 4475.4 |
Interpretasi. Pengolahan Natural/Dry menaikkan laju cacat ~30% (IRR 1,30; p < 0,001) dibanding washed — wajar karena pengeringan dengan kulit buah lebih rawan cacat. Tiap +100 m ketinggian sedikit menurunkan cacat (IRR 0,99; p = 0,04), dan tiap +1% kelembapan menaikkan cacat ~1,7% (p < 0,001). Dibanding Meksiko, semua negara lain punya laju cacat jauh lebih rendah (mis. Taiwan IRR 0,06; Kolombia 0,28).
Diagnostik overdispersi. Varians cacat (≈30) jauh di atas rata-ratanya (≈3,7), dan dispersi deviance/df ≈ 4.5 (idealnya ≈1). Asumsi Poisson dilanggar, sehingga galat baku Poisson terlalu optimis. Negative Binomial jauh lebih cocok (AIC 4475 vs Poisson 6526). Untuk data cacah seperti ini, gunakan Negative Binomial / quasi-Poisson.
nnet::multinom) untuk
respons nominal — metode proses ditentukan terutama
oleh ketinggian & kelembapan.MASS::polr) untuk respons
berurut — mutu kopi naik seiring ketinggian, pengolahan
natural, dan asal negara tertentu.glm, lalu
MASS::glm.nb) untuk count — cacat
dipengaruhi proses/asal/kelembapan; data overdispersi ⇒ pakai Negative
Binomial..Rmd ini di RStudio (paket:
rmarkdown, knitr, nnet,
MASS).