Analisis Rantai Markov
DISUSUN OLEH KELOMPOK 3 MATAKULIAH PROSES STOKASTIK
- Angelia Yuflih Pambayun (23031030002)
- Safira Wafda Danantika (23031030009)
- Devi Roslidiyanti (23031030024)
- Gilang Adi Prasetyo (23031030040)
- Patrecia Daniela Butarbutar (23031030044)
- Nayli Kurnia Ilahi (23031030045)
- Nafisa Salsabila (23031030048)
DATA PENJUALAN Z8 (HT70) Jan 2015 - Jun 2017
periode <- c(
"Jan-15","Feb-15","Mar-15","Apr-15","May-15","Jun-15",
"Jul-15","Aug-15","Sep-15","Oct-15","Nov-15","Dec-15",
"Jan-16","Feb-16","Mar-16","Apr-16","May-16","Jun-16",
"Jul-16","Aug-16","Sep-16","Oct-16","Nov-16","Dec-16",
"Jan-17","Feb-17","Mar-17","Apr-17","May-17","Jun-17"
)
penjualan <- c(
66, 70, 69, 76, 85, 59,
58, 109, 153, 70, 102, 62,
76, 69, 74, 76, 85, 80,
107, 83, 103, 107, 112, 83,
78, 153, 53, 67, 63, 58
)
df <- data.frame(Periode = periode, Penjualan = penjualan)
knitr::kable(df, caption = "Tabel 1. Data Penjualan HT70 (Z8)", align = "c")| Periode | Penjualan |
|---|---|
| Jan-15 | 66 |
| Feb-15 | 70 |
| Mar-15 | 69 |
| Apr-15 | 76 |
| May-15 | 85 |
| Jun-15 | 59 |
| Jul-15 | 58 |
| Aug-15 | 109 |
| Sep-15 | 153 |
| Oct-15 | 70 |
| Nov-15 | 102 |
| Dec-15 | 62 |
| Jan-16 | 76 |
| Feb-16 | 69 |
| Mar-16 | 74 |
| Apr-16 | 76 |
| May-16 | 85 |
| Jun-16 | 80 |
| Jul-16 | 107 |
| Aug-16 | 83 |
| Sep-16 | 103 |
| Oct-16 | 107 |
| Nov-16 | 112 |
| Dec-16 | 83 |
| Jan-17 | 78 |
| Feb-17 | 153 |
| Mar-17 | 53 |
| Apr-17 | 67 |
| May-17 | 63 |
| Jun-17 | 58 |
TAHAP 1 PENENTUAN STATE ORDE SATU
State: P = 0 (naik/sama), K = 1 (turun) Bandingkan penjualan[t] vs penjualan[t-1]
n <- length(penjualan)
state_label <- ifelse(penjualan[2:n] >= penjualan[1:(n-1)], "P", "K")
state_val <- ifelse(penjualan[2:n] >= penjualan[1:(n-1)], 0, 1)
df_state <- data.frame(
Periode = periode[2:n],
Penjualan = penjualan[2:n],
Prev = penjualan[1:(n-1)],
Perubahan = penjualan[2:n] - penjualan[1:(n-1)],
State_Label = state_label,
State_Val = state_val
)
knitr::kable(df_state,
col.names = c("Periode","Jual (t)","Jual (t-1)","Δ","State","Nilai"),
caption = "Tabel 2. Penentuan State Perbulan (Orde Satu)",
align = "c")| Periode | Jual (t) | Jual (t-1) | Δ | State | Nilai |
|---|---|---|---|---|---|
| Feb-15 | 70 | 66 | 4 | P | 0 |
| Mar-15 | 69 | 70 | -1 | K | 1 |
| Apr-15 | 76 | 69 | 7 | P | 0 |
| May-15 | 85 | 76 | 9 | P | 0 |
| Jun-15 | 59 | 85 | -26 | K | 1 |
| Jul-15 | 58 | 59 | -1 | K | 1 |
| Aug-15 | 109 | 58 | 51 | P | 0 |
| Sep-15 | 153 | 109 | 44 | P | 0 |
| Oct-15 | 70 | 153 | -83 | K | 1 |
| Nov-15 | 102 | 70 | 32 | P | 0 |
| Dec-15 | 62 | 102 | -40 | K | 1 |
| Jan-16 | 76 | 62 | 14 | P | 0 |
| Feb-16 | 69 | 76 | -7 | K | 1 |
| Mar-16 | 74 | 69 | 5 | P | 0 |
| Apr-16 | 76 | 74 | 2 | P | 0 |
| May-16 | 85 | 76 | 9 | P | 0 |
| Jun-16 | 80 | 85 | -5 | K | 1 |
| Jul-16 | 107 | 80 | 27 | P | 0 |
| Aug-16 | 83 | 107 | -24 | K | 1 |
| Sep-16 | 103 | 83 | 20 | P | 0 |
| Oct-16 | 107 | 103 | 4 | P | 0 |
| Nov-16 | 112 | 107 | 5 | P | 0 |
| Dec-16 | 83 | 112 | -29 | K | 1 |
| Jan-17 | 78 | 83 | -5 | K | 1 |
| Feb-17 | 153 | 78 | 75 | P | 0 |
| Mar-17 | 53 | 153 | -100 | K | 1 |
| Apr-17 | 67 | 53 | 14 | P | 0 |
| May-17 | 63 | 67 | -4 | K | 1 |
| Jun-17 | 58 | 63 | -5 | K | 1 |
TAHAP 2 — MATRIKS FREKUENSI TRANSISI 2x2
Hitung berapa kali terjadi transisi
sv <- state_val # panjang 29
frek2 <- matrix(0, nrow = 2, ncol = 2,
dimnames = list(
c("dari_P","dari_K"),
c("ke_P", "ke_K")
))
for (t in 1:(length(sv) - 1)) {
i <- sv[t] + 1 # baris (1-based)
j <- sv[t+1] + 1 # kolom (1-based)
frek2[i, j] <- frek2[i, j] + 1
}
knitr::kable(frek2,
caption = "Tabel 3. Matriks Frekuensi Transisi f_ij (2×2)",
align = "c")| ke_P | ke_K | |
|---|---|---|
| dari_P | 6 | 10 |
| dari_K | 9 | 3 |
TAHAP 3 MATRIKS PROBABILITAS TRANSISI 2x2
P_ij = f_ij / jumlah baris i
row_sum2 <- rowSums(frek2)
prob2 <- matrix(0, nrow = 2, ncol = 2,
dimnames = list(
c("P (Perolehan)","K (Kehilangan)"),
c("→ P","→ K")
))
for (i in 1:2) {
if (row_sum2[i] > 0) {
prob2[i, ] <- frek2[i, ] / row_sum2[i]
}
}
knitr::kable(round(prob2, 4),
caption = "Tabel 4. Matriks Probabilitas Transisi P (2×2)",
align = "c")| → P | → K | |
|---|---|---|
| P (Perolehan) | 0.375 | 0.625 |
| K (Kehilangan) | 0.750 | 0.250 |
TAHAP 4 ITERASI STEADY STATE
S(t+1) = S(t) · P hingga konvergen
S <- c(0.5, 0.5) # vektor awal seragam
tol <- 1e-8
max_iter <- 10000
history <- list()
for (iter in 1:max_iter) {
S_new <- as.numeric(S %*% prob2)
if (iter <= 5 || iter %% 500 == 0) {
history[[length(history) + 1]] <- c(iter = iter, round(S_new, 6))
}
if (max(abs(S_new - S)) < tol) {
cat("Steady state tercapai pada iterasi ke:", iter, "\n")
S <- S_new
break
}
S <- S_new
}## Steady state tercapai pada iterasi ke: 17
df_hist <- do.call(rbind, history)
df_hist <- as.data.frame(df_hist)
colnames(df_hist) <- c("Iterasi", "S_P", "S_K")
knitr::kable(df_hist,
caption = "Tabel 5. Riwayat Iterasi Menuju Steady State",
align = "c", row.names = FALSE)| Iterasi | S_P | S_K |
|---|---|---|
| 1 | 0.562500 | 0.437500 |
| 2 | 0.539062 | 0.460938 |
| 3 | 0.547852 | 0.452148 |
| 4 | 0.544556 | 0.455444 |
| 5 | 0.545792 | 0.454208 |
TAHAP 5 HASIL STEADY STATE & PANGSA PASAR
##
## === PANGSA PASAR HT70 (ORDE SATU) ===
## Perolehan konsumen (P) : 0.5455 (54.55%)
## Kehilangan konsumen (K): 0.4545 (45.45%)
## Jumlah : 1.0000
df_ss <- data.frame(
Kondisi = c("Perolehan (P)","Kehilangan (K)"),
Probabilitas = round(S, 4),
Persentase = paste0(round(S * 100, 2), "%")
)
knitr::kable(df_ss,
caption = "Tabel 6. Pangsa Pasar HT70 — Steady State Orde Satu",
align = "c")| Kondisi | Probabilitas | Persentase |
|---|---|---|
| Perolehan (P) | 0.5455 | 54.55% |
| Kehilangan (K) | 0.4545 | 45.45% |
TAHAP 6 PREDIKSI PENJUALAN JULI 2017
Prediksi = Jual(t) + Jual(t) × %P − Jual(t) × %K
jual_juni <- penjualan[length(penjualan)] # Jun-17 = 58
prediksi_juli_o1 <- jual_juni + (jual_juni * S[1]) - (jual_juni * S[2])
cat("\n=== PREDIKSI PENJUALAN JULI 2017 (ORDE SATU) ===\n")##
## === PREDIKSI PENJUALAN JULI 2017 (ORDE SATU) ===
## Penjualan Juni 2017 : 58 pcs
## % Perolehan : 54.55%
## % Kehilangan : 45.45%
cat(sprintf(" Prediksi Juli 2017 : %.2f pcs ≈ %d pcs\n",
prediksi_juli_o1, round(prediksi_juli_o1)))## Prediksi Juli 2017 : 63.27 pcs ≈ 63 pcs
df_pred <- data.frame(
Keterangan = c(
"Penjualan Juni 2017",
"% Perolehan (P)",
"% Kehilangan (K)",
"Prediksi Juli 2017 (desimal)",
"Prediksi Juli 2017 (dibulatkan)"
),
Nilai = c(
jual_juni,
round(S[1] * 100, 2),
round(S[2] * 100, 2),
round(prediksi_juli_o1, 4),
round(prediksi_juli_o1)
)
)
knitr::kable(df_pred,
caption = "Tabel 7. Ringkasan Prediksi Penjualan HT70 (Orde Satu)",
align = "c")| Keterangan | Nilai |
|---|---|
| Penjualan Juni 2017 | 58.0000 |
| % Perolehan (P) | 54.5500 |
| % Kehilangan (K) | 45.4500 |
| Prediksi Juli 2017 (desimal) | 63.2727 |
| Prediksi Juli 2017 (dibulatkan) | 63.0000 |
##PLOT ORDE SATU: Data Historis + Prediksi + Pangsa Pasar
par(mfrow = c(1, 2), mar = c(5, 4, 4, 2))
# --- Plot 1: Time series + prediksi ---
plot(1:n, penjualan, type = "b", pch = 16, col = "steelblue",
xlab = "Bulan ke-", ylab = "Penjualan (pcs)",
main = "Penjualan HT70 (Z9) Jan 2015 – Jun 2017\n(Orde Satu)",
xaxt = "n", lwd = 2)
axis(1, at = c(1, 7, 13, 19, 25, 30),
labels = c("Jan-15","Jul-15","Jan-16","Jul-16","Jan-17","Jun-17"),
cex.axis = 0.7)
points(n + 1, round(prediksi_juli_o1), pch = 17, col = "red", cex = 1.5)
lines(c(n, n + 1), c(jual_juni, round(prediksi_juli_o1)),
lty = 2, col = "red", lwd = 2)
legend("topright",
legend = c("Historis", "Prediksi Jul-17"),
col = c("steelblue","red"), pch = c(16, 17),
lty = c(1, 2), cex = 0.8)
abline(h = mean(penjualan), lty = 3, col = "gray50")
text(2, mean(penjualan) + 2,
paste0("rata-rata = ", round(mean(penjualan), 1)),
col = "gray40", cex = 0.75)
# --- Plot 2: Pangsa Pasar ---
barplot(S * 100,
names.arg = c("Perolehan (P)", "Kehilangan (K)"),
col = c("steelblue", "tomato"),
ylab = "Probabilitas (%)",
main = "Pangsa Pasar HT70\n(Steady State Orde Satu)",
ylim = c(0, 100))
text(0.7, S[1] * 100 + 3, paste0(round(S[1]*100, 2), "%"), cex = 0.9)
text(1.9, S[2] * 100 + 3, paste0(round(S[2]*100, 2), "%"), cex = 0.9)ANALISIS RANTAI MARKOV ORDE DUA
##
##
## ========================================
## ANALISIS RANTAI MARKOV ORDE DUA
## ========================================
TAHAP 7 STATE GABUNGAN Y_t (ORDE DUA)
Y_t dibentuk dari pasangan (X(t-1), X(t)) PP=0, PK=1, KP=2, KK=3
Xt <- sv[2:length(sv)]
Xt_1 <- sv[1:(length(sv) - 1)]
Y_val <- ifelse(Xt == 0 & Xt_1 == 0, 0,
ifelse(Xt == 0 & Xt_1 == 1, 1,
ifelse(Xt == 1 & Xt_1 == 0, 2, 3)))
Y_label <- c("PP","PK","KP","KK")[Y_val + 1]
df_Y <- data.frame(
t = 2:length(sv),
Periode = periode[3:n],
Xt_1_val = Xt_1,
Xt_val = Xt,
Y_val = Y_val,
Y_label = Y_label
)
knitr::kable(df_Y,
col.names = c("t","Periode","X(t-1)","X(t)","Y_t","Label"),
caption = "Tabel 8. State Gabungan Y_t (Orde Dua)",
align = "c")| t | Periode | X(t-1) | X(t) | Y_t | Label |
|---|---|---|---|---|---|
| 2 | Mar-15 | 0 | 1 | 2 | KP |
| 3 | Apr-15 | 1 | 0 | 1 | PK |
| 4 | May-15 | 0 | 0 | 0 | PP |
| 5 | Jun-15 | 0 | 1 | 2 | KP |
| 6 | Jul-15 | 1 | 1 | 3 | KK |
| 7 | Aug-15 | 1 | 0 | 1 | PK |
| 8 | Sep-15 | 0 | 0 | 0 | PP |
| 9 | Oct-15 | 0 | 1 | 2 | KP |
| 10 | Nov-15 | 1 | 0 | 1 | PK |
| 11 | Dec-15 | 0 | 1 | 2 | KP |
| 12 | Jan-16 | 1 | 0 | 1 | PK |
| 13 | Feb-16 | 0 | 1 | 2 | KP |
| 14 | Mar-16 | 1 | 0 | 1 | PK |
| 15 | Apr-16 | 0 | 0 | 0 | PP |
| 16 | May-16 | 0 | 0 | 0 | PP |
| 17 | Jun-16 | 0 | 1 | 2 | KP |
| 18 | Jul-16 | 1 | 0 | 1 | PK |
| 19 | Aug-16 | 0 | 1 | 2 | KP |
| 20 | Sep-16 | 1 | 0 | 1 | PK |
| 21 | Oct-16 | 0 | 0 | 0 | PP |
| 22 | Nov-16 | 0 | 0 | 0 | PP |
| 23 | Dec-16 | 0 | 1 | 2 | KP |
| 24 | Jan-17 | 1 | 1 | 3 | KK |
| 25 | Feb-17 | 1 | 0 | 1 | PK |
| 26 | Mar-17 | 0 | 1 | 2 | KP |
| 27 | Apr-17 | 1 | 0 | 1 | PK |
| 28 | May-17 | 0 | 1 | 2 | KP |
| 29 | Jun-17 | 1 | 1 | 3 | KK |
TAHAP 8 MATRIKS FREKUENSI TRANSISI 4x4
frek <- matrix(0, nrow = 4, ncol = 4,
dimnames = list(
paste0("dari_", c("PP","PK","KP","KK")),
paste0("ke_", c("PP","PK","KP","KK"))
))
for (t in 1:(length(Y_val) - 1)) {
i <- Y_val[t] + 1
j <- Y_val[t + 1] + 1
frek[i, j] <- frek[i, j] + 1
}
knitr::kable(frek,
caption = "Tabel 9. Matriks Frekuensi Transisi f_ij (4×4)",
align = "c")| ke_PP | ke_PK | ke_KP | ke_KK | |
|---|---|---|---|---|
| dari_PP | 2 | 0 | 4 | 0 |
| dari_PK | 4 | 0 | 5 | 0 |
| dari_KP | 0 | 7 | 0 | 3 |
| dari_KK | 0 | 2 | 0 | 0 |
TAHAP 9 MATRIKS PROBABILITAS TRANSISI 4x4
row_sum <- rowSums(frek)
prob <- matrix(0, nrow = 4, ncol = 4,
dimnames = list(
c("PP","PK","KP","KK"),
c("PP","PK","KP","KK")
))
for (i in 1:4) {
if (row_sum[i] > 0) {
prob[i, ] <- frek[i, ] / row_sum[i]
}
}
knitr::kable(round(prob, 4),
caption = "Tabel 10. Matriks Probabilitas Transisi P (4×4)",
align = "c")| PP | PK | KP | KK | |
|---|---|---|---|---|
| PP | 0.3333 | 0.0 | 0.6667 | 0.0 |
| PK | 0.4444 | 0.0 | 0.5556 | 0.0 |
| KP | 0.0000 | 0.7 | 0.0000 | 0.3 |
| KK | 0.0000 | 1.0 | 0.0000 | 0.0 |
TAHAP 10 ITERASI STEADY STATE ORDE DUA
S2 <- c(0.25, 0.25, 0.25, 0.25)
history2 <- list()
for (iter in 1:max_iter) {
S2_new <- as.numeric(S2 %*% prob)
if (iter <= 5 || iter %% 500 == 0) {
history2[[length(history2) + 1]] <- c(iter = iter, round(S2_new, 6))
}
if (max(abs(S2_new - S2)) < tol) {
cat("Steady state orde dua tercapai pada iterasi ke:", iter, "\n")
S2 <- S2_new
break
}
S2 <- S2_new
}## Steady state orde dua tercapai pada iterasi ke: 22
df_hist2 <- as.data.frame(do.call(rbind, history2))
colnames(df_hist2) <- c("Iterasi", "S_PP", "S_PK", "S_KP", "S_KK")
knitr::kable(df_hist2,
caption = "Tabel 11. Riwayat Iterasi Menuju Steady State (Orde Dua)",
align = "c", row.names = FALSE)| Iterasi | S_PP | S_PK | S_KP | S_KK |
|---|---|---|---|---|
| 1 | 0.194444 | 0.425000 | 0.305556 | 0.075000 |
| 2 | 0.253704 | 0.288889 | 0.365741 | 0.091667 |
| 3 | 0.212963 | 0.347685 | 0.329630 | 0.109722 |
| 4 | 0.225514 | 0.340463 | 0.335134 | 0.098889 |
| 5 | 0.226488 | 0.333483 | 0.339489 | 0.100540 |
##
## HASIL STEADY STATE ORDE DUA
## P_0 (PP) = 0.2247
## P_1 (PK) = 0.3371
## P_2 (KP) = 0.3371
## P_3 (KK) = 0.1011
## Jumlah = 1.0000
df_ss2 <- data.frame(
State = c("0-PP","1-PK","2-KP","3-KK"),
Probabilitas = round(S2, 4)
)
knitr::kable(df_ss2,
caption = "Tabel 12. Probabilitas Steady State HT70 (Orde Dua)",
align = "c")| State | Probabilitas |
|---|---|
| 0-PP | 0.2247 |
| 1-PK | 0.3371 |
| 2-KP | 0.3371 |
| 3-KK | 0.1011 |
TAHAP 11 REDUKSI ORDE DUA ORDE SATU
Frekuensi stabil dari steady state × total transisi
total_trans <- sum(frek)
f_stabil <- round(S2 * total_trans)
names(f_stabil) <- c("PP","PK","KP","KK")
cat("Total transisi yang diamati:", total_trans, "\n\n")## Total transisi yang diamati: 27
df_fstab <- data.frame(
State = c("PP (f00)","PK (f01)","KP (f10)","KK (f11)"),
Frekuensi_Stabil = f_stabil
)
knitr::kable(df_fstab,
caption = "Tabel 13. Frekuensi di Kondisi Stabil (Orde Dua ke Orde Satu)",
align = "c", row.names = FALSE)| State | Frekuensi_Stabil |
|---|---|
| PP (f00) | 6 |
| PK (f01) | 9 |
| KP (f10) | 9 |
| KK (f11) | 3 |
TAHAP 12 MATRIKS PROBABILITAS TRANSISI BARU (2x2)
f00 <- f_stabil["PP"]; f01 <- f_stabil["PK"]
f10 <- f_stabil["KP"]; f11 <- f_stabil["KK"]
mat_baru <- matrix(
c(f00/(f00+f01), f01/(f00+f01),
f10/(f10+f11), f11/(f10+f11)),
nrow = 2, byrow = TRUE,
dimnames = list(c("P (Perolehan)","K (Kehilangan)"),
c("→ P","→ K"))
)
knitr::kable(round(mat_baru, 4),
caption = "Tabel 14. Matriks Probabilitas Transisi Baru (2×2)",
align = "c")| → P | → K | |
|---|---|---|
| P (Perolehan) | 0.40 | 0.60 |
| K (Kehilangan) | 0.75 | 0.25 |
TAHAP 13 STEADY STATE 2x2 PANGSA PASAR ORDE DUA
S3 <- c(0.5, 0.5)
for (iter in 1:max_iter) {
S3_new <- as.numeric(S3 %*% mat_baru)
if (max(abs(S3_new - S3)) < tol) break
S3 <- S3_new
}
cat("=== PANGSA PASAR HT70 (ORDE DUA) ===\n")## === PANGSA PASAR HT70 (ORDE DUA) ===
## Perolehan konsumen (P) : 0.5556 (55.56%)
## Kehilangan konsumen (K): 0.4444 (44.44%)
df_ms <- data.frame(
Kondisi = c("Perolehan (P)","Kehilangan (K)"),
Probabilitas = round(S3, 4),
Persentase = paste0(round(S3 * 100, 2), "%")
)
knitr::kable(df_ms,
caption = "Tabel 15. Pangsa Pasar HT70 (Steady State Orde Dua)",
align = "c")| Kondisi | Probabilitas | Persentase |
|---|---|---|
| Perolehan (P) | 0.5556 | 55.56% |
| Kehilangan (K) | 0.4444 | 44.44% |
TAHAP 14 PREDIKSI PENJUALAN JULI 2017 (ORDE DUA)
prediksi_juli_o2 <- jual_juni + (jual_juni * S3[1]) - (jual_juni * S3[2])
cat("=== PREDIKSI PENJUALAN JULI 2017 (ORDE DUA) ===\n")## === PREDIKSI PENJUALAN JULI 2017 (ORDE DUA) ===
## Penjualan Juni 2017 : 58 pcs
## % Perolehan : 55.56%
## % Kehilangan : 44.44%
cat(sprintf(" Prediksi Juli 2017 : %.2f pcs ≈ %d pcs\n",
prediksi_juli_o2, round(prediksi_juli_o2)))## Prediksi Juli 2017 : 64.44 pcs ≈ 64 pcs
df_pred2 <- data.frame(
Keterangan = c(
"Penjualan Juni 2017",
"% Perolehan (P)",
"% Kehilangan (K)",
"Prediksi Juli 2017 (desimal)",
"Prediksi Juli 2017 (dibulatkan)"
),
Nilai = c(
jual_juni,
round(S3[1] * 100, 2),
round(S3[2] * 100, 2),
round(prediksi_juli_o2, 4),
round(prediksi_juli_o2)
)
)
knitr::kable(df_pred2,
caption = "Tabel 16. Ringkasan Prediksi Penjualan HT70 (Orde Dua)",
align = "c")| Keterangan | Nilai |
|---|---|
| Penjualan Juni 2017 | 58.0000 |
| % Perolehan (P) | 55.5600 |
| % Kehilangan (K) | 44.4400 |
| Prediksi Juli 2017 (desimal) | 64.4444 |
| Prediksi Juli 2017 (dibulatkan) | 64.0000 |
TABEL PERBANDINGAN ORDE SATU vs ORDE DUA
df_compare <- data.frame(
Keterangan = c("% Perolehan (P)",
"% Kehilangan (K)",
"Prediksi Juli 2017 (pcs)"),
Orde_Satu = c(paste0(round(S[1]*100, 2), "%"),
paste0(round(S[2]*100, 2), "%"),
as.character(round(prediksi_juli_o1))),
Orde_Dua = c(paste0(round(S3[1]*100, 2), "%"),
paste0(round(S3[2]*100, 2), "%"),
as.character(round(prediksi_juli_o2)))
)
knitr::kable(df_compare,
caption = "Tabel 17. Perbandingan Hasil Orde Satu vs Orde Dua",
align = "c")| Keterangan | Orde_Satu | Orde_Dua |
|---|---|---|
| % Perolehan (P) | 54.55% | 55.56% |
| % Kehilangan (K) | 45.45% | 44.44% |
| Prediksi Juli 2017 (pcs) | 63 | 64 |
PLOT ORDE DUA: Data Historis + Prediksi + Pangsa Pasar
par(mfrow = c(1, 2), mar = c(5, 4, 4, 2))
# --- Plot 1: Time series + prediksi ---
plot(1:n, penjualan, type = "b", pch = 16, col = "steelblue",
xlab = "Bulan ke-", ylab = "Penjualan (pcs)",
main = "Penjualan HT70 (Z9) Jan 2015 – Jun 2017\n(Orde Dua)",
xaxt = "n", lwd = 2)
axis(1, at = c(1, 7, 13, 19, 25, 30),
labels = c("Jan-15","Jul-15","Jan-16","Jul-16","Jan-17","Jun-17"),
cex.axis = 0.7)
points(n + 1, round(prediksi_juli_o2), pch = 17, col = "darkgreen", cex = 1.5)
lines(c(n, n + 1), c(jual_juni, round(prediksi_juli_o2)),
lty = 2, col = "darkgreen", lwd = 2)
legend("topright",
legend = c("Historis", "Prediksi Jul-17"),
col = c("steelblue","darkgreen"), pch = c(16, 17),
lty = c(1, 2), cex = 0.8)
abline(h = mean(penjualan), lty = 3, col = "gray50")
text(2, mean(penjualan) + 2,
paste0("rata-rata = ", round(mean(penjualan), 1)),
col = "gray40", cex = 0.75)
# --- Plot 2: Pangsa Pasar ---
barplot(S3 * 100,
names.arg = c("Perolehan (P)", "Kehilangan (K)"),
col = c("steelblue", "tomato"),
ylab = "Probabilitas (%)",
main = "Pangsa Pasar HT70\n(Steady State Orde Dua)",
ylim = c(0, 100))
text(0.7, S3[1] * 100 + 3, paste0(round(S3[1]*100, 2), "%"), cex = 0.9)
text(1.9, S3[2] * 100 + 3, paste0(round(S3[2]*100, 2), "%"), cex = 0.9)DATA PENJUALAN NB170
Data penjualan bulanan DOVE SHP NOURISHING BLACK 170 ML (NB170) selama 30 periode dari Januari 2015 hingga Juni 2017 di Swalayan Pamella 1, Yogyakarta. Catatan: nilai Oktober 2015 (Oct-15) = 0.
# DATA PENJUALAN (NB170) Jan 2015 - Jun 2017
periode <- c(
"Jan-15","Feb-15","Mar-15","Apr-15","May-15","Jun-15",
"Jul-15","Aug-15","Sep-15","Oct-15","Nov-15","Dec-15",
"Jan-16","Feb-16","Mar-16","Apr-16","May-16","Jun-16",
"Jul-16","Aug-16","Sep-16","Oct-16","Nov-16","Dec-16",
"Jan-17","Feb-17","Mar-17","Apr-17","May-17","Jun-17"
)
penjualan <- c(
21, 22, 18, 17, 20, 24, # 2015 Jan-Jun
10, 30, 28, 28, 14, 21, # 2015 Jul-Dec
26, 31, 29, 36, 29, 22, # 2016 Jan-Jun
26, 17, 25, 20, 15, 23, # 2016 Jul-Dec
18, 16, 30, 17, 18, 12 # 2017 Jan-Jun
)
df <- data.frame(Periode = periode, Penjualan = penjualan)
knitr::kable(df, caption = "Tabel 1. Data Penjualan NB170", align = "c")| Periode | Penjualan |
|---|---|
| Jan-15 | 21 |
| Feb-15 | 22 |
| Mar-15 | 18 |
| Apr-15 | 17 |
| May-15 | 20 |
| Jun-15 | 24 |
| Jul-15 | 10 |
| Aug-15 | 30 |
| Sep-15 | 28 |
| Oct-15 | 28 |
| Nov-15 | 14 |
| Dec-15 | 21 |
| Jan-16 | 26 |
| Feb-16 | 31 |
| Mar-16 | 29 |
| Apr-16 | 36 |
| May-16 | 29 |
| Jun-16 | 22 |
| Jul-16 | 26 |
| Aug-16 | 17 |
| Sep-16 | 25 |
| Oct-16 | 20 |
| Nov-16 | 15 |
| Dec-16 | 23 |
| Jan-17 | 18 |
| Feb-17 | 16 |
| Mar-17 | 30 |
| Apr-17 | 17 |
| May-17 | 18 |
| Jun-17 | 12 |
Penentuan State (Perolehan / Kehilangan)
State \(X\_t\) ditentukan berdasarkan perubahan penjualan bulan ke-\(t\) dibanding bulan ke-\((t-1)\).
Jika penjualan mengalami peningkatan atau tetap, maka dikategorikan
sebagai Perolehan (P).
Sebaliknya, jika penjualan mengalami penurunan, maka dikategorikan
sebagai Kehilangan (K).
Secara matematis, state dapat dituliskan sebagai berikut:
\[ X\_t = \begin{cases} 0 \; (P = \text{Perolehan}), & \text{jika penjualan naik atau sama} \\ 1 \; (K = \text{Kehilangan}), & \text{jika penjualan turun} \end{cases} \]
Berdasarkan data penjualan yang digunakan, perubahan dari bulan ke bulan kemudian diklasifikasikan ke dalam dua state tersebut. Hasil klasifikasi ini akan digunakan untuk membentuk urutan state yang menjadi dasar dalam analisis rantai Markov orde dua.
# TENTUKAN STATE:
# P = 0 (naik atau sama), K = 1 (turun)
n <- length(penjualan)
# Bandingkan bulan t dengan t-1
state_label <- ifelse(penjualan[2:n] >= penjualan[1:(n-1)], "P", "K")
state_val <- ifelse(penjualan[2:n] >= penjualan[1:(n-1)], 0, 1)
# Data frame hasil
df_state <- data.frame(
Periode = periode[2:n],
Penjualan_t = penjualan[2:n],
Penjualan_t1= penjualan[1:(n-1)],
Perubahan = penjualan[2:n] - penjualan[1:(n-1)],
State = state_label,
Nilai = state_val
)
# Tampilkan tabel
knitr::kable(
df_state,
col.names = c("Periode","Jual (t)","Jual (t-1)","Δ","State","Nilai"),
caption = "Tabel 2. Penentuan State Perubahan Penjualan NB170",
align = "c"
)| Periode | Jual (t) | Jual (t-1) | Δ | State | Nilai |
|---|---|---|---|---|---|
| Feb-15 | 22 | 21 | 1 | P | 0 |
| Mar-15 | 18 | 22 | -4 | K | 1 |
| Apr-15 | 17 | 18 | -1 | K | 1 |
| May-15 | 20 | 17 | 3 | P | 0 |
| Jun-15 | 24 | 20 | 4 | P | 0 |
| Jul-15 | 10 | 24 | -14 | K | 1 |
| Aug-15 | 30 | 10 | 20 | P | 0 |
| Sep-15 | 28 | 30 | -2 | K | 1 |
| Oct-15 | 28 | 28 | 0 | P | 0 |
| Nov-15 | 14 | 28 | -14 | K | 1 |
| Dec-15 | 21 | 14 | 7 | P | 0 |
| Jan-16 | 26 | 21 | 5 | P | 0 |
| Feb-16 | 31 | 26 | 5 | P | 0 |
| Mar-16 | 29 | 31 | -2 | K | 1 |
| Apr-16 | 36 | 29 | 7 | P | 0 |
| May-16 | 29 | 36 | -7 | K | 1 |
| Jun-16 | 22 | 29 | -7 | K | 1 |
| Jul-16 | 26 | 22 | 4 | P | 0 |
| Aug-16 | 17 | 26 | -9 | K | 1 |
| Sep-16 | 25 | 17 | 8 | P | 0 |
| Oct-16 | 20 | 25 | -5 | K | 1 |
| Nov-16 | 15 | 20 | -5 | K | 1 |
| Dec-16 | 23 | 15 | 8 | P | 0 |
| Jan-17 | 18 | 23 | -5 | K | 1 |
| Feb-17 | 16 | 18 | -2 | K | 1 |
| Mar-17 | 30 | 16 | 14 | P | 0 |
| Apr-17 | 17 | 30 | -13 | K | 1 |
| May-17 | 18 | 17 | 1 | P | 0 |
| Jun-17 | 12 | 18 | -6 | K | 1 |
Rantai Markov Orde Dua
State Gabungan Y_t Orde
Untuk rantai Markov orde dua, state gabungan \(Y\_t\) dibentuk berdasarkan dua state sebelumnya, yaitu \((X\_t, X\_{t-1})\).
Karena setiap \(X\_t\) hanya memiliki dua kemungkinan, yaitu: - 0 = Perolehan (P) - 1 = Kehilangan (K)
Maka kombinasi state yang mungkin adalah sebagai berikut:
| \(Y\_t\) | \(X\_t\) | \(X\_{t-1}\) | Kode |
|---|---|---|---|
| 0 | 0 (P) | 0 (P) | PP |
| 1 | 0 (P) | 1 (K) | PK |
| 2 | 1 (K) | 0 (P) | KP |
| 3 | 1 (K) | 1 (K) | KK |
State gabungan ini digunakan untuk merepresentasikan dinamika perubahan penjualan berdasarkan dua periode sebelumnya, sehingga dapat menangkap pola yang lebih kompleks dibandingkan rantai Markov orde satu.
sv <- state_val # panjang = 29
# Ambil pasangan (X(t-1), X(t))
Xt_1 <- sv[1:(length(sv)-1)] # 1..28
Xt <- sv[2:length(sv)] # 2..29
# Bentuk state gabungan
Y_val <- ifelse(Xt == 0 & Xt_1 == 0, 0,
ifelse(Xt == 0 & Xt_1 == 1, 1,
ifelse(Xt == 1 & Xt_1 == 0, 2, 3)))
Y_label <- c("PP","PK","KP","KK")[Y_val + 1]
# Periode harus mulai dari bulan ke-3
df_Y <- data.frame(
t = 3:length(penjualan), # karena mulai dari pasangan ke-2
Periode = periode[3:length(penjualan)],
Xt_1_val = Xt_1,
Xt_val = Xt,
Y_val = Y_val,
Y_label = Y_label
)
# Tampilkan
knitr::kable(
df_Y,
col.names = c("t","Periode","X(t-1)","X(t)","Y_t","Label"),
caption = "Tabel 3. State Gabungan Y_t (Orde Dua) NB170",
align = "c"
)| t | Periode | X(t-1) | X(t) | Y_t | Label |
|---|---|---|---|---|---|
| 3 | Mar-15 | 0 | 1 | 2 | KP |
| 4 | Apr-15 | 1 | 1 | 3 | KK |
| 5 | May-15 | 1 | 0 | 1 | PK |
| 6 | Jun-15 | 0 | 0 | 0 | PP |
| 7 | Jul-15 | 0 | 1 | 2 | KP |
| 8 | Aug-15 | 1 | 0 | 1 | PK |
| 9 | Sep-15 | 0 | 1 | 2 | KP |
| 10 | Oct-15 | 1 | 0 | 1 | PK |
| 11 | Nov-15 | 0 | 1 | 2 | KP |
| 12 | Dec-15 | 1 | 0 | 1 | PK |
| 13 | Jan-16 | 0 | 0 | 0 | PP |
| 14 | Feb-16 | 0 | 0 | 0 | PP |
| 15 | Mar-16 | 0 | 1 | 2 | KP |
| 16 | Apr-16 | 1 | 0 | 1 | PK |
| 17 | May-16 | 0 | 1 | 2 | KP |
| 18 | Jun-16 | 1 | 1 | 3 | KK |
| 19 | Jul-16 | 1 | 0 | 1 | PK |
| 20 | Aug-16 | 0 | 1 | 2 | KP |
| 21 | Sep-16 | 1 | 0 | 1 | PK |
| 22 | Oct-16 | 0 | 1 | 2 | KP |
| 23 | Nov-16 | 1 | 1 | 3 | KK |
| 24 | Dec-16 | 1 | 0 | 1 | PK |
| 25 | Jan-17 | 0 | 1 | 2 | KP |
| 26 | Feb-17 | 1 | 1 | 3 | KK |
| 27 | Mar-17 | 1 | 0 | 1 | PK |
| 28 | Apr-17 | 0 | 1 | 2 | KP |
| 29 | May-17 | 1 | 0 | 1 | PK |
| 30 | Jun-17 | 0 | 1 | 2 | KP |
Matriks Frekuensi Transisi Orde Dua
Frekuensi transisi \(f\_{ij}\) dihitung berdasarkan banyaknya perpindahan dari state gabungan \(Y\_t\) ke \(Y\_{t+1}\).
Setiap elemen \(f\_{ij}\) menyatakan jumlah kejadian ketika sistem berada pada state \(Y\_t = i\) dan berpindah ke state \(Y\_{t+1} = j\) pada periode berikutnya.
Proses perhitungan dilakukan dengan mengamati urutan state gabungan yang telah diperoleh, kemudian menghitung jumlah transisi antar state secara berurutan.
Secara matematis, frekuensi transisi dapat dituliskan sebagai:
\[ f\_{ij} = \text{jumlah transisi dari } Y\_t = i \text{ ke } Y\_{t+1} = j \]
Hasil perhitungan frekuensi transisi ini kemudian disusun dalam bentuk matriks frekuensi transisi orde dua, yang akan digunakan untuk menentukan matriks probabilitas transisi.
# FREKUENSI TRANSISI f_ij (Y_t → Y_{t+1})
states_label <- c("PP","PK","KP","KK")
frek <- matrix(0, nrow = 4, ncol = 4,
dimnames = list(
paste0("dari_", states_label),
paste0("ke_", states_label)
))
# Hitung transisi
for (t in 1:(length(Y_val) - 1)) {
i <- Y_val[t] + 1 # baris
j <- Y_val[t+1] + 1 # kolom
frek[i, j] <- frek[i, j] + 1
}
# Tampilkan tabel
knitr::kable(
frek,
caption = "Tabel 4. Matriks Frekuensi Transisi f_ij (Orde Dua) NB170",
align = "c"
)| ke_PP | ke_PK | ke_KP | ke_KK | |
|---|---|---|---|---|
| dari_PP | 1 | 0 | 2 | 0 |
| dari_PK | 2 | 0 | 8 | 0 |
| dari_KP | 0 | 6 | 0 | 4 |
| dari_KK | 0 | 4 | 0 | 0 |
Matriks Probabilitas Transisi Orde Dua
Probabilitas transisi \(P\_{ij}\) diperoleh dari normalisasi matriks frekuensi transisi \(f\_{ij}\).
Setiap elemen \(P\_{ij}\) menyatakan peluang berpindah dari state gabungan \(Y\_t = i\) ke \(Y\_{t+1} = j\), yang dihitung dengan membagi frekuensi transisi terhadap total frekuensi dari state asal \(i\).
Secara matematis, probabilitas transisi dirumuskan sebagai:
\[ P\_{ij} = \frac{f\_{ij}}{\sum\_{j=0}^{3} f\_{ij}} \]
dengan: - \(f\_{ij}\) = frekuensi transisi dari state \(i\) ke state \(j\) - \(\sum\_{j=0}^{3} f\_{ij}\) = total transisi yang berasal dari state \(i\)
Berdasarkan data yang digunakan, setiap baris pada matriks probabilitas transisi merupakan hasil pembagian nilai frekuensi transisi dengan jumlah total transisi pada baris tersebut, sehingga jumlah setiap baris pada matriks \(P\_{ij}\) bernilai 1.
# MATRIKS PROBABILITAS TRANSISI P
states_label <- c("PP","PK","KP","KK")
row_sum <- rowSums(frek)
prob <- matrix(0, nrow = 4, ncol = 4,
dimnames = list(states_label, states_label))
# Hitung probabilitas
for (i in 1:4) {
if (row_sum[i] > 0) {
prob[i, ] <- frek[i, ] / row_sum[i]
}
}
# Tampilkan
knitr::kable(
round(prob, 4),
caption = "Tabel 5. Matriks Probabilitas Transisi P (Orde Dua) NB170",
align = "c"
)| PP | PK | KP | KK | |
|---|---|---|---|---|
| PP | 0.3333 | 0.0 | 0.6667 | 0.0 |
| PK | 0.2000 | 0.0 | 0.8000 | 0.0 |
| KP | 0.0000 | 0.6 | 0.0000 | 0.4 |
| KK | 0.0000 | 1.0 | 0.0000 | 0.0 |
Probabilitas Steady State
Probabilitas steady state diperoleh melalui proses iterasi menggunakan matriks probabilitas transisi \(P\).
Metode yang digunakan adalah sebagai berikut:
\[ S(t) = S(0) \cdot P \]
dengan: - \(S(t)\) = vektor probabilitas pada iterasi ke-\(t\) - \(S(0)\) = vektor probabilitas awal - \(P\) = matriks probabilitas transisi
Pada penelitian ini, digunakan vektor probabilitas awal yang bersifat seragam karena tidak ada informasi awal yang lebih spesifik, yaitu:
\[ S(0) = [0.25 \;\; 0.25 \;\; 0.25 \;\; 0.25] \]
yang merepresentasikan peluang awal untuk masing-masing state gabungan: - PP - PK - KP - KK
Proses iterasi dilakukan secara berulang hingga nilai \(S(t)\) konvergen (tidak mengalami perubahan signifikan). Nilai konvergen tersebut merupakan probabilitas steady state, yang menunjukkan distribusi jangka panjang dari masing-masing state dalam sistem.
# STEADY STATE S(t) = S(0) · P
states_label <- c("PP","PK","KP","KK")
# Distribusi awal (boleh bebas, biasanya merata)
S <- c(0.25, 0.25, 0.25, 0.25)
tol <- 1e-8
max_iter <- 10000
history <- list()
for (iter in 1:max_iter) {
S_new <- S %*% prob
S_new <- as.numeric(S_new)
# Simpan beberapa iterasi awal + interval
if (iter <= 5 || iter %% 500 == 0) {
history[[length(history) + 1]] <- c(iter, round(S_new, 6))
}
# Cek konvergensi
if (max(abs(S_new - S)) < tol) {
cat("Steady state tercapai pada iterasi ke:", iter, "\n")
S <- S_new
break
}
S <- S_new
}## Steady state tercapai pada iterasi ke: 31
# Data frame hasil iterasi
df_hist <- do.call(rbind, history)
df_hist <- as.data.frame(df_hist)
colnames(df_hist) <- c("Iterasi", "S_PP", "S_PK", "S_KP", "S_KK")
knitr::kable(
df_hist,
caption = "Tabel 6. Riwayat Iterasi Menuju Steady State NB170",
align = "c",
row.names = FALSE
)| Iterasi | S_PP | S_PK | S_KP | S_KK |
|---|---|---|---|---|
| 1 | 0.133333 | 0.400000 | 0.366667 | 0.100000 |
| 2 | 0.124444 | 0.320000 | 0.408889 | 0.146667 |
| 3 | 0.105481 | 0.392000 | 0.338963 | 0.163556 |
| 4 | 0.113560 | 0.366933 | 0.383921 | 0.135585 |
| 5 | 0.111240 | 0.365938 | 0.369254 | 0.153568 |
# Tampilkan steady state akhir
steady_state <- round(S, 6)
names(steady_state) <- states_label
steady_state## PP PK KP KK
## 0.111111 0.370370 0.370370 0.148148
## Probabilitas Steady State (NB170):
## P_0 (PP) = 0.1111
## P_1 (PK) = 0.3704
## P_2 (KP) = 0.3704
## P_3 (KK) = 0.1481
## Jumlah = 1.0000
# Tabel
df_ss <- data.frame(
State = c("0 - PP","1 - PK","2 - KP","3 - KK"),
Probabilitas = round(S, 4)
)
knitr::kable(
df_ss,
caption = "Tabel 7. Probabilitas Steady State NB170",
align = "c"
)| State | Probabilitas |
|---|---|
| 0 - PP | 0.1111 |
| 1 - PK | 0.3704 |
| 2 - KP | 0.3704 |
| 3 - KK | 0.1481 |
Matriks Probabilitas Transisi Baru (2×2)
Setelah diperoleh matriks frekuensi transisi pada state gabungan (PP, PK, KP, KK), analisis kemudian direduksi kembali ke dua kondisi utama, yaitu Perolehan (P) dan Kehilangan (K).
Reduksi ini dilakukan dengan mengelompokkan state gabungan berdasarkan state saat ini (\(X\_t\)), sehingga diperoleh matriks probabilitas transisi baru berukuran 2×2.
Secara matematis, matriks probabilitas transisi baru dirumuskan sebagai:
\[ P\_{\text{new}} = \begin{bmatrix} \frac{f\_{PP}}{f\_{PP} + f\_{PK}} & \frac{f\_{PK}}{f\_{PP} + f\_{PK}} \\ \frac{f\_{KP}}{f\_{KP} + f\_{KK}} & \frac{f\_{KK}}{f\_{KP} + f\_{KK}} \end{bmatrix} \]
dengan: - \(f\_{PP}\) = frekuensi
transisi dari P ke P
- \(f\_{PK}\) = frekuensi transisi dari
P ke K
- \(f\_{KP}\) = frekuensi transisi dari
K ke P
- \(f\_{KK}\) = frekuensi transisi dari
K ke K
Baris pertama menunjukkan probabilitas transisi dari kondisi Perolehan (P), sedangkan baris kedua menunjukkan probabilitas transisi dari kondisi Kehilangan (K).
Matriks ini memberikan gambaran yang lebih sederhana mengenai dinamika perpindahan antara kondisi perolehan dan kehilangan dalam sistem.
# Matriks Probabilitas Transisi Baru (2×2)
f00 <- f_stabil["PP"]; f01 <- f_stabil["PK"]
f10 <- f_stabil["KP"]; f11 <- f_stabil["KK"]
# Baris P
P00_new <- f00 / (f00 + f01)
P01_new <- f01 / (f00 + f01)
# Baris K (antisipasi pembagi nol)
if ((f10 + f11) == 0) {
P10_new <- 0
P11_new <- 0
} else {
P10_new <- f10 / (f10 + f11)
P11_new <- f11 / (f10 + f11)
}
# Matriks
mat_baru <- matrix(
c(P00_new, P01_new,
P10_new, P11_new),
nrow = 2, byrow = TRUE,
dimnames = list(c("P (Perolehan)","K (Kehilangan)"),
c("→ P","→ K"))
)
knitr::kable(
round(mat_baru, 4),
caption = "Tabel 9. Matriks Probabilitas Transisi Baru (2×2) NB170",
align = "c"
)| → P | → K | |
|---|---|---|
| P (Perolehan) | 0.2308 | 0.7692 |
| K (Kehilangan) | 0.7143 | 0.2857 |
Steady State 2×2 (Pangsa Pasar)
# Steady State 2×2 (Pangsa Pasar)
# Salin matriks baru
P2 <- mat_baru
# Perbaiki jika ada baris nol (tidak terdefinisi)
for (i in 1:nrow(P2)) {
if (sum(P2[i, ]) == 0) {
P2[i, ] <- c(0.5, 0.5) # asumsi netral
}
}
# Vektor awal
S2 <- c(0.5, 0.5)
# Iterasi
for (iter in 1:max_iter) {
S2_new <- as.numeric(S2 %*% P2)
if (max(abs(S2_new - S2)) < tol) break
S2 <- S2_new
}
# Output
cat("=== PANGSA PASAR NB170 ===\n")## === PANGSA PASAR NB170 ===
## Perolehan konsumen (P) : 0.4815 (48.15%)
## Kehilangan konsumen (K): 0.5185 (51.85%)
# Tabel
df_ms <- data.frame(
Kondisi = c("Perolehan (P)","Kehilangan (K)"),
Probabilitas = round(S2, 4),
Persentase = paste0(round(S2 * 100, 2), "%")
)
knitr::kable(
df_ms,
caption = "Tabel 10. Pangsa Pasar NB170 (Steady State 2×2)",
align = "c"
)| Kondisi | Probabilitas | Persentase |
|---|---|---|
| Perolehan (P) | 0.4815 | 48.15% |
| Kehilangan (K) | 0.5185 | 51.85% |
Prediksi Penjualan Juli 2017
Prediksi penjualan bulan Juli 2017 dilakukan berdasarkan nilai penjualan bulan sebelumnya (Juni 2017) serta proporsi kondisi Perolehan (P) dan Kehilangan (K) yang diperoleh dari hasil analisis.
Rumus yang digunakan adalah sebagai berikut:
\[ \text{Prediksi Juli} = \text{Jual Juni} + (\text{Jual Juni} \times \% \text{Perolehan}) - (\text{Jual Juni} \times \% \text{Kehilangan}) \]
dengan: - \(\text{Jual Juni}\) =
jumlah penjualan pada bulan Juni 2017
- \(\% \text{Perolehan}\) = proporsi
kejadian perolehan (P)
- \(\% \text{Kehilangan}\) = proporsi
kejadian kehilangan (K)
Persentase perolehan dan kehilangan diperoleh dari hasil reduksi probabilitas atau pangsa pasar pada kondisi stabil.
Nilai prediksi ini mencerminkan estimasi perubahan penjualan dengan mempertimbangkan kecenderungan peningkatan dan penurunan yang terjadi pada periode sebelumnya.
Sebagai hasil akhir, jika nilai persentase perolehan lebih besar dibandingkan kehilangan, maka penjualan diprediksi mengalami peningkatan. Sebaliknya, jika persentase kehilangan lebih besar, maka penjualan diprediksi menurun.
jual_juni <- penjualan[length(penjualan)] # Jun-17 = 12
pct_perolehan <- S2[1]
pct_kehilangan <- S2[2]
# Model prediksi
prediksi_juli <- jual_juni +
(jual_juni * pct_perolehan) -
(jual_juni * pct_kehilangan)
cat(sprintf(" Penjualan Juni 2017 : %d pcs\n", jual_juni))## Penjualan Juni 2017 : 12 pcs
## % Perolehan : 48.15%
## % Kehilangan : 51.85%
## Prediksi Juli 2017 : 11.56 pcs ≈ 12 pcs
# Tabel ringkasan
df_pred <- data.frame(
Keterangan = c(
"Penjualan Juni 2017",
"% Perolehan (P)",
"% Kehilangan (K)",
"Prediksi Juli 2017 (desimal)",
"Prediksi Juli 2017 (dibulatkan)"
),
Nilai = c(
jual_juni,
round(pct_perolehan * 100, 2),
round(pct_kehilangan * 100, 2),
round(prediksi_juli, 4),
round(prediksi_juli)
)
)
knitr::kable(
df_pred,
caption = "Tabel 11. Ringkasan Prediksi Penjualan NB170",
align = "c"
)| Keterangan | Nilai |
|---|---|
| Penjualan Juni 2017 | 12.0000 |
| % Perolehan (P) | 48.1500 |
| % Kehilangan (K) | 51.8500 |
| Prediksi Juli 2017 (desimal) | 11.5556 |
| Prediksi Juli 2017 (dibulatkan) | 12.0000 |
Visualisasi
# PLOT DATA HISTORIS + PREDIKSI
par(mfrow = c(1, 2), mar = c(5, 4, 4, 2))
# Plot 1: Data penjualan + prediksi
plot(1:n, penjualan, type = "b", pch = 16, col = "steelblue",
xlab = "Bulan ke-", ylab = "Penjualan (pcs)",
main = "Penjualan NB170 Jan 2015 – Jun 2017",
xaxt = "n", lwd = 2)
axis(1, at = c(1, 7, 13, 19, 25, 30),
labels = c("Jan-15","Jul-15","Jan-16","Jul-16","Jan-17","Jun-17"),
cex.axis = 0.7)
# Titik prediksi Juli 2017
points(n + 1, round(prediksi_juli), pch = 17, col = "red", cex = 1.5)
# Garis ke prediksi
lines(c(n, n + 1), c(jual_juni, round(prediksi_juli)),
lty = 2, col = "red", lwd = 2)
# Legend
legend("topright",
legend = c("Historis", "Prediksi Jul-17"),
col = c("steelblue","red"),
pch = c(16, 17), lty = c(1, 2), cex = 0.8)
# Garis rata-rata
abline(h = mean(penjualan), lty = 3, col = "gray50")
text(2, mean(penjualan) + 0.5,
paste0("rata-rata = ", round(mean(penjualan), 1)),
col = "gray40", cex = 0.75)
# Plot 2: Pangsa Pasar
barplot(S2 * 100,
names.arg = c("Perolehan (P)", "Kehilangan (K)"),
col = c("steelblue", "tomato"),
ylab = "Probabilitas (%)",
main = "Pangsa Pasar NB170\n(Steady State 2×2)",
ylim = c(0, 100))
text(0.7, S2[1] * 100 + 3,
paste0(round(S2[1]*100, 2), "%"), cex = 0.9)
text(1.9, S2[2] * 100 + 3,
paste0(round(S2[2]*100, 2), "%"), cex = 0.9)
## Ringkasan
## RINGKASAN ANALISIS RANTAI MARKOV ORDE DUA
## Produk: DOVE SHP NOURISHING BLACK 170 ML (NB170)
##
## 1. STEADY STATE ORDE DUA (4 state):
## PP: 0.1111 | PK: 0.3704 | KP: 0.3704 | KK: 0.1481
##
## 2. PANGSA PASAR (Steady State 2×2):
## Perolehan konsumen : 0.4815 (48.15%)
## Kehilangan konsumen: 0.5185 (51.85%)
# Tren pasar
trend <- ifelse(S2[1] > S2[2], "MENINGKAT ▲", "MENURUN ▼")
cat(sprintf("\n3. TREN PASAR : %s\n", trend))##
## 3. TREN PASAR : MENURUN ▼
## Penjualan Juni 2017 : 12 pcs
## Prediksi Juli 2017 : 12 pcs
cat(sprintf(" Perubahan prediksi : %.2f pcs (%s)\n",
abs(prediksi_juli - jual_juni),
ifelse(prediksi_juli >= jual_juni, "naik", "turun")))## Perubahan prediksi : 0.44 pcs (turun)
DATA PENJUALAN NOC320 (Z9)
Data penjualan bulanan DOVE SHP NOURISHING OIL CARE 320 ML dari Januari 2015 hingga Juni 2017 di Swalayan Pamella 1, Yogyakarta. Catatan: nilai Oktober 2015 (Oct-15) = 0.
# DATA PENJUALAN Z9 (NOC320) Jan 2015 - Jun 2017
periode <- c(
"Jan-15","Feb-15","Mar-15","Apr-15","May-15","Jun-15",
"Jul-15","Aug-15","Sep-15","Oct-15","Nov-15","Dec-15",
"Jan-16","Feb-16","Mar-16","Apr-16","May-16","Jun-16",
"Jul-16","Aug-16","Sep-16","Oct-16","Nov-16","Dec-16",
"Jan-17","Feb-17","Mar-17","Apr-17","May-17","Jun-17"
)
penjualan <- c(
10, 11, 2, 5, 8, 1, # 2015 Jan-Jun
7, 4, 2, 0, 1, 10, # 2015 Jul-Dec
16, 17, 15, 1, 9, 15, # 2016 Jan-Jun
17, 8, 16, 19, 19, 26, # 2016 Jul-Dec
12, 7, 19, 16, 15, 8 # 2017 Jan-Jun
)
df <- data.frame(Periode = periode, Penjualan = penjualan)
knitr::kable(df, caption = "Tabel 1. Data Penjualan NOC320 (Z9)", align = "c")| Periode | Penjualan |
|---|---|
| Jan-15 | 10 |
| Feb-15 | 11 |
| Mar-15 | 2 |
| Apr-15 | 5 |
| May-15 | 8 |
| Jun-15 | 1 |
| Jul-15 | 7 |
| Aug-15 | 4 |
| Sep-15 | 2 |
| Oct-15 | 0 |
| Nov-15 | 1 |
| Dec-15 | 10 |
| Jan-16 | 16 |
| Feb-16 | 17 |
| Mar-16 | 15 |
| Apr-16 | 1 |
| May-16 | 9 |
| Jun-16 | 15 |
| Jul-16 | 17 |
| Aug-16 | 8 |
| Sep-16 | 16 |
| Oct-16 | 19 |
| Nov-16 | 19 |
| Dec-16 | 26 |
| Jan-17 | 12 |
| Feb-17 | 7 |
| Mar-17 | 19 |
| Apr-17 | 16 |
| May-17 | 15 |
| Jun-17 | 8 |
Penentuan State (Perolehan / Kehilangan)
State \(X_t\) ditentukan berdasarkan perubahan penjualan bulan ke-\(t\) dibanding bulan ke-\((t-1)\):
\[X_t = \begin{cases} 0 \text{ (P = Perolehan)} & \text{jika penjualan naik atau sama} \\ 1 \text{ (K = Kehilangan)} & \text{jika penjualan turun} \end{cases}\]
# TENTUKAN STATE: P = 0 (naik/sama), K = 1 (turun)
n <- length(penjualan)
# State: bandingkan bulan t dengan bulan t-1
# Indeks 2..n → 29 nilai state
state_label <- ifelse(penjualan[2:n] >= penjualan[1:(n-1)], "P", "K")
state_val <- ifelse(penjualan[2:n] >= penjualan[1:(n-1)], 0, 1)
df_state <- data.frame(
Periode = periode[2:n],
Penjualan = penjualan[2:n],
Prev = penjualan[1:(n-1)],
Perubahan = penjualan[2:n] - penjualan[1:(n-1)],
State_Label= state_label,
State_Val = state_val
)
knitr::kable(df_state,
col.names = c("Periode","Jual (t)","Jual (t-1)","Δ","State","Nilai"),
caption = "Tabel 2. Penentuan State Perbulan",
align = "c")| Periode | Jual (t) | Jual (t-1) | Δ | State | Nilai |
|---|---|---|---|---|---|
| Feb-15 | 11 | 10 | 1 | P | 0 |
| Mar-15 | 2 | 11 | -9 | K | 1 |
| Apr-15 | 5 | 2 | 3 | P | 0 |
| May-15 | 8 | 5 | 3 | P | 0 |
| Jun-15 | 1 | 8 | -7 | K | 1 |
| Jul-15 | 7 | 1 | 6 | P | 0 |
| Aug-15 | 4 | 7 | -3 | K | 1 |
| Sep-15 | 2 | 4 | -2 | K | 1 |
| Oct-15 | 0 | 2 | -2 | K | 1 |
| Nov-15 | 1 | 0 | 1 | P | 0 |
| Dec-15 | 10 | 1 | 9 | P | 0 |
| Jan-16 | 16 | 10 | 6 | P | 0 |
| Feb-16 | 17 | 16 | 1 | P | 0 |
| Mar-16 | 15 | 17 | -2 | K | 1 |
| Apr-16 | 1 | 15 | -14 | K | 1 |
| May-16 | 9 | 1 | 8 | P | 0 |
| Jun-16 | 15 | 9 | 6 | P | 0 |
| Jul-16 | 17 | 15 | 2 | P | 0 |
| Aug-16 | 8 | 17 | -9 | K | 1 |
| Sep-16 | 16 | 8 | 8 | P | 0 |
| Oct-16 | 19 | 16 | 3 | P | 0 |
| Nov-16 | 19 | 19 | 0 | P | 0 |
| Dec-16 | 26 | 19 | 7 | P | 0 |
| Jan-17 | 12 | 26 | -14 | K | 1 |
| Feb-17 | 7 | 12 | -5 | K | 1 |
| Mar-17 | 19 | 7 | 12 | P | 0 |
| Apr-17 | 16 | 19 | -3 | K | 1 |
| May-17 | 15 | 16 | -1 | K | 1 |
| Jun-17 | 8 | 15 | -7 | K | 1 |
Rantai Markov Orde Dua
Definisi State Gabungan \(Y_t\)
Untuk orde kedua, state gabungan \(Y_t\) didefinisikan berdasarkan dua bulan berurutan \((X_t, X_{t-1})\):
| \(Y_t\) | \(X_t\) | \(X_{t-1}\) | Kode |
|---|---|---|---|
| 0 | 0 (P) | 0 (P) | PP |
| 1 | 0 (P) | 1 (K) | PK |
| 2 | 1 (K) | 0 (P) | KP |
| 3 | 1 (K) | 1 (K) | KK |
# STATE GABUNGAN Y_t (indeks 2..28 dari state_val → 28 pasang)
# state_val punya 29 elemen (indeks 1..29 di R)
# Y_t dibentuk dari pasangan (state_val[t], state_val[t-1])
sv <- state_val
Xt <- sv[2:length(sv)]
Xt_1 <- sv[1:(length(sv)-1)]
# Kode Y
Y_val <- ifelse(Xt == 0 & Xt_1 == 0, 0,
ifelse(Xt == 0 & Xt_1 == 1, 1,
ifelse(Xt == 1 & Xt_1 == 0, 2, 3)))
Y_label <- c("PP","PK","KP","KK")[Y_val + 1]
df_Y <- data.frame(
t = 2:(length(sv)),
Periode = periode[3:n],
Xt_1_val = Xt_1,
Xt_val = Xt,
Y_val = Y_val,
Y_label = Y_label
)
knitr::kable(df_Y,
col.names = c("t","Periode","X(t-1)","X(t)","Y_t","Label"),
caption = "Tabel 3. State Gabungan Y_t (Orde Dua)",
align = "c")| t | Periode | X(t-1) | X(t) | Y_t | Label |
|---|---|---|---|---|---|
| 2 | Mar-15 | 0 | 1 | 2 | KP |
| 3 | Apr-15 | 1 | 0 | 1 | PK |
| 4 | May-15 | 0 | 0 | 0 | PP |
| 5 | Jun-15 | 0 | 1 | 2 | KP |
| 6 | Jul-15 | 1 | 0 | 1 | PK |
| 7 | Aug-15 | 0 | 1 | 2 | KP |
| 8 | Sep-15 | 1 | 1 | 3 | KK |
| 9 | Oct-15 | 1 | 1 | 3 | KK |
| 10 | Nov-15 | 1 | 0 | 1 | PK |
| 11 | Dec-15 | 0 | 0 | 0 | PP |
| 12 | Jan-16 | 0 | 0 | 0 | PP |
| 13 | Feb-16 | 0 | 0 | 0 | PP |
| 14 | Mar-16 | 0 | 1 | 2 | KP |
| 15 | Apr-16 | 1 | 1 | 3 | KK |
| 16 | May-16 | 1 | 0 | 1 | PK |
| 17 | Jun-16 | 0 | 0 | 0 | PP |
| 18 | Jul-16 | 0 | 0 | 0 | PP |
| 19 | Aug-16 | 0 | 1 | 2 | KP |
| 20 | Sep-16 | 1 | 0 | 1 | PK |
| 21 | Oct-16 | 0 | 0 | 0 | PP |
| 22 | Nov-16 | 0 | 0 | 0 | PP |
| 23 | Dec-16 | 0 | 0 | 0 | PP |
| 24 | Jan-17 | 0 | 1 | 2 | KP |
| 25 | Feb-17 | 1 | 1 | 3 | KK |
| 26 | Mar-17 | 1 | 0 | 1 | PK |
| 27 | Apr-17 | 0 | 1 | 2 | KP |
| 28 | May-17 | 1 | 1 | 3 | KK |
| 29 | Jun-17 | 1 | 1 | 3 | KK |
Matriks Frekuensi Transisi Orde Dua
Hitung frekuensi transisi \(f_{ij}\) dari state \(Y_t\) ke \(Y_{t+1}\)
# HITUNG FREKUENSI TRANSISI f_ij (4x4)
# dari Y_t ke Y_{t+1}
states <- 0:3
frek <- matrix(0, nrow = 4, ncol = 4,
dimnames = list(
paste0("dari_", c("PP","PK","KP","KK")),
paste0("ke_", c("PP","PK","KP","KK"))
))
for (t in 1:(length(Y_val) - 1)) {
i <- Y_val[t] + 1 # baris (1-based)
j <- Y_val[t+1] + 1 # kolom (1-based)
frek[i, j] <- frek[i, j] + 1
}
knitr::kable(frek,
caption = "Tabel 4. Matriks Frekuensi Transisi f_ij (4×4)",
align = "c")| ke_PP | ke_PK | ke_KP | ke_KK | |
|---|---|---|---|---|
| dari_PP | 5 | 0 | 4 | 0 |
| dari_PK | 4 | 0 | 2 | 0 |
| dari_KP | 0 | 3 | 0 | 4 |
| dari_KK | 0 | 3 | 0 | 2 |
Matriks Probabilitas Transisi Orde Dua
\[P_{ij} = \frac{f_{ij}}{\sum_{j=0}^{3} f_{ij}}\]
# HITUNG MATRIKS PROBABILITAS TRANSISI P (4x4)
row_sum <- rowSums(frek)
prob <- matrix(0, nrow = 4, ncol = 4,
dimnames = list(
c("PP","PK","KP","KK"),
c("PP","PK","KP","KK")
))
for (i in 1:4) {
if (row_sum[i] > 0) {
prob[i, ] <- frek[i, ] / row_sum[i]
}
}
knitr::kable(round(prob, 4),
caption = "Tabel 5. Matriks Probabilitas Transisi P (4×4)",
align = "c")| PP | PK | KP | KK | |
|---|---|---|---|---|
| PP | 0.5556 | 0.0000 | 0.4444 | 0.0000 |
| PK | 0.6667 | 0.0000 | 0.3333 | 0.0000 |
| KP | 0.0000 | 0.4286 | 0.0000 | 0.5714 |
| KK | 0.0000 | 0.6000 | 0.0000 | 0.4000 |
Probabilitas Steady State
Iterasi \(S(t) = S(0) \cdot P\)
Mulai dari vektor probabilitas awal seragam: \[S(0) = [0.25 \quad 0.25 \quad 0.25 \quad 0.25]\]
# HITUNG STEADY STATE S(t) = S(0) · P (iteratif)
S <- c(0.25, 0.25, 0.25, 0.25)
tol <- 1e-8
max_iter <- 10000
history <- list()
for (iter in 1:max_iter) {
S_new <- S %*% prob # baris × matriks
S_new <- as.numeric(S_new)
# Simpan setiap 500 iterasi + awal
if (iter <= 5 || iter %% 500 == 0) {
history[[length(history) + 1]] <- c(iter = iter, round(S_new, 6))
}
if (max(abs(S_new - S)) < tol) {
cat("Steady state tercapai pada iterasi ke:", iter, "\n")
S <- S_new
break
}
S <- S_new
}## Steady state tercapai pada iterasi ke: 18
# Tampilkan riwayat iterasi
df_hist <- do.call(rbind, history)
df_hist <- as.data.frame(df_hist)
colnames(df_hist) <- c("Iterasi", "S_PP", "S_PK", "S_KP", "S_KK")
knitr::kable(df_hist,
caption = "Tabel 6. Riwayat Iterasi Menuju Steady State",
align = "c", row.names = FALSE)| Iterasi | S_PP | S_PK | S_KP | S_KK |
|---|---|---|---|---|
| 1 | 0.305556 | 0.257143 | 0.194444 | 0.242857 |
| 2 | 0.341182 | 0.229048 | 0.221517 | 0.208254 |
| 3 | 0.342244 | 0.219888 | 0.227985 | 0.209883 |
| 4 | 0.336728 | 0.223638 | 0.225404 | 0.214230 |
| 5 | 0.336163 | 0.225140 | 0.224203 | 0.214495 |
##
## HASIL STEADY STATE
## P_0 (PP) = 0.3369
## P_1 (PK) = 0.2246
## P_2 (KP) = 0.2246
## P_3 (KK) = 0.2139
## Jumlah = 1.0000
df_ss <- data.frame(
State = c("0-PP","1-PK","2-KP","3-KK"),
Probabilitas = round(S, 4)
)
knitr::kable(df_ss,
caption = "Tabel 7. Probabilitas Steady State NOC320",
align = "c")| State | Probabilitas |
|---|---|
| 0-PP | 0.3369 |
| 1-PK | 0.2246 |
| 2-KP | 0.2246 |
| 3-KK | 0.2139 |
Prediksi Penjualan Juli 2017
Menggunakan rumus prediksi:
\[\text{Prediksi}_{\text{Juli}} = \text{Jual}_{\text{Juni}} + (\text{Jual}_{\text{Juni}} \times \%\text{Perolehan}) - (\text{Jual}_{\text{Juni}} \times \%\text{Kehilangan})\]
# PREDIKSI PENJUALAN JULI 2017
jual_juni <- penjualan[length(penjualan)] # Jun-17
pct_perolehan <- S2[1]
pct_kehilangan <- S2[2]
prediksi_juli <- jual_juni + (jual_juni * pct_perolehan) - (jual_juni * pct_kehilangan)
cat("=== PREDIKSI PENJUALAN ===\n")## === PREDIKSI PENJUALAN ===
## Penjualan Juni 2017 : 8 pcs
## % Perolehan : 55.56%
## % Kehilangan : 44.44%
## Prediksi Juli 2017 : 8.89 pcs ≈ 9 pcs
df_pred <- data.frame(
Keterangan = c(
"Penjualan Juni 2017",
"% Perolehan (P)",
"% Kehilangan (K)",
"Prediksi Juli 2017 (desimal)",
"Prediksi Juli 2017 (dibulatkan)"
),
Nilai = c(
jual_juni,
round(pct_perolehan * 100, 2),
round(pct_kehilangan * 100, 2),
round(prediksi_juli, 4),
round(prediksi_juli)
)
)
knitr::kable(df_pred,
caption = "Tabel 11. Ringkasan Prediksi Penjualan NOC320",
align = "c")| Keterangan | Nilai |
|---|---|
| Penjualan Juni 2017 | 8.0000 |
| % Perolehan (P) | 55.5600 |
| % Kehilangan (K) | 44.4400 |
| Prediksi Juli 2017 (desimal) | 8.8889 |
| Prediksi Juli 2017 (dibulatkan) | 9.0000 |
Visualisasi
# PLOT DATA HISTORIS + PREDIKSI
par(mfrow = c(1, 2), mar = c(5, 4, 4, 2))
# --- Plot 1: Data penjualan + prediksi ---
plot(1:n, penjualan, type = "b", pch = 16, col = "steelblue",
xlab = "Bulan ke-", ylab = "Penjualan (pcs)",
main = "Penjualan NOC320 (Z9) Jan 2015 – Jun 2017",
xaxt = "n", lwd = 2)
axis(1, at = c(1, 7, 13, 19, 25, 30),
labels = c("Jan-15","Jul-15","Jan-16","Jul-16","Jan-17","Jun-17"), cex.axis = 0.7)
points(n + 1, round(prediksi_juli), pch = 17, col = "red", cex = 1.5)
lines(c(n, n + 1), c(jual_juni, round(prediksi_juli)),
lty = 2, col = "red", lwd = 2)
legend("topright",
legend = c("Historis", "Prediksi Jul-17"),
col = c("steelblue","red"), pch = c(16, 17), lty = c(1, 2), cex = 0.8)
abline(h = mean(penjualan), lty = 3, col = "gray50")
text(2, mean(penjualan) + 0.5, paste0("rata-rata = ", round(mean(penjualan), 1)),
col = "gray40", cex = 0.75)
# --- Plot 2: Pangsa Pasar ---
barplot(S2 * 100,
names.arg = c("Perolehan (P)", "Kehilangan (K)"),
col = c("steelblue", "tomato"),
ylab = "Probabilitas (%)",
main = "Pangsa Pasar NOC320\n(Steady State Orde 2)",
ylim = c(0, 100))
text(0.7, S2[1] * 100 + 3, paste0(round(S2[1]*100, 2), "%"), cex = 0.9)
text(1.9, S2[2] * 100 + 3, paste0(round(S2[2]*100, 2), "%"), cex = 0.9)Ringkasan Hasil
## RINGKASAN ANALISIS RANTAI MARKOV ORDE DUA
## Produk: DOVE SHP NOURISHING OIL CARE 320 ML (Z9)
## 1. STEADY STATE ORDE DUA (4 state):
## PP: 0.3369 | PK: 0.2246 | KP: 0.2246 | KK: 0.2139
##
## 2. PANGSA PASAR (Steady State 2×2):
## Perolehan konsumen : 0.5556 (55.56%)
## Kehilangan konsumen: 0.4444 (44.44%)
trend <- ifelse(S2[1] > S2[2], "MENINGKAT ▲", "MENURUN ▼")
cat(sprintf("\n3. TREN PASAR : %s\n", trend))##
## 3. TREN PASAR : MENINGKAT ▲
## Penjualan Juni 2017 : 8 pcs
## Prediksi Juli 2017 : 9 pcs
cat(sprintf(" Perubahan prediksi : %.2f pcs (%s)\n",
abs(prediksi_juli - jual_juni),
ifelse(prediksi_juli >= jual_juni, "naik", "turun")))## Perubahan prediksi : 0.89 pcs (naik)