library(dplyr)
##
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
##
## filter, lag
## The following objects are masked from 'package:base':
##
## intersect, setdiff, setequal, union
library(lubridate)
##
## Attaching package: 'lubridate'
## The following objects are masked from 'package:base':
##
## date, intersect, setdiff, union
library(plotly)
## Loading required package: ggplot2
##
## Attaching package: 'plotly'
## The following object is masked from 'package:ggplot2':
##
## last_plot
## The following object is masked from 'package:stats':
##
## filter
## The following object is masked from 'package:graphics':
##
## layout
library(ggplot2)
library(tidyr)
library(patchwork)
library(writexl)
library(shiny)
library(shinydashboard)
##
## Attaching package: 'shinydashboard'
## The following object is masked from 'package:graphics':
##
## box
library(readxl)
library(Matrix)
##
## Attaching package: 'Matrix'
## The following objects are masked from 'package:tidyr':
##
## expand, pack, unpack
library(scales)
library(readr)
##
## Attaching package: 'readr'
## The following object is masked from 'package:scales':
##
## col_factor
library(reshape2) # Untuk melt data (jika diperlukan)
##
## Attaching package: 'reshape2'
## The following object is masked from 'package:tidyr':
##
## smiths
setwd("~/KULIAH/5Semester5/MSP")
############## Proses Data ##############
kab_bogor_df <- read_excel("bmkgbogor.xlsx", sheet = 1)
str(kab_bogor_df)
## tibble [3,652 × 6] (S3: tbl_df/tbl/data.frame)
## $ Tanggal: POSIXct[1:3652], format: "2013-01-01" "2013-01-02" ...
## $ TA : num [1:3652] 21.3 21.6 21.4 20 20.8 20 20.4 20.3 19.3 21.8 ...
## $ RH : num [1:3652] 89 90 89 97 85 94 91 92 98 83 ...
## $ CH : num [1:3652] 10 47 0 0 26 42 8 9 71 66 ...
## $ S : num [1:3652] 1.4 5.2 2.2 2 0 3.4 0 0 0.5 0 ...
## $ FF : num [1:3652] 2.3 1.83 2.02 2.57 2.54 2.65 2.94 3.28 3.87 4.82 ...
#kab_bogor_df <- kab_bogor_df[,c(3,12,15,4,8,10)]
colnames(kab_bogor_df) <- c('tgl','suhu','rh','ch', 'lama', 'angin')
#View(kab_bogor_df)
#kab_bogor_df <- arrange(kab_bogor_df, tgl)
str(kab_bogor_df)
## tibble [3,652 × 6] (S3: tbl_df/tbl/data.frame)
## $ tgl : POSIXct[1:3652], format: "2013-01-01" "2013-01-02" ...
## $ suhu : num [1:3652] 21.3 21.6 21.4 20 20.8 20 20.4 20.3 19.3 21.8 ...
## $ rh : num [1:3652] 89 90 89 97 85 94 91 92 98 83 ...
## $ ch : num [1:3652] 10 47 0 0 26 42 8 9 71 66 ...
## $ lama : num [1:3652] 1.4 5.2 2.2 2 0 3.4 0 0 0.5 0 ...
## $ angin: num [1:3652] 2.3 1.83 2.02 2.57 2.54 2.65 2.94 3.28 3.87 4.82 ...
#Penyesuaian
kab_bogor_df$tgl <- as.Date(kab_bogor_df$tgl, format="%Y-%m-%d")
kab_bogor_df$lama <- as.numeric(kab_bogor_df$lama)
kab_bogor_df$ch <- as.numeric(kab_bogor_df$ch)
kab_bogor_df$suhu <- as.numeric(kab_bogor_df$suhu)
kab_bogor_df$rh <- as.numeric(kab_bogor_df$rh)
kab_bogor_df$angin <- as.numeric(kab_bogor_df$angin)
#Pengecekan Data Kosong
print(paste('Jumlah Data NA pada CH: ', round(sum(is.na(kab_bogor_df$ch))/length(kab_bogor_df$ch)*100,2), '%', sep = ''))
## [1] "Jumlah Data NA pada CH: 14.21%"
print(paste('Jumlah Data NA pada Suhu: ', round(sum(is.na(kab_bogor_df$suhu))/length(kab_bogor_df$suhu)*100,2), '%', sep = ''))
## [1] "Jumlah Data NA pada Suhu: 6.76%"
print(paste('Jumlah Data NA pada RH: ', round(sum(is.na(kab_bogor_df$rh))/length(kab_bogor_df$rh)*100,2), '%', sep = ''))
## [1] "Jumlah Data NA pada RH: 7.34%"
print(paste('Jumlah Data NA pada Angin: ', round(sum(is.na(kab_bogor_df$angin))/length(kab_bogor_df$angin)*100,2), '%', sep = ''))
## [1] "Jumlah Data NA pada Angin: 0%"
print(paste('Jumlah Data NA pada Lama Penyinaran: ', round(sum(is.na(kab_bogor_df$lama))/length(kab_bogor_df$lama)*100,2), '%', sep = ''))
## [1] "Jumlah Data NA pada Lama Penyinaran: 7.5%"
summary(kab_bogor_df)
## tgl suhu rh ch
## Min. :2013-01-01 Min. :18.3 Min. : 3.2 Min. : 0.00
## 1st Qu.:2015-07-02 1st Qu.:21.0 1st Qu.: 82.0 1st Qu.: 0.00
## Median :2017-12-31 Median :21.5 Median : 87.0 Median : 2.80
## Mean :2017-12-31 Mean :21.5 Mean : 86.0 Mean : 10.69
## 3rd Qu.:2020-06-30 3rd Qu.:22.0 3rd Qu.: 90.0 3rd Qu.: 14.40
## Max. :2022-12-30 Max. :57.5 Max. :100.0 Max. :192.80
## NA's :247 NA's :268 NA's :519
## lama angin
## Min. : 0.000 Min. :0.450
## 1st Qu.: 1.500 1st Qu.:1.050
## Median : 3.600 Median :1.290
## Mean : 3.744 Mean :1.412
## 3rd Qu.: 5.700 3rd Qu.:1.650
## Max. :10.000 Max. :4.820
## NA's :274
############## Modifikasi Parameter Cuaca ##############
#Pengisian Data Kosong
kab_bogor_df$ch[is.na(kab_bogor_df$ch)] <- 0
kab_bogor_df$lama[is.na(kab_bogor_df$lama)] <- mean(kab_bogor_df$lama, na.rm = TRUE)
kab_bogor_df$lama <- kab_bogor_df$lama + 0.25
kab_bogor_df$suhu[is.na(kab_bogor_df$suhu)] <- mean(kab_bogor_df$suhu, na.rm = TRUE)
##kab_bogor_df$suhu <- kab_bogor_df$suhu + 2
kab_bogor_df$rh[is.na(kab_bogor_df$rh)] <- mean(kab_bogor_df$rh, na.rm = TRUE)
##kab_bogor_df$rh <- kab_bogor_df$rh - 20
##kab_bogor_df$rh <- pmin(kab_bogor_df$rh + 10, 100)
kab_bogor_df$angin[is.na(kab_bogor_df$angin)] <- mean(kab_bogor_df$angin, na.rm = TRUE)
summary(kab_bogor_df)
## tgl suhu rh ch
## Min. :2013-01-01 Min. :18.3 Min. : 3.2 Min. : 0.000
## 1st Qu.:2015-07-02 1st Qu.:21.0 1st Qu.: 83.0 1st Qu.: 0.000
## Median :2017-12-31 Median :21.5 Median : 86.0 Median : 1.250
## Mean :2017-12-31 Mean :21.5 Mean : 86.0 Mean : 9.167
## 3rd Qu.:2020-06-30 3rd Qu.:22.0 3rd Qu.: 90.0 3rd Qu.: 11.400
## Max. :2022-12-30 Max. :57.5 Max. :100.0 Max. :192.800
## lama angin
## Min. : 0.250 Min. :0.450
## 1st Qu.: 1.950 1st Qu.:1.050
## Median : 3.994 Median :1.290
## Mean : 3.994 Mean :1.412
## 3rd Qu.: 5.775 3rd Qu.:1.650
## Max. :10.250 Max. :4.820
sum(is.na(kab_bogor_df))
## [1] 0
############## CODING BARU ##############
##Masukkan parameter tanaman dan pendefinisian
k = as.numeric(0.4)
sla = as.numeric(0.07)
rue = as.numeric(1.3) #10.13031/trans.12144
div = as.numeric(1000000)
tb = as.numeric(15.5)
tgl_tanam = as.Date("2018-01-01", format="%Y-%m-%d")
print(tgl_tanam)
## [1] "2018-01-01"
# TU
suhutu1<- kab_bogor_df$suhu[1:5]
suhutu2<- kab_bogor_df$suhu[6:47]
suhutu3<- kab_bogor_df$suhu[48:82]
tu1 = abs(10*(tb-mean(suhutu1)))
tu2 = abs(42*(tb-mean(suhutu2)))
tu3 = abs(35*(tb-mean(suhutu3)))
tu = tu1+tu2+tu3
waktu_tanam = 87#Durasi tanam tanaman yang dipilih
#Julian date
kab_bogor_df$jd1970 <- julian(kab_bogor_df$tgl)
# ==============================================================================
# 1. PERSIAPAN DATA INPUT (Pastikan kab_bogor_df sudah bersih)
# ==============================================================================
# (Asumsi: kab_bogor_df sudah ada di environment kamu dari script sebelumnya)
# Pastikan kolom Julian Date ada
kab_bogor_df$jd1970 <- julian(kab_bogor_df$tgl)
# Tentukan Tanggal Tanam: Mulai 1 Jan 2018, interval 87 hari
tgl_mulai <- as.Date("2018-01-01")
tgl_akhir <- as.Date("2018-12-31")
list_tgl_tanam <- seq(from = tgl_mulai, to = tgl_akhir, by = 87)
# --- BAGIAN PENTING: FILTERING ---
# Kita hanya ambil tanggal tanam jika (Tanggal Tanam + 87 hari) MASIH di 2018.
# Jika panennya lompat ke 2016, tanggal tanam itu kita buang.
list_tgl_tanam <- list_tgl_tanam[ (list_tgl_tanam + 87) <= tgl_akhir]
print("Jadwal Tanam Final (Panen selesai di 2018):")
## [1] "Jadwal Tanam Final (Panen selesai di 2018):"
print(list_tgl_tanam)
## [1] "2018-01-01" "2018-03-29" "2018-06-24" "2018-09-19"
# ==============================================================================
# 2. UPDATE FUNGSI SIMULASI (Output Kolom Lengkap)
# ==============================================================================
run_simulation_full <- function(start_date, weather_db) {
# A. Filter Data Cuaca
data_slice <- weather_db %>%
filter(tgl >= start_date) %>%
slice(1:120)
if(nrow(data_slice) < 90) return(NULL)
# B. Parameter Tanaman
k = 0.4; sla = 0.07; rue = 1.3; div = 1000000; tb = 15.5
# C. Hitung Thermal Unit (TU)
suhu_slice <- data_slice$suhu
suhutu1 <- suhu_slice[1:5]
suhutu2 <- suhu_slice[6:47]
suhutu3 <- suhu_slice[48:82]
tu1 = abs(10*(tb-mean(suhutu1, na.rm=TRUE)))
tu2 = abs(42*(tb-mean(suhutu2, na.rm=TRUE)))
tu3 = abs(35*(tb-mean(suhutu3, na.rm=TRUE)))
tu = tu1+tu2+tu3
# D. Durasi Tanam
fase = rep(NA, nrow(data_slice))
s = rep(0, nrow(data_slice))
ds = rep(0, nrow(data_slice))
waktu_tanam_curr = 87
for (i in 2:nrow(data_slice)) {
if (s[i-1] >= 0.57) {
ds[i] = 0.43 * (suhu_slice[i] - tb) / tu3; fase[i] = 3
} else if (s[i-1] >= 0.06) {
ds[i] = 0.51 * (suhu_slice[i] - tb) / tu2; fase[i] = 2
} else {
ds[i] = 0.06 * (suhu_slice[i] - tb) / tu1; fase[i] = 1
}
s[i] = s[i-1] + ds[i]
if (s[i] >= 1) { waktu_tanam_curr = i; break }
}
# Potong Data
n_rows <- waktu_tanam_curr
data_crop <- data_slice[1:n_rows, ]
# E. Inisialisasi Variabel Lengkap
tggl <- data_crop$tgl; ch_vec <- data_crop$ch
lama_vec <- data_crop$lama; suhu_vec <- data_crop$suhu
rh_vec <- data_crop$rh; angin_vec <- data_crop$angin
lai <- numeric(n_rows); lai[1] <- 1.14
dW <- numeric(n_rows); Qint <- numeric(n_rows); Q10 <- numeric(n_rows)
wdaun <- numeric(n_rows); wbatang <- numeric(n_rows)
wakar <- numeric(n_rows); wbuah <- numeric(n_rows); wtot <- numeric(n_rows)
# Biomassa flow vars
dWdaun <- numeric(n_rows); dWbatang <- numeric(n_rows)
dWakar <- numeric(n_rows); dWbuah <- numeric(n_rows)
rdaun <- numeric(n_rows); rbatang <- numeric(n_rows)
rakar <- numeric(n_rows); rbuah <- numeric(n_rows)
# Neraca Air vars
KAT <- numeric(n_rows); KAT[1] <- 0.35
ic <- numeric(n_rows); ch_ef <- numeric(n_rows); ro <- numeric(n_rows)
ETo <- numeric(n_rows); ETc <- numeric(n_rows); wdf <- numeric(n_rows)
Rs <- numeric(n_rows); ta <- numeric(n_rows); tm <- numeric(n_rows)
em <- numeric(n_rows)
# Constants
KL <- 0.35; TLP <- 0.2832; miring <- 0.8; atm <- 100.299
Lat <- -6.479679; Alt <- 920; KATawal_val <- 0.35
# F. Loop Perhitungan Harian
for (i in 2:n_rows) {
# --- Radiasi & Penman ---
dr = 1 + 0.003 * cos((2 * pi * Lat / 365))
delta = 0.409 * sin((2 * pi * Lat / 365) - 1.39)
phi = Lat * (pi / 180)
omega_s = acos(-tan(phi) * tan((delta)))
Ra = ((cos(phi) * cos(delta) * sin((omega_s)) + (omega_s * sin(phi) * sin(delta))) * ((24 * 60 / pi) * 0.082 * dr))
N = (24 / pi) * omega_s
Rs[i] = (0.25 + 0.5 * (lama_vec[i] / N)) * Ra
Rs0 = (0.75 + 2 * 10 ^ -5 * Alt) * Ra
Rns = (1 - 0.23) * Rs[i]
es = 0.6108 * exp(17.27 * suhu_vec[i] / (suhu_vec[i] + 237.3))
ea_val = rh_vec[i] * es / 100
vpd = es - ea_val
delta_vap = 4098 * (0.6108 * exp((17.27 * suhu_vec[i]) / (suhu_vec[i] + 237.3)) / (suhu_vec[i] + 237.3) ^ 2)
gamma = 0.665 * 10 ^ (-3) * atm
Rn = Rns - (4.903e-9 * (suhu_vec[i] + 273.16)^4 * (0.34 - 0.14 * sqrt(ea_val)) * (1.35 * (Rs[i] / Rs0) - 0.35))
u2 = angin_vec[i]
ETo[i] = (0.408 * delta_vap * (Rn) + gamma * (900 / (suhu_vec[i] + 273)) * u2 * vpd) / (delta_vap + gamma * (1 + 0.34 * u2))
# --- Biomassa ---
Qint[i] = Rs[i] * (1 - exp(-k * lai[i-1]))
dW[i] = rue * Qint[i]
Q10[i] = 2 ^ ((suhu_vec[i] - 20) / 10)
km = 0.03
rdaun[i] = km * wdaun[i-1] * Q10[i]; rbatang[i] = km * wbatang[i-1] * Q10[i]
rakar[i] = km * wakar[i-1] * Q10[i]; rbuah[i] = km * wbuah[i-1] * Q10[i]
b_total = 905.78 + 422.35 + 148.52
if (s[i] < 0.33) {
dWdaun[i] = dW[i] * (905.78/b_total) - rdaun[i]
dWbatang[i] = dW[i] * (422.35/b_total) - rbatang[i]
dWakar[i] = dW[i] * (148.52/b_total) - rakar[i]
wdaun[i] = wdaun[i-1] + dWdaun[i]; wbatang[i] = wbatang[i-1] + dWbatang[i]
wakar[i] = wakar[i-1] + dWakar[i]; wbuah[i] = 0
} else {
dWbuah[i] = dW[i] - rbuah[i]
wbuah[i] = wbuah[i-1] + dWbuah[i]
wdaun[i] = wdaun[i-1] - rdaun[i]; wbatang[i] = wbatang[i-1] - rbatang[i]; wakar[i] = wakar[i-1] - rakar[i]
}
lai[i] = sla * wdaun[i]
wtot[i] = wdaun[i] + wakar[i] + wbatang[i] + wbuah[i]
# --- Neraca Air ---
kc_val <- if(i > 60) 1.15 else (0.5 + (i/60)*(1.15-0.5))
ETc[i] = ETo[i] * kc_val
ic[i] = if(lai[i] <= 3) 0.35 * lai[i] else 1.27
if(ch_vec[i] < ic[i]) ic[i] = ch_vec[i]
ch_ef[i] = ch_vec[i] - ic[i]
ro[i] = (sin(pi * miring / 180) * ch_ef[i] * (KAT[i-1] / KL)) / (lai[i] + 1)
em[i] = ETo[i] * exp(-k * lai[i]); if(em[i] < 0) em[i] = 0
tm[i] = ETo[i] - em[i]
if (KAT[i-1] < KL) {
ta[i] = if(KAT[i-1] > TLP) tm[i] * (KAT[i-1] - TLP) / (KL - TLP) else 0
} else { ta[i] = tm[i] }
delta_kat = (ch_ef[i] - ro[i] - ta[i]) / 1000
prev_kat = if(i==2) KATawal_val else KAT[i-1]
KAT[i] = prev_kat + delta_kat
if (KAT[i] > KL) KAT[i] = KL
if (KAT[i] < 0) KAT[i] = 0
wdf[i] = ta[i] / tm[i]
if (is.na(wdf[i]) || wdf[i] < 0.35) wdf[i] = 0.35
}
# G. PACKING HASIL (STRUKTUR DIPERBAIKI DISINI)
# Menyesuaikan nama kolom agar mirip hh_2013_2022
res_df <- data.frame(
Tanggal = tggl,
HST = 1:n_rows,
# --- DATA CUACA & INPUT ---
Suhu = suhu_vec,
CH = ch_vec,
Lama_Penyinaran = lama_vec, # Menggunakan underscore biar aman
Radiasi = Rs,
RH = rh_vec,
Angin = angin_vec,
# --- DATA BIOMASSA ---
LAI = lai,
Daun = wdaun,
Batang = wbatang,
Akar = wakar,
Buah = wbuah,
Total = wtot,
Fase = as.factor(fase[1:n_rows]),
# --- DATA NERACA AIR ---
Intersepsi = ic,
Run_off = ro,
ETo = ETo,
ETc = ETc,
em = em, # Evaporasi
tm = tm, # Transpirasi
wdf = wdf,
KAT = KAT,
Curah_hujan_efektif = ch_ef,
# --- PRODUKTIVITAS & IDENTITAS ---
Prod1HAinTon = (wbuah * 50000) / div,
Tanggal_Tanam_Siklus = rep(start_date, n_rows),
Siklus_Ke = NA
)
return(res_df)
}
# ==============================================================================
# 3. GENERATE ULANG DATA FRAME df_simulasi_2018
# ==============================================================================
all_results <- list()
for (k in seq_along(list_tgl_tanam)) {
tgl <- list_tgl_tanam[k]
hasil <- run_simulation_full(tgl, kab_bogor_df)
if (!is.null(hasil)) {
hasil$Siklus_Ke <- k
all_results[[k]] <- hasil
}
}
df_simulasi_2018 <- bind_rows(all_results)
# ==============================================================================
# 4. CEK HASIL & VISUALISASI
# ==============================================================================
print("Struktur Data Frame Baru:")
## [1] "Struktur Data Frame Baru:"
str(df_simulasi_2018)
## 'data.frame': 354 obs. of 27 variables:
## $ Tanggal : Date, format: "2018-01-01" "2018-01-02" ...
## $ HST : int 1 2 3 4 5 6 7 8 9 10 ...
## $ Suhu : num 21.5 21.8 21 22.2 22 21.7 22.1 22 21 21.7 ...
## $ CH : num 0 0.6 0.5 20 0.4 21.2 0 5.3 22.2 7.2 ...
## $ Lama_Penyinaran : num 7.45 7.05 0.25 2.25 3.15 0.25 4.95 0.25 5.65 2.25 ...
## $ Radiasi : num 0 19.83 9.64 12.64 13.99 ...
## $ RH : num 80 81 88 87 80 82 79 83 88 84 ...
## $ Angin : num 1.5 1.79 1.25 1.95 2.45 2.35 2.09 1.84 1.27 1.95 ...
## $ LAI : num 1.14 0.405 0.473 0.578 0.719 ...
## $ Daun : num 0 5.79 6.76 8.26 10.27 ...
## $ Batang : num 0 2.7 3.15 3.85 4.79 ...
## $ Akar : num 0 0.949 1.108 1.354 1.685 ...
## $ Buah : num 0 0 0 0 0 0 0 0 0 0 ...
## $ Total : num 0 9.44 11.01 13.46 16.75 ...
## $ Fase : Factor w/ 3 levels "1","2","3": NA 1 1 1 1 1 1 1 1 1 ...
## $ Intersepsi : num 0 0.142 0.165 0.202 0.252 ...
## $ Run_off : num 0 0.00455 0.00317 0.17512 0.0012 ...
## $ ETo : num 0 3.67 2.04 2.58 3.06 ...
## $ ETc : num 0 1.92 1.09 1.4 1.7 ...
## $ em : num 0 3.12 1.69 2.05 2.3 ...
## $ tm : num 0 0.55 0.352 0.533 0.765 ...
## $ wdf : num 0 1 0.999 0.998 1 ...
## $ KAT : num 0.35 0.35 0.35 0.35 0.349 ...
## $ Curah_hujan_efektif : num 0 0.458 0.335 19.798 0.148 ...
## $ Prod1HAinTon : num 0 0 0 0 0 0 0 0 0 0 ...
## $ Tanggal_Tanam_Siklus: Date, format: "2018-01-01" "2018-01-01" ...
## $ Siklus_Ke : int 1 1 1 1 1 1 1 1 1 1 ...
# Visualisasi Perbandingan Produktivitas Antar Siklus Tanam
plot_multi_cycle <- ggplot(df_simulasi_2018, aes(x = HST, y = Prod1HAinTon, color = factor(Tanggal_Tanam_Siklus))) +
geom_line(size = 1) +
labs(
title = "Simulasi Pertumbuhan Timun: 4 Siklus Tanam di 2018",
subtitle = "Interval Tanam setiap ~87 Hari",
x = "Hari Setelah Tanam (HST)",
y = "Produktivitas (Ton/Ha)",
color = "Tanggal Tanam"
) +
theme_classic()
## Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
## ℹ Please use `linewidth` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
print(plot_multi_cycle)
ggplotly(plot_multi_cycle)
#ggplot ver
ggplot(df_simulasi_2018, aes(x = HST, y = Prod1HAinTon, color = factor(Tanggal_Tanam_Siklus))) +
geom_line(size = 1) +
labs(
title = "Simulasi Pertumbuhan Timun: 4 Siklus Tanam di 2018",
x = "Hari Setelah Tanam (HST)",
y = "Produktivitas (Ton/Ha)",
color = "Tanggal Tanam"
) +
scale_y_continuous(limits = c(0, NA)) +
scale_x_continuous(limits = c(0, 90)) +
theme_classic()+
theme_classic(base_size = 14)+
theme(text = element_text(family = "serif"))+
theme(plot.title = element_text(face = "bold"))
# ==============================================================================
# 1. MENENTUKAN STATUS MUSIM PER SIKLUS (DATA DRIVEN)
# ==============================================================================
# Hitung ringkasan per siklus tanam
summary_siklus <- df_simulasi_2018 %>%
group_by(Siklus_Ke, Tanggal_Tanam_Siklus) %>%
summarise(
Total_CH_Siklus = sum(CH, na.rm = TRUE), # Total air yang diterima tanaman
Rata_Suhu_Siklus = mean(Suhu, na.rm = TRUE), # Rata-rata suhu
Panen_Akhir = max(Prod1HAinTon, na.rm = TRUE), # Hasil panen final (akumulasi maks)
.groups = "drop"
)
# Tentukan Threshold (Ambang Batas) berdasarkan rata-rata tahun itu
threshold_lokal <- mean(summary_siklus$Total_CH_Siklus)
print(paste("Rata-rata Curah Hujan per Siklus Tanam (87 hari):", round(threshold_lokal, 2), "mm"))
## [1] "Rata-rata Curah Hujan per Siklus Tanam (87 hari): 731.65 mm"
# Beri Label Musim
summary_siklus <- summary_siklus %>%
mutate(Status_Musim = ifelse(Total_CH_Siklus >= threshold_lokal,
"Musim Basah",
"Musim Kering"))
print("Klasifikasi Musim per Siklus:")
## [1] "Klasifikasi Musim per Siklus:"
print(summary_siklus %>% select(Siklus_Ke, Tanggal_Tanam_Siklus, Total_CH_Siklus, Status_Musim))
## # A tibble: 4 × 4
## Siklus_Ke Tanggal_Tanam_Siklus Total_CH_Siklus Status_Musim
## <int> <date> <dbl> <chr>
## 1 1 2018-01-01 1435. Musim Basah
## 2 2 2018-03-29 526. Musim Kering
## 3 3 2018-06-24 233. Musim Kering
## 4 4 2018-09-19 732. Musim Kering
# ==============================================================================
# 2. VISUALISASI PERBANDINGAN PRODUKTIVITAS
# ==============================================================================
# Gabungkan status musim kembali ke data utama (Opsional, jika ingin mewarnai grafik harian)
# Tapi untuk membandingkan produksi, kita cukup pakai data summary_siklus
# A. Bar Chart: Curah Hujan per Siklus (Untuk Validasi Musim)
p_ch <- ggplot(summary_siklus, aes(x = factor(Tanggal_Tanam_Siklus), y = Total_CH_Siklus, fill = Status_Musim)) +
geom_col() +
geom_hline(yintercept = threshold_lokal, linetype = "dashed", color = "red") +
annotate("text", x = 1, y = threshold_lokal + 50, label = "Threshold Rata-rata", color = "red", hjust=0) +
scale_fill_manual(values = c("Musim Basah" = "#0072B2", "Musim Kering" = "#E69F00")) +
labs(title = "Validasi: Total Hujan per Siklus Tanam",
y = "Total Curah Hujan (mm/87 hari)", x = "Tanggal Mulai Tanam") +
theme_classic()
# B. Bar Chart: Perbandingan Produktivitas (Basah vs Kering)
p_prod <- ggplot(summary_siklus, aes(x = Status_Musim, y = Panen_Akhir, fill = Status_Musim)) +
stat_summary(fun = mean, geom = "bar", width = 0.6, color = "black") +
geom_jitter(width = 0.1, size = 3, color = "white", stroke = 1) + # Menampilkan titik data asli
geom_text(stat = 'summary', fun = mean, aes(label = round(..y.., 2)), vjust = -0.5, fontface="bold") +
scale_fill_manual(values = c("Musim Basah" = "#0072B2", "Musim Kering" = "#E69F00")) +
labs(
title = "Perbandingan Produktivitas Timun",
subtitle = "Musim Basah vs Musim Kering (Berdasarkan CH Aktual)",
y = "Rata-rata Produktivitas (Ton/Ha)",
x = NULL
) +
theme_classic(base_size = 14) +
theme(legend.position = "none")
# Tampilkan
print(p_ch)
print(p_prod)
## Warning: The dot-dot notation (`..y..`) was deprecated in ggplot2 3.4.0.
## ℹ Please use `after_stat(y)` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
# --- 1. Persiapan Data Agregat (Jaga-jaga jika belum run yang tadi) ---
# Kita butuh rata-rata produktivitas per Kategori Musim untuk Bar Chart
stat_musim <- summary_siklus %>%
group_by(Status_Musim) %>%
summarise(
Mean_Prod = mean(Panen_Akhir, na.rm = TRUE),
SD_Prod = sd(Panen_Akhir, na.rm = TRUE), # Opsional: untuk error bar
Count = n()
)
# --- 2. Membuat Plot A: Bar Chart (Rata-rata) ---
p_bar <- ggplot(stat_musim, aes(x = Status_Musim, y = Mean_Prod, fill = Status_Musim)) +
geom_col(width = 0.6, color = "black", alpha = 0.9) +
# Menambah Label Angka di atas batang
geom_text(aes(label = round(Mean_Prod, 2)),
vjust = -0.5, size = 5, family = "serif", fontface = "bold") +
# Skala & Warna Konsisten
scale_fill_manual(values = c("Musim Basah" = "#0072B2", "Musim Kering" = "#E69F00")) +
scale_y_continuous(expand = expansion(mult = c(0, 0.2))) + # Memberi ruang atas
# Labeling
labs(
title = "Rata-rata Produktivitas Timun",
subtitle = "Komparasi Rata-rata per Musim",
x = NULL,
y = "Produktivitas (Ton/Ha)"
) +
theme_classic(base_size = 14) +
theme(
text = element_text(family = "serif"),
legend.position = "none",
plot.title = element_text(face = "bold", hjust = 0.5),
plot.subtitle = element_text(hjust = 0.5)
)
# --- 3. Membuat Plot B: Boxplot (Distribusi) ---
p_box <- ggplot(summary_siklus, aes(x = Status_Musim, y = Panen_Akhir, fill = Status_Musim)) +
# Jitter: Titik data asli (penting jika datanya sedikit)
geom_jitter(width = 0.1, size = 3, color = "grey40", alpha = 0.6) +
# Boxplot transparan di atasnya
geom_boxplot(width = 0.4, alpha = 0.7, outlier.shape = NA) +
# Tanda Mean (Wajik Putih)
stat_summary(fun = mean, geom = "point", shape = 23, size = 4,
fill = "white", color = "black", stroke = 1.5) +
# Skala & Warna
scale_fill_manual(values = c("Musim Basah" = "#0072B2", "Musim Kering" = "#E69F00")) +
# Labeling
labs(
title = "Distribusi Hasil Panen",
subtitle = "Sebaran Data per Siklus Tanam",
x = NULL,
y = NULL # Y axis dihapus biar tidak redundan
) +
scale_y_continuous()+
theme_classic(base_size = 14) +
theme(
text = element_text(family = "serif"),
legend.position = "none",
axis.text.y = element_blank(), # Hilangkan angka sumbu Y kanan
axis.ticks.y = element_blank(),
axis.line.y = element_blank(),
plot.title = element_text(face = "bold", hjust = 0.5),
plot.subtitle = element_text(hjust = 0.5)
)
# --- 4. Menggabungkan dengan Patchwork ---
final_plot <- p_bar + p_box +
plot_layout(widths = c(1, 1)) + # Proporsi lebar 50:50
# Judul Utama Gabungan
plot_annotation(
title = "Analisis Dampak Iklim Terhadap Produktivitas Timun",
subtitle = "Simulasi Model Pertumbuhan Tanaman (Data Driven Season)",
theme = theme(
plot.title = element_text(size = 18, face = "bold", family = "serif", hjust = 0.5),
plot.subtitle = element_text(size = 13, family = "serif", hjust = 0.5, margin = margin(b=20))
)
)
# Tampilkan
print(final_plot)
## VIZ LAI SEASONAL
####VIZ####
str(df_simulasi_2018)
## 'data.frame': 354 obs. of 27 variables:
## $ Tanggal : Date, format: "2018-01-01" "2018-01-02" ...
## $ HST : int 1 2 3 4 5 6 7 8 9 10 ...
## $ Suhu : num 21.5 21.8 21 22.2 22 21.7 22.1 22 21 21.7 ...
## $ CH : num 0 0.6 0.5 20 0.4 21.2 0 5.3 22.2 7.2 ...
## $ Lama_Penyinaran : num 7.45 7.05 0.25 2.25 3.15 0.25 4.95 0.25 5.65 2.25 ...
## $ Radiasi : num 0 19.83 9.64 12.64 13.99 ...
## $ RH : num 80 81 88 87 80 82 79 83 88 84 ...
## $ Angin : num 1.5 1.79 1.25 1.95 2.45 2.35 2.09 1.84 1.27 1.95 ...
## $ LAI : num 1.14 0.405 0.473 0.578 0.719 ...
## $ Daun : num 0 5.79 6.76 8.26 10.27 ...
## $ Batang : num 0 2.7 3.15 3.85 4.79 ...
## $ Akar : num 0 0.949 1.108 1.354 1.685 ...
## $ Buah : num 0 0 0 0 0 0 0 0 0 0 ...
## $ Total : num 0 9.44 11.01 13.46 16.75 ...
## $ Fase : Factor w/ 3 levels "1","2","3": NA 1 1 1 1 1 1 1 1 1 ...
## $ Intersepsi : num 0 0.142 0.165 0.202 0.252 ...
## $ Run_off : num 0 0.00455 0.00317 0.17512 0.0012 ...
## $ ETo : num 0 3.67 2.04 2.58 3.06 ...
## $ ETc : num 0 1.92 1.09 1.4 1.7 ...
## $ em : num 0 3.12 1.69 2.05 2.3 ...
## $ tm : num 0 0.55 0.352 0.533 0.765 ...
## $ wdf : num 0 1 0.999 0.998 1 ...
## $ KAT : num 0.35 0.35 0.35 0.35 0.349 ...
## $ Curah_hujan_efektif : num 0 0.458 0.335 19.798 0.148 ...
## $ Prod1HAinTon : num 0 0 0 0 0 0 0 0 0 0 ...
## $ Tanggal_Tanam_Siklus: Date, format: "2018-01-01" "2018-01-01" ...
## $ Siklus_Ke : int 1 1 1 1 1 1 1 1 1 1 ...
# --- Definisi Tema Konsisten (Serif + Clean) ---
my_theme <- theme_classic(base_size = 14) +
theme(
text = element_text(family = "serif"),
plot.title = element_text(face = "bold", hjust = 0.5, size = 16),
plot.subtitle = element_text(hjust = 0.5, color = "gray40"),
legend.position = "bottom",
strip.background = element_rect(fill = "#f0f0f0", color = NA), # Latar judul facet
strip.text = element_text(face = "bold")
)
# --- Plot 1: Dinamika LAI per Siklus Tanam ---
p_lai <- ggplot(df_simulasi_2018, aes(x = HST, y = LAI, color = factor(Tanggal_Tanam_Siklus))) +
geom_line(linewidth = 1.2, alpha = 0.8) +
scale_color_brewer(palette = "Dark2") + # Warna pembeda siklus
labs(
title = "Dinamika Leaf Area Index (LAI)",
subtitle = "Perbandingan Antar Siklus Tanam (2018)",
x = "Hari Setelah Tanam (HST)",
y = "LAI",
color = "Tanggal Tanam"
) +
my_theme
print(p_lai)
## VIZ BIOMASS
# Pivot data agar formatnya long (untuk stacked chart)
biomassa_long <- df_simulasi_2018 %>%
select(HST, Tanggal_Tanam_Siklus, Daun, Batang, Akar, Buah) %>%
pivot_longer(cols = c(Daun, Batang, Akar, Buah),
names_to = "Organ", values_to = "Berat_Kering")
p_biom <- ggplot(biomassa_long, aes(x = HST, y = Berat_Kering, fill = Organ)) +
geom_area(alpha = 0.85, color = "white", size = 0.1) +
facet_wrap(~Tanggal_Tanam_Siklus, ncol = 2) + # Pisah per siklus
labs(
title = "Akumulasi Biomassa Timun",
subtitle = "Sebaran Organ per Siklus Tanam",
x = "HST", y = "Berat Kering (g/tanaman)"
) +
theme_classic(base_size = 14) +
theme(
text = element_text(family = "serif"),
plot.title = element_text(face = "bold", hjust = 0.5),
legend.position = "bottom"
)
print(p_biom)
# --- 1. Ambil Tanggal Batas Musim Tanam ---
# Kita ambil tanggal unik dari kolom 'Tanggal_Tanam_Siklus' sebagai batas
batas_musim <- unique(as.Date(df_simulasi_2018$Tanggal_Tanam_Siklus))
# --- 2. Plot KAT (Atas) ---
p_kat <- ggplot(df_simulasi_2018, aes(x = Tanggal, y = KAT)) +
# Garis KAT
geom_line(color = "#0072B2", linewidth = 1) +
# Garis Vertikal Pemisah Musim
geom_vline(xintercept = batas_musim, linetype = "dashed", color = "gray9", alpha = 0.8) +
# Label Siklus di Atas
annotate("text", x = batas_musim + 5, y = max(df_simulasi_2018$KAT) * 1.02,
label = paste("Musim", seq_along(batas_musim)),
hjust = 0, vjust = 0, size = 3.5, family = "serif", color = "black", fontface="bold") +
# Format Axis X (Bulan)
scale_x_date(date_breaks = "1 month", date_labels = "%b") +
labs(title = "Dinamika Kadar Air Tanah (KAT)",
y = "KAT (Fraksi)",
x = NULL) + # X label dihapus agar tidak duplikat dengan grafik bawah
theme_classic(base_size = 14) +
theme(
text = element_text(family = "serif"),
plot.title = element_text(face = "bold"),
axis.text.x = element_blank(), # Hilangkan teks bulan di grafik atas agar rapi
axis.ticks.x = element_blank()
)
# --- 3. Plot Curah Hujan (Bawah) ---
p_ch <- ggplot(df_simulasi_2018, aes(x = Tanggal, y = CH)) +
# Bar Curah Hujan
geom_col(fill = "#E69F00", alpha = 0.8, width = 1) +
# Garis Vertikal Pemisah Musim (Agar sejajar dengan atas)
geom_vline(xintercept = batas_musim, linetype = "dashed", color = "gray9", alpha = 0.8) +
# Format Axis X (Bulan)
scale_x_date(date_breaks = "1 month", date_labels = "%b") +
labs(title = "Curah Hujan Harian",
y = "Curah Hujan (mm)",
x = "Bulan (Tahun 2018)") +
theme_classic(base_size = 14) +
theme(
text = element_text(family = "serif"),
plot.title = element_text(face = "bold")
)
# --- 4. Gabungkan dengan Patchwork ---
combined_plot <- p_kat / p_ch +
plot_layout(heights = c(1, 1)) + # Proporsi tinggi sama
plot_annotation(
title = "Hubungan Curah Hujan dan Kadar Air Tanah",
subtitle = "Simulasi Sepanjang Tahun 2018 dengan Pemisah Musim Tanam",
theme = theme(
plot.title = element_text(size = 18, face = "bold", family = "serif", hjust = 0.5),
plot.subtitle = element_text(size = 12, family = "serif", hjust = 0.5)
)
)
# Tampilkan
print(combined_plot)
#THEME
# ==============================================================================
# 1. DEFINISI TEMA KUSTOM (Agar tampilan konsisten dan rapi)
# ==============================================================================
# Kita membuat tema dasar dari theme_classic lalu memodifikasinya
theme_publication <- theme_classic(base_size = 14, base_family = "serif") +
theme(
# Judul Plot
plot.title = element_text(face = "bold", hjust = 0.5, size = 18, color = "black"),
plot.subtitle = element_text(hjust = 0.5, size = 12, color = "gray30"),
# Sumbu dan Grid
axis.line = element_line(color = "black", linewidth = 0.8), # Garis sumbu lebih tegas
axis.ticks = element_line(color = "black"),
axis.text = element_text(color = "black"),
panel.grid.major.y = element_line(color = "gray90", linewidth = 0.5), # Tambah grid horizontal tipis
# Legenda
legend.position = "bottom",
legend.title = element_blank(), # Hilangkan judul legenda
legend.text = element_text(size = 12),
legend.key = element_rect(fill = "transparent", color = NA)
)
# ==============================================================================
# 2. PERSIAPAN DATA
# ==============================================================================
# Ambil tanggal batas musim tanam untuk garis vertikal
batas_musim <- unique(as.Date(df_simulasi_2018$Tanggal_Tanam_Siklus))
# ==============================================================================
# 3. PLOT ATAS: EVAPOTRANSPIRASI (ET)
# ==============================================================================
p_et <- ggplot(df_simulasi_2018, aes(x = Tanggal)) +
# Garis ETo (Potensial) - Oranye/Coklat
geom_line(aes(y = ETo, color = "ETo (Potensial)"), linetype = "solid", linewidth = 1) +
# Garis ETc (Tanaman) - Biru
geom_line(aes(y = ETc, color = "ETc (Aktual)"), linewidth = 1) +
# Garis Vertikal Pemisah Musim
geom_vline(xintercept = batas_musim, linetype = "dashed", color = "gray40", alpha = 0.7) +
# Label Siklus di Atas
annotate("text", x = batas_musim + 5, y = max(df_simulasi_2018$ETo) * 1.02,
label = paste("Musim Tanam", seq_along(batas_musim)),
hjust = 0, vjust = 0, size = 3.5, family = "serif", color = "black", fontface="bold") +
# Pengaturan Warna (Menyesuaikan contoh gambar: Biru dan Oranye)
scale_color_manual(values = c("ETc (Aktual)" = "#0072B2", "ETo (Potensial)" = "#D55E00")) +
# Axis X
scale_x_date(date_breaks = "1 month", date_labels = "%b") +
# Label dan Tema
labs(title = "Evapotranspirasi — Bogor (2018)",
y = "mm/hari",
x = NULL) + # X label dihapus agar tidak duplikat
theme_publication +
theme(
axis.ticks.x = element_blank()
)
print(p_et)
# ==============================================================================
# 4. PLOT BAWAH: RADIASI & LAMA PENYINARAN
# ==============================================================================
# Faktor Skala untuk sumbu ganda
scale_rad <- 2.5
p_rad <- ggplot(df_simulasi_2018, aes(x = Tanggal)) +
# Area Radiasi (Background) - Pink/Ungu Transparan
geom_area(aes(y = Radiasi, fill = "Radiasi (Rs)"), alpha = 0.4) +
# Garis Lama Penyinaran (Foreground) - Hijau Teal
geom_line(aes(y = Lama_Penyinaran * scale_rad, color = "Lama Penyinaran"), linewidth = 1) +
# Garis Vertikal Pemisah Musim
geom_vline(xintercept = batas_musim, linetype = "dashed", color = "gray9", alpha = 0.7) +
# Label Siklus di Atas
annotate("text", x = batas_musim + 5, y = max(df_simulasi_2018$Radiasi) * 1.02,
label = paste("Musim Tanam", seq_along(batas_musim)),
hjust = 0, vjust = 0, size = 3.5, family = "serif", color = "black", fontface="bold") +
# Dual Axis Y
scale_y_continuous(
name = "Radiasi (MJ/m²/hari)",
sec.axis = sec_axis(~ . / scale_rad, name = "Lama Penyinaran (Jam)")
) +
# Pengaturan Warna & Fill (Menyesuaikan contoh gambar: Pink dan Hijau Teal)
scale_fill_manual(values = c("Radiasi (Rs)" = "#CC79A7")) +
scale_color_manual(values = c("Lama Penyinaran" = "#009E73")) +
# Axis X
scale_x_date(date_breaks = "1 month", date_labels = "%b") +
# Label dan Tema
labs(title = "Lama Penyinaran & Radiasi",
x = "Tanggal (Bulan)") +
theme_publication +
theme(
axis.title.y.right = element_text(vjust = 1, angle = 90) # Merapikan label sumbu kanan
)
print(p_rad)
#KLIMOGRAF 10 TAHUN
# KLIMOGRAF
# Pastikan kolom 'tgl' sudah berformat Date
monthly_clim <- kab_bogor_df %>%
# 1. Buat kolom baru yang membulatkan tanggal ke awal bulan
mutate(bulan = floor_date(tgl, unit = "month")) %>%
# 2. Group berdasarkan bulan tersebut
group_by(bulan) %>%
# 3. Lakukan summarise
# Pastikan nama kolom (CH/Suhu) sesuai besar-kecil hurufnya dengan data aslimu
summarise(CH = sum(ch, na.rm=TRUE), # Curah hujan biasanya di-TOTAL per bulan
Suhu = mean(suhu, na.rm=TRUE)) # Suhu biasanya di-RATA-RATA per bulan
#vis bulanan
# 1. Agregasi data berdasarkan Nama Bulan
# Kita menghitung: "Berapa rata-rata curah hujan di bulan Januari selama 10 tahun terakhir?"
df_seasonal <- monthly_clim %>%
mutate(Bulan_Nama = month(bulan, label = TRUE, abbr = FALSE, locale = "ID")) %>% # Mengambil nama bulan (Januari, dst)
group_by(Bulan_Nama) %>%
summarise(
# Rata-rata CH: Rata-rata dari total hujan per bulan
Rata_CH = mean(CH, na.rm = TRUE),
# Rata-rata Suhu
Rata_Suhu = mean(Suhu, na.rm = TRUE)
)
# Cek hasil (harus ada 12 baris)
print(df_seasonal)
## # A tibble: 12 × 3
## Bulan_Nama Rata_CH Rata_Suhu
## <ord> <dbl> <dbl>
## 1 Januari 446. 20.9
## 2 Februari 520. 20.9
## 3 Maret 343. 21.5
## 4 April 370. 21.8
## 5 Mei 231. 22.1
## 6 Juni 158. 21.7
## 7 Juli 128. 21.3
## 8 Agustus 100. 21.3
## 9 September 119. 21.6
## 10 Oktober 248. 21.7
## 11 November 331. 21.7
## 12 Desember 353. 21.5
# --- Hitung Faktor Skala Baru ---
# Kita hitung ulang karena nilai rata-rata mungkin lebih rendah/tinggi dari nilai ekstrem bulanan
max_ch_avg <- max(df_seasonal$Rata_CH, na.rm = TRUE)
max_suhu_avg <- max(df_seasonal$Rata_Suhu, na.rm = TRUE)
#vis biar lebih keliatan
# --- 1. Tentukan Batas dan Skala ---
ylim_ch <- max(df_seasonal$Rata_CH, na.rm = TRUE) * 1.1 # Kasih sedikit ruang di atas bar
suhu_min <- 15 # Batas bawah yang kamu minta
suhu_max <- 25 # Batas atas yang kamu minta
# Rumus Skala:
# Kita harus memetakan rentang suhu (10 derajat) ke rentang hujan (0 - ylim_ch)
scale_factor <- ylim_ch / (suhu_max - suhu_min)
ggplot(df_seasonal, aes(x = Bulan_Nama)) +
# --- 2. Grafik Batang (Curah Hujan) - Tetap Normal ---
geom_col(aes(y = Rata_CH), fill = "#80c1ff", alpha = 0.7, width = 0.7) +
# --- 3. Grafik Garis (Suhu) - DITRANSFORMASI ---
# Rumusnya: (Suhu - 20) * scale_factor
# Artinya: Kalau suhunya 20, maka (20-20)*scale = 0 (titik terbawah)
geom_line(aes(y = (Rata_Suhu - suhu_min) * scale_factor, group = 1),
color = "#E31A1C", linewidth = 1.2) +
geom_point(aes(y = (Rata_Suhu - suhu_min) * scale_factor),
color = "#E31A1C", size = 3) +
# Label nilai suhu agar lebih jelas (opsional)
# Label untuk Bar (Curah Hujan)
geom_text(aes(y = Rata_CH, label = round(Rata_CH, 0)),
vjust = -0.5, color = "#2c5aa0", size = 3, fontface = "bold") +
# Label untuk Line (Suhu)
geom_text(aes(y = (Rata_Suhu - suhu_min) * scale_factor,
label = round(Rata_Suhu, 1)),
vjust = -1.5, color = "#E31A1C", size = 3, fontface = "bold") +
# --- 4. Mengatur Sumbu Ganda ---
scale_y_continuous(
name = "Rata-rata Curah Hujan (mm)",
limits = c(0, ylim_ch), # Kunci batas sumbu kiri
# Transformasi Balik untuk Label Kanan:
# (Nilai / scale_factor) + 20
sec.axis = sec_axis(~ (. / scale_factor) + suhu_min,
name = "Rata-rata Suhu (°C)",
breaks = seq(suhu_min, suhu_max, 1)) # Label per 1 derajat
) +
labs(
title = "Pola Iklim Bulanan (2013–2022)",
x = NULL
) +
theme_classic(base_size = 14) +
theme(
text = element_text(family = "serif"),
axis.title.y.left = element_text(color = "#2c5aa0", face = "bold"),
axis.title.y.right = element_text(color = "#E31A1C", face = "bold", angle = 90),
plot.title = element_text(face = "bold", hjust = 0.5),
axis.text.x = element_text(angle = 45, hjust = 1)
)
# ==============================================================================
# 1. DATA PREPARATION: FILTER KHUSUS 2018
# ==============================================================================
# Kita ambil dari monthly_clim yang sudah dibuat sebelumnya
df_2018 <- monthly_clim %>%
# Filter hanya tahun 2018
filter(year(bulan) == 2018) %>%
# Buat kolom Nama Bulan untuk Sumbu X
mutate(Bulan_Nama = month(bulan, label = TRUE, abbr = FALSE, locale = "ID"))
# Cek data (Pastikan ada 12 baris untuk Jan-Des 2018)
print(df_2018)
## # A tibble: 12 × 4
## bulan CH Suhu Bulan_Nama
## <date> <dbl> <dbl> <ord>
## 1 2018-01-01 333. 20.8 Januari
## 2 2018-02-01 672. 20.7 Februari
## 3 2018-03-01 432 21.4 Maret
## 4 2018-04-01 291. 21.9 April
## 5 2018-05-01 108 22.1 Mei
## 6 2018-06-01 152. 21.7 Juni
## 7 2018-07-01 9 21.0 Juli
## 8 2018-08-01 20.5 21.1 Agustus
## 9 2018-09-01 162. 21.3 September
## 10 2018-10-01 130. 21.9 Oktober
## 11 2018-11-01 382. 21.6 November
## 12 2018-12-01 196. 21.6 Desember
# ==============================================================================
# 2. HITUNG FAKTOR SKALA (KHUSUS DATA 2018)
# ==============================================================================
# Kita hitung batas atas/bawah berdasarkan data aktual 2018
max_ch_2018 <- max(df_2018$CH, na.rm = TRUE)
# --- Setting Batas Skala ---
ylim_ch <- max_ch_2018 * 1.1 # Memberi ruang kosong 10% di atas bar chart
suhu_min <- 17 # Batas bawah sumbu suhu (sesuaikan jika suhu 2018 ekstrem)
suhu_max <- 25 # Batas atas sumbu suhu (saya naikkan ke 30 untuk jaga-jaga El Nino 2018)
# Rumus Skala Konversi
scale_factor <- ylim_ch / (suhu_max - suhu_min)
# ==============================================================================
# 3. VISUALISASI KLIMOGRAF 2018
# ==============================================================================
clim_2018 <- ggplot(df_2018, aes(x = Bulan_Nama)) +
# --- Grafik Batang (Curah Hujan) ---
# Perhatikan: variable y menggunakan 'CH' (bukan Rata_CH)
geom_col(aes(y = CH), fill = "#80c1ff", alpha = 0.7, width = 0.7) +
# --- Grafik Garis (Suhu) ---
# Perhatikan: variable y menggunakan 'Suhu' (bukan Rata_Suhu)
geom_line(aes(y = (Suhu - suhu_min) * scale_factor, group = 1),
color = "#E31A1C", linewidth = 1.2) +
geom_point(aes(y = (Suhu - suhu_min) * scale_factor),
color = "#E31A1C", size = 3) +
# --- Label Angka ---
# Label CH
geom_text(aes(y = CH, label = round(CH, 0)),
vjust = -0.5, color = "#2c5aa0", size = 3, fontface = "bold") +
# Label Suhu
geom_text(aes(y = (Suhu - suhu_min) * scale_factor,
label = round(Suhu, 1)),
vjust = -1.5, color = "#E31A1C", size = 3, fontface = "bold") +
# --- Sumbu Ganda (Dual Axis) ---
scale_y_continuous(
name = "Curah Hujan (mm)",
limits = c(0, ylim_ch),
sec.axis = sec_axis(~ (. / scale_factor) + suhu_min,
name = "Suhu (°C)",
breaks = seq(suhu_min, suhu_max, 1))
) +
# --- Judul & Tema ---
labs(
title = "Klimograf Bogor Tahun 2018",
subtitle = "Total Curah Hujan & Rata-rata Suhu Bulanan",
x = NULL
) +
theme_classic(base_size = 14) +
theme(
text = element_text(family = "serif"),
axis.title.y.left = element_text(color = "#2c5aa0", face = "bold"),
axis.title.y.right = element_text(color = "#E31A1C", face = "bold", angle = 90),
plot.title = element_text(face = "bold", hjust = 0.5),
plot.subtitle = element_text(hjust = 0.5, color = "gray40"),
axis.text.x = element_text(angle = 45, hjust = 1)
)
print(clim_2018)
# ==============================================================================
# 1. PERSIAPAN DATA (Filter Tahun 2018)
# ==============================================================================
# Menggunakan df_simulasi_start2018 karena struktur datanya paling lengkap
# Kita filter hanya tahun 2018 dan pastikan tidak ada nilai NA
mod_df <- df_simulasi_2018
# ==============================================================================
# 2. PEMODELAN & VISUALISASI
# ==============================================================================
if(nrow(mod_df) >= 30) {
# --- A. Membuat Model Regresi Linier ---
# Model: Produktivitas diprediksi oleh Suhu harian
lm_mod <- lm(Prod1HAinTon ~ Suhu, data = mod_df)
# Tampilkan ringkasan model di console (opsional, untuk cek signifikansi)
print(summary(lm_mod))
# --- B. Membuat Skenario Prediksi ---
base_mean_temp <- mean(mod_df$Suhu, na.rm=TRUE)
scenarios <- data.frame(
Suhu = c(base_mean_temp, base_mean_temp+1, base_mean_temp+2, base_mean_temp+3),
scenario = c("Base", "+1°C", "+2°C", "+3°C")
)
# Hitung prediksi berdasarkan model
scenarios$predicted_prod <- predict(lm_mod, newdata = scenarios)
# --- C. Mengatur Urutan Factor (Agar grafik rapi) ---
scenarios$scenario <- factor(scenarios$scenario,
levels = c("Base", "+1°C", "+2°C", "+3°C"))
# --- D. Visualisasi ---
p10 <- ggplot(scenarios, aes(x = scenario, y = predicted_prod, fill = scenario)) +
# Membuat Bar Chart
geom_col(width = 0.6, alpha = 0.9, color = "black") +
# Menambahkan Label Angka di Atas Batang
geom_text(aes(label = round(predicted_prod, 2)),
vjust = -0.5, fontface = "bold", family = "serif", size = 4) +
# Pewarnaan: Kuning (Base) ke Merah Tua (+3°C)
scale_fill_manual(values = c("Base" = "#F0E442",
"+1°C" = "#E69F00",
"+2°C" = "#D55E00",
"+3°C" = "#800000")) +
# Skala Y: Memberi ruang di atas bar
scale_y_continuous(expand = expansion(mult = c(0, 0.2))) +
# Label dan Judul
labs(
title = "Proyeksi Produktivitas Timun vs Kenaikan Suhu",
subtitle = paste("Model Regresi Linier Data Simulasi Tahun 2018 (n =", nrow(mod_df), ")"),
x = "Skenario Kenaikan Suhu",
y = "Prediksi Rata-rata Produktivitas Harian (Ton/Ha)",
fill = "Skenario"
) +
# Tema Konsisten
theme_classic(base_size = 14) +
theme(
text = element_text(family = "serif"),
plot.title = element_text(face = "bold", hjust = 0.5),
plot.subtitle = element_text(hjust = 0.5, color = "gray40"),
axis.text.y = element_text(color = "black"),
legend.position = "none"
)
print(p10)
} else {
message("Data tahun 2015 tidak mencukupi untuk analisis regresi (< 30 observasi).")
}
##
## Call:
## lm(formula = Prod1HAinTon ~ Suhu, data = mod_df)
##
## Residuals:
## Min 1Q Median 3Q Max
## -12.7608 -9.7884 0.3107 8.2014 18.0419
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -13.1998 12.6374 -1.045 0.2970
## Suhu 1.0908 0.5895 1.850 0.0651 .
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 9.365 on 352 degrees of freedom
## Multiple R-squared: 0.009632, Adjusted R-squared: 0.006818
## F-statistic: 3.423 on 1 and 352 DF, p-value: 0.06512
# ==============================================================================
# 1. INPUT DATA AKTUAL (BPS KAB. BOGOR)
# ==============================================================================
# Data diketik manual berdasarkan gambar Excel yang Anda lampirkan
# Satuan awal: Kuintal/Hektar -> Dikonversi ke Ton/Ha (dibagi 10)
df_simulasi_start2018 <- read_excel("df_simulasi_start2018.xlsx")
df_bps <- data.frame(
Tahun = c(2018, 2019, 2020, 2021, 2022),
Prod_Kuintal = c(157.04, 152.24, 151.57, 201.61, 243.83)
) %>%
mutate(
Prod_Aktual = Prod_Kuintal / 10, # Konversi ke Ton/Ha
Sumber = "Observasi (BPS)"
)
print("Data Aktual BPS (Converted to Ton/Ha):")
## [1] "Data Aktual BPS (Converted to Ton/Ha):"
print(df_bps)
## Tahun Prod_Kuintal Prod_Aktual Sumber
## 1 2018 157.04 15.704 Observasi (BPS)
## 2 2019 152.24 15.224 Observasi (BPS)
## 3 2020 151.57 15.157 Observasi (BPS)
## 4 2021 201.61 20.161 Observasi (BPS)
## 5 2022 243.83 24.383 Observasi (BPS)
# ==============================================================================
# 2. PERSIAPAN DATA MODEL (AGREGASI TAHUNAN)
# ==============================================================================
# Karena data BPS adalah data tahunan, kita harus merata-ratakan hasil
# dari 4 musim tanam dalam satu tahun di model kita.
df_model_validasi <- df_simulasi_start2018 %>%
# Filter tahun yang sama dengan data BPS
filter(Tahun >= 2018 & Tahun <= 2022) %>%
# Langkah 1: Ambil Panen Maksimum per Musim Tanam
group_by(Tahun, Musim_Ke) %>%
summarise(Panen_Per_Musim = max(Prod1HAinTon, na.rm=TRUE), .groups = "drop") %>%
# Langkah 2: Rata-ratakan 4 musim tersebut menjadi 1 nilai per tahun
group_by(Tahun) %>%
summarise(
Prod_Model_Mean = mean(Panen_Per_Musim, na.rm=TRUE),
Prod_Model_Min = min(Panen_Per_Musim, na.rm=TRUE), # Untuk Error Bar
Prod_Model_Max = max(Panen_Per_Musim, na.rm=TRUE) # Untuk Error Bar
) %>%
mutate(Sumber = "Simulasi Model")
# ==============================================================================
# 3. PENGGABUNGAN & PERHITUNGAN ERROR (RMSE & MAPE)
# ==============================================================================
df_validasi <- left_join(df_bps, df_model_validasi, by = "Tahun")
# Hitung Statistik Error
rmse <- sqrt(mean((df_validasi$Prod_Model_Mean - df_validasi$Prod_Aktual)^2))
mape <- mean(abs((df_validasi$Prod_Model_Mean - df_validasi$Prod_Aktual) / df_validasi$Prod_Aktual)) * 100
korelasi <- cor(df_validasi$Prod_Model_Mean, df_validasi$Prod_Aktual)
print(paste("RMSE (Root Mean Square Error):", round(rmse, 2), "Ton/Ha"))
## [1] "RMSE (Root Mean Square Error): 4.91 Ton/Ha"
print(paste("MAPE (Mean Absolute Percentage Error):", round(mape, 2), "%"))
## [1] "MAPE (Mean Absolute Percentage Error): 26.43 %"
print(paste("Korelasi (r):", round(korelasi, 2)))
## [1] "Korelasi (r): -0.72"
# ==============================================================================
# 4. VISUALISASI VALIDASI MODEL (GGPLOT)
# ==============================================================================
# Menyiapkan label performa untuk ditempel di grafik
label_stats <- paste0(
"Evaluasi Model (2018-2022):\n",
"RMSE: ", round(rmse, 2), " Ton/Ha\n",
"MAPE: ", round(mape, 2), " %"
)
p_val <- ggplot() +
# --- A. AREA RANGE MODEL (Min-Max 4 Musim) ---
# Ini menunjukkan bahwa meskipun rata-rata mungkin beda, data BPS
# mungkin masih masuk dalam "Range" hasil simulasi musim basah/kering
geom_ribbon(data = df_model_validasi,
aes(x = Tahun, ymin = Prod_Model_Min, ymax = Prod_Model_Max, fill = "Rentang Model (4 Musim)"),
alpha = 0.2) +
# --- B. GARIS MODEL (Rata-rata) ---
geom_line(data = df_model_validasi,
aes(x = Tahun, y = Prod_Model_Mean, color = "Simulasi Model (Rata-rata)"),
linewidth = 1.2, linetype = "dashed") +
geom_point(data = df_model_validasi,
aes(x = Tahun, y = Prod_Model_Mean, color = "Simulasi Model (Rata-rata)"),
size = 3) +
# --- C. GARIS OBSERVASI (BPS) ---
geom_line(data = df_validasi,
aes(x = Tahun, y = Prod_Aktual, color = "Observasi Aktual (BPS)"),
linewidth = 1.2) +
geom_point(data = df_validasi,
aes(x = Tahun, y = Prod_Aktual, color = "Observasi Aktual (BPS)"),
size = 3, shape = 17) + # Shape segitiga untuk pembeda
# --- D. LABEL ANGKA ---
geom_text(data = df_validasi, aes(x = Tahun, y = Prod_Aktual, label = round(Prod_Aktual, 1)),
vjust = -1, color = "#E31A1C", fontface = "bold", size = 3.5) +
# --- E. KOSMETIK & TEMA ---
scale_fill_manual(values = c("Rentang Model (4 Musim)" = "gray50"), name = NULL) +
scale_color_manual(values = c("Simulasi Model (Rata-rata)" = "black",
"Observasi Aktual (BPS)" = "#E31A1C"), name = NULL) +
scale_y_continuous(limits = c(0, 30)) + # Sesuaikan batas Y agar lega
labs(
title = "Validasi Model: Simulasi vs Data Aktual BPS",
subtitle = "Perbandingan Produktivitas Timun di Kab. Bogor (2018-2022)",
y = "Produktivitas (Ton/Ha)",
x = "Tahun"
) +
# Menambahkan Kotak Statistik di Pojok
annotate("label", x = 2018, y = 28, label = label_stats,
hjust = 0, vjust = 1, family = "serif", size = 3.5,
fill = "white", alpha = 0.8, color = "black") +
theme_classic(base_size = 14) +
theme(
text = element_text(family = "serif"),
plot.title = element_text(face = "bold", hjust = 0.5),
plot.subtitle = element_text(hjust = 0.5, color = "gray40"),
legend.position = "bottom",
legend.box = "vertical"
)
print(p_val)
# ==============================================================================
# 1. PERSIAPAN DATA BPS (Manual Input dari Gambar Excel)
# ==============================================================================
# Input data BPS (2018-2022)
# Satuan Asli: Kuintal/Hektar. Kita konversi ke Ton/Ha (bagi 10)
df_bps
## Tahun Prod_Kuintal Prod_Aktual Sumber
## 1 2018 157.04 15.704 Observasi (BPS)
## 2 2019 152.24 15.224 Observasi (BPS)
## 3 2020 151.57 15.157 Observasi (BPS)
## 4 2021 201.61 20.161 Observasi (BPS)
## 5 2022 243.83 24.383 Observasi (BPS)
# ==============================================================================
# 2. AGREGASI DATA MODEL SIMULASI
# ==============================================================================
# Kita ambil dari df_simulasi_start2018
df_model_compare <- df_simulasi_start2018 %>%
# Filter tahun yang beririsan dengan data BPS
filter(Tahun >= 2018 & Tahun <= 2022) %>%
# Ambil nilai panen maksimum dari setiap musim tanam
group_by(Tahun, Musim_Ke) %>%
summarise(Panen_Musim = max(Prod1HAinTon, na.rm=TRUE), .groups = "drop") %>%
# Rata-ratakan 4 musim menjadi 1 nilai per tahun
group_by(Tahun) %>%
summarise(
Prod_Model = mean(Panen_Musim, na.rm=TRUE),
Prod_Model_SD = sd(Panen_Musim, na.rm=TRUE) # Standar Deviasi (Variasi musim)
) %>%
mutate(Sumber = "Model Simulasi (Potensial)")
# ==============================================================================
# 3. GABUNGKAN DATA (MERGE)
# ==============================================================================
df_final_compare <- left_join(df_bps, df_model_compare, by = "Tahun") %>%
mutate(
# Hitung Selisih (Gap)
Gap = Prod_Model - Prod_Aktual,
# Status: Apakah Model Overestimate (Lebih tinggi) atau Underestimate
Status = ifelse(Gap > 0, "Model > Aktual (Yield Gap)", "Model < Aktual")
)
print("Tabel Perbandingan Tahunan:")
## [1] "Tabel Perbandingan Tahunan:"
print(df_final_compare %>% select(Tahun, Prod_Aktual, Prod_Model, Gap, Status))
## Tahun Prod_Aktual Prod_Model Gap Status
## 1 2018 15.704 21.74980 6.0457978 Model > Aktual (Yield Gap)
## 2 2019 15.224 21.10946 5.8854639 Model > Aktual (Yield Gap)
## 3 2020 15.157 19.65724 4.5002413 Model > Aktual (Yield Gap)
## 4 2021 20.161 19.50451 -0.6564874 Model < Aktual
## 5 2022 24.383 19.00766 -5.3753410 Model < Aktual
# ==============================================================================
# 4. VISUALISASI PERBANDINGAN
# ==============================================================================
# Definisikan warna agar konsisten
cols <- c("Data BPS (Aktual)" = "#E31A1C", "Model Simulasi (Potensial)" = "#1f78b4")
ggplot(df_final_compare, aes(x = Tahun)) +
# --- 1. Area Error Bar Model (Variasi Musiman) ---
# Menunjukkan bahwa dalam 1 tahun, hasil panen bisa bervariasi tergantung musim
geom_errorbar(aes(ymin = Prod_Model - Prod_Model_SD,
ymax = Prod_Model + Prod_Model_SD,
color = "Model Simulasi (Potensial)"),
width = 0.1, linewidth = 0.8) +
# --- 2. Garis Model ---
geom_line(aes(y = Prod_Model, color = "Model Simulasi (Potensial)"),
linewidth = 1.2, linetype = "dashed") +
geom_point(aes(y = Prod_Model, color = "Model Simulasi (Potensial)"),
size = 3.5, shape = 16) +
# --- 3. Garis BPS ---
geom_line(aes(y = Prod_Aktual, color = "Data BPS (Aktual)"),
linewidth = 1.2) +
geom_point(aes(y = Prod_Aktual, color = "Data BPS (Aktual)"),
size = 3.5, shape = 17) +
# --- 4. Label Angka di Grafik ---
geom_text(aes(y = Prod_Aktual, label = round(Prod_Aktual, 1)),
vjust = -1, color = "#E31A1C", fontface = "bold", size = 3.5) +
geom_text(aes(y = Prod_Model, label = round(Prod_Model, 1)),
vjust = 1.5, color = "#1f78b4", fontface = "bold", size = 3.5) +
# --- 5. Kosmetik Grafik ---
scale_y_continuous(limits = c(0, max(df_final_compare$Prod_Aktual, df_final_compare$Prod_Model) + 5)) +
scale_color_manual(name = NULL, values = cols) +
labs(
title = "Komparasi Produktivitas: Model vs Aktual",
subtitle = "Analisis Yield Gap (Celah Hasil) di Kab. Bogor 2018-2022",
y = "Produktivitas (Ton/Ha)",
x = "Tahun",
caption = "Error Bar pada garis biru menunjukkan variasi hasil antar musim dalam tahun tersebut"
) +
theme_classic(base_size = 14) +
theme(
text = element_text(family = "serif"),
plot.title = element_text(face = "bold", hjust = 0.5),
plot.subtitle = element_text(hjust = 0.5, color = "gray40"),
legend.position = "bottom"
)
# ==============================================================================
# 1. PERSIAPAN DATA
# ==============================================================================
# --- A. Data BPS (Aktual) ---
df_bps_bar <- data.frame(
Tahun = c(2018, 2019, 2020, 2021, 2022),
Nilai = c(157.04, 152.24, 151.57, 201.61, 243.83)
) %>%
mutate(
Nilai = Nilai / 10, # Konversi Kuintal ke Ton
Sumber = "Data Aktual (BPS)",
SD = NA # Tidak ada variasi musim untuk data tahunan BPS
)
# --- B. Data Model (Simulasi) ---
df_model_bar <- df_simulasi_start2018%>%
filter(Tahun >= 2018 & Tahun <= 2022) %>%
# Ambil max panen per musim, lalu rata-ratakan per tahun
group_by(Tahun, Musim_Ke) %>%
summarise(Panen_Musim = max(Prod1HAinTon, na.rm=TRUE), .groups = "drop") %>%
group_by(Tahun) %>%
summarise(
Nilai = mean(Panen_Musim, na.rm=TRUE),
SD = sd(Panen_Musim, na.rm=TRUE) # Standar Deviasi antar musim
) %>%
mutate(Sumber = "Model Simulasi")
# --- C. Gabung Data ---
df_gabung <- bind_rows(df_bps_bar, df_model_bar)
# Urutkan Level agar Hijau (Aktual) muncul duluan atau sesuai keinginan
df_gabung$Sumber <- factor(df_gabung$Sumber, levels = c("Data Aktual (BPS)", "Model Simulasi"))
# ==============================================================================
# 2. VISUALISASI (HIJAU & KUNING)
# ==============================================================================
p_compare <- ggplot(df_gabung, aes(x = factor(Tahun), y = Nilai, fill = Sumber)) +
# --- 1. Bar Chart Samping-Sampingan (Dodge) ---
geom_col(position = position_dodge(width = 0.8), width = 0.7, color = "black", alpha = 0.9) +
# --- 2. Error Bar (Untuk Model) ---
geom_errorbar(aes(ymin = Nilai - SD, ymax = Nilai + SD),
position = position_dodge(width = 0.8),
width = 0.25, size = 0.6, color = "black") +
# --- 3. Label Angka ---
geom_text(aes(label = round(Nilai, 1)),
position = position_dodge(width = 0.8),
vjust = -0.5, size = 4, fontface = "bold", family = "serif") +
# --- 4. Warna Kustom (Hijau & Kuning) ---
scale_fill_manual(values = c(
"Data Aktual (BPS)" = "#228B22", # ForestGreen (Hijau Tua yang enak dilihat)
"Model Simulasi" = "#FFD700" # Gold (Kuning Emas agar kontras dengan putih)
)) +
# --- 5. Pengaturan Sumbu & Skala ---
scale_y_continuous(limits = c(0, max(df_gabung$Nilai) * 1.15), expand = c(0,0)) +
# --- 6. Label & Tema ---
labs(
title = "Validasi Model: Aktual vs Simulasi (2018-2022)",
subtitle = "Hijau: Observasi Lapangan | Kuning: Potensi Model Klimatologis",
y = "Produktivitas (Ton/Ha)",
x = "Tahun",
fill = NULL
) +
theme_classic(base_size = 14) +
theme(
text = element_text(family = "serif"),
plot.title = element_text(face = "bold", hjust = 0.5),
plot.subtitle = element_text(hjust = 0.5, color = "gray40"),
legend.position = "bottom",
axis.line = element_line(linewidth = 0.8),
panel.grid.major.y = element_line(color = "gray90", linetype = "dashed")
)
print(p_compare)
library(dplyr)
library(ggplot2)
# ==============================================================================
# 1. PERSIAPAN DATA
# ==============================================================================
# --- A. Data BPS (Aktual) ---
df_bps_bar <- data.frame(
Tahun = c(2018, 2019, 2020, 2021, 2022),
Nilai = c(157.04, 152.24, 151.57, 201.61, 243.83)
) %>%
mutate(
Nilai = Nilai / 10, # Konversi Kuintal ke Ton
Sumber = "Data Aktual (BPS)",
SD = NA
)
# --- B. Data Model (Simulasi) ---
df_model_bar <- df_simulasi_start2018 %>%
filter(Tahun >= 2018 & Tahun <= 2022) %>%
group_by(Tahun, Musim_Ke) %>%
summarise(Panen_Musim = max(Prod1HAinTon, na.rm=TRUE), .groups = "drop") %>%
group_by(Tahun) %>%
summarise(
Nilai = mean(Panen_Musim, na.rm=TRUE),
SD = sd(Panen_Musim, na.rm=TRUE)
) %>%
mutate(Sumber = "Model Simulasi")
# --- C. Hitung RMSE & MAPE (BAGIAN BARU) ---
# Kita gabungkan dulu kedua tabel untuk menghitung selisihnya
df_metric <- left_join(df_bps_bar, df_model_bar, by = "Tahun", suffix = c("_bps", "_model"))
# Rumus RMSE & MAPE
rmse_val <- sqrt(mean((df_metric$Nilai_model - df_metric$Nilai_bps)^2))
mape_val <- mean(abs((df_metric$Nilai_model - df_metric$Nilai_bps) / df_metric$Nilai_bps)) * 100
# Buat Label Teks untuk Grafik
label_validasi <- paste0(
"Statistik Validasi:\n",
"RMSE : ", round(rmse_val, 2), " Ton/Ha\n",
"MAPE : ", round(mape_val, 2), " %"
)
# --- D. Gabung Data untuk Plotting ---
df_gabung <- bind_rows(df_bps_bar, df_model_bar)
df_gabung$Sumber <- factor(df_gabung$Sumber, levels = c("Data Aktual (BPS)", "Model Simulasi"))
# ==============================================================================
# 2. VISUALISASI DENGAN LABEL METRIK
# ==============================================================================
p_compare <- ggplot(df_gabung, aes(x = factor(Tahun), y = Nilai, fill = Sumber)) +
# --- 1. Bar Chart ---
geom_col(position = position_dodge(width = 0.8), width = 0.7, color = "black", alpha = 0.9) +
# --- 2. Error Bar ---
geom_errorbar(aes(ymin = Nilai - SD, ymax = Nilai + SD),
position = position_dodge(width = 0.8),
width = 0.25, size = 0.6, color = "black") +
# --- 3. Label Angka di Bar ---
geom_text(aes(label = round(Nilai, 1)),
position = position_dodge(width = 0.8),
vjust = -0.5, size = 4, fontface = "bold", family = "serif") +
# --- 4. KOTAK RMSE & MAPE (BAGIAN BARU) ---
# Menempatkan kotak di pojok kiri atas
annotate("label",
x = 0.6, # Posisi X (sedikit di kiri tahun pertama)
y = max(df_gabung$Nilai) * 1.1, # Posisi Y (di atas bar tertinggi)
label = label_validasi,
hjust = 0, vjust = 1, # Rata kiri atas
family = "serif", size = 3.5,
fill = "white", alpha = 0.8, color = "black") +
# --- 5. Warna & Tema ---
scale_fill_manual(values = c(
"Data Aktual (BPS)" = "#228B22",
"Model Simulasi" = "#FFD700"
)) +
scale_y_continuous(limits = c(0, max(df_gabung$Nilai) * 1.15), expand = c(0,0)) +
labs(
title = "Validasi Model: Aktual vs Simulasi (2018-2022)",
subtitle = "Perbandingan Hasil Lapangan dengan Potensi Iklim",
y = "Produktivitas (Ton/Ha)",
x = "Tahun",
fill = NULL
) +
theme_classic(base_size = 14) +
theme(
text = element_text(family = "serif"),
plot.title = element_text(face = "bold", hjust = 0.5),
plot.subtitle = element_text(hjust = 0.5, color = "gray40"),
legend.position = "bottom",
axis.line = element_line(linewidth = 0.8),
panel.grid.major.y = element_line(color = "gray90", linetype = "dashed")
)
print(p_compare)
This is an R Markdown document. Markdown is a simple formatting syntax for authoring HTML, PDF, and MS Word documents. For more details on using R Markdown see http://rmarkdown.rstudio.com.
When you click the Knit button a document will be generated that includes both content as well as the output of any embedded R code chunks within the document. You can embed an R code chunk like this:
summary(cars)
## speed dist
## Min. : 4.0 Min. : 2.00
## 1st Qu.:12.0 1st Qu.: 26.00
## Median :15.0 Median : 36.00
## Mean :15.4 Mean : 42.98
## 3rd Qu.:19.0 3rd Qu.: 56.00
## Max. :25.0 Max. :120.00
You can also embed plots, for example:
Note that the echo = FALSE parameter was added to the
code chunk to prevent printing of the R code that generated the
plot.