1 Pendahuluan

Tugas ini bertujuan untuk mengimplementasikan konsep Pemrograman Berorientasi Objek (OOP) menggunakan sistem S4 di R. Sistem S4 dipilih karena mendukung definisi kelas yang lebih formal dibandingkan S3, termasuk definisi slot yang eksplisit, kontrol pewarisan yang ketat, serta validasi tipe data otomatis.

Program yang dibangun mencakup dua kelas utama:

  • ModelRegresi sebagai kelas induk, digunakan untuk regresi linier berganda
  • ModelPoli sebagai kelas turunan dari ModelRegresi, digunakan untuk regresi polinomial (nonlinier)

Estimasi koefisien pada kedua kelas dilakukan menggunakan optimasi numerik melalui fungsi optim(), selain juga tersedia metode analitik OLS sebagai pembanding.


2 Langkah 1 — Definisi Kelas Induk ModelRegresi

Kelas ModelRegresi berfungsi sebagai kerangka utama yang menyimpan seluruh hasil fitting regresi linier. Kelas ini memiliki 8 slot yang mencakup informasi koefisien, nilai fitted, residual, matriks desain, dan status optimasi.

setClass("ModelRegresi",
  representation(
    beta  = "numeric",    # koefisien hasil estimasi (beta-hat)
    yhat  = "numeric",    # nilai fitted y-hat
    ehat  = "numeric",    # residual = y - y-hat
    matX  = "matrix",     # matriks desain X
    vekY  = "numeric",    # vektor respons y
    rumus = "character",  # formula model yang digunakan
    conv  = "logical",    # cek optimasi konvergen
    iter  = "numeric"     # jumlah iterasi yang dibutuhkan
  )
)

3 Langkah 2 — Fungsi Konstruktor

Konstruktor ModelRegresi() bertugas membentuk objek kelas S4 dari data yang diberikan. Proses estimasi koefisien dapat dilakukan dengan dua cara:

  1. OLS analitik — menggunakan rumus \(\hat{\beta} = (X^TX)^{-1}X^Ty\), diselesaikan dalam satu langkah
  2. Optimasi numerik — meminimalkan Sum of Squared Errors (SSE) menggunakan optim() dengan pilihan metode BFGS, Nelder-Mead, CG, atau L-BFGS-B
ModelRegresi <- function(rumus, data, metode = "OLS") {

  # parsing formula ke matriks desain dan vektor respons
  mf <- model.frame(rumus, data = data)
  X  <- model.matrix(rumus, data = data)
  y  <- model.response(mf)
  p  <- ncol(X)

  # fungsi objektif: SSE yang akan diminimalkan
  SSE_fn <- function(b) {
    resid <- y - X %*% b
    sum(resid^2)
  }

  if (metode == "OLS") {
    # solusi analitik
    b_hat <- as.numeric(solve(t(X) %*% X) %*% t(X) %*% y)
    ok    <- TRUE
    niter <- 1L

  } else {
    # optimasi numerik via optim()
    hasil <- optim(
      par     = rep(0, p),
      fn      = SSE_fn,
      method  = metode,
      control = list(maxit = 5000, reltol = 1e-10)
    )
    b_hat <- hasil$par
    ok    <- hasil$convergence == 0
    niter <- hasil$counts[1]
  }

  names(b_hat) <- colnames(X)

  # buat dan kembalikan objek S4
  new("ModelRegresi",
      beta  = b_hat,
      yhat  = as.numeric(X %*% b_hat),
      ehat  = as.numeric(y - X %*% b_hat),
      matX  = X,
      vekY  = y,
      rumus = deparse(rumus),
      conv  = ok,
      iter  = as.numeric(niter))
}

4 Langkah 3 — Fungsi Aksesor

Fungsi aksesor dibuat agar pengguna dapat mengambil informasi dari dalam objek tanpa perlu mengakses slot secara langsung (dengan operator @). Dengan adanya aksesor, informasi dari objek dapat diakses dengan lebih mudah tanpa perlu memanggil slot secara langsung.

# definisi generik
setGeneric("getKoef",  function(obj) standardGeneric("getKoef"))
[1] "getKoef"
setGeneric("getFit",   function(obj) standardGeneric("getFit"))
[1] "getFit"
setGeneric("getResid", function(obj) standardGeneric("getResid"))
[1] "getResid"
setGeneric("getConv",  function(obj) standardGeneric("getConv"))
[1] "getConv"
# implementasi untuk kelas ModelRegresi
setMethod("getKoef",  "ModelRegresi", function(obj) obj@beta)
setMethod("getFit",   "ModelRegresi", function(obj) obj@yhat)
setMethod("getResid", "ModelRegresi", function(obj) obj@ehat)
setMethod("getConv",  "ModelRegresi", function(obj)
  list(konvergen = obj@conv, iterasi = obj@iter)
)

5 Langkah 4 — Metode Generik

Pada langkah ini didefinisikan tiga metode generik sesuai yang diminta: plot, summary, dan residuals.

5.1 Metode plot_model()

Menghasilkan empat panel diagnostik yang umum digunakan untuk mengevaluasi asumsi model regresi: plot aktual vs fitted, residual vs fitted, histogram residual, dan QQ-plot.

setGeneric("plot_model", function(obj, ...) standardGeneric("plot_model"))
[1] "plot_model"
setMethod("plot_model", "ModelRegresi", function(obj, ...) {

  par(mfrow = c(2, 2), mar = c(4, 4, 3, 1))

  # panel 1: nilai aktual vs fitted
  plot(obj@vekY, obj@yhat,
       main = "Aktual vs Fitted",
       xlab = "y aktual", ylab = "y fitted",
       pch = 16, col = "steelblue", cex = 0.8)
  abline(0, 1, col = "red", lty = 2)

  # panel 2: residual vs fitted (cek homoskedastisitas)
  plot(obj@yhat, obj@ehat,
       main = "Residual vs Fitted",
       xlab = "y fitted", ylab = "residual",
       pch = 16, col = "coral", cex = 0.8)
  abline(h = 0, col = "red", lty = 2)

  # panel 3: histogram residual
  hist(obj@ehat, breaks = 15, freq = FALSE,
       main = "Distribusi Residual",
       xlab = "residual",
       col = "lightblue", border = "white")
  curve(dnorm(x, mean(obj@ehat), sd(obj@ehat)),
        add = TRUE, col = "darkblue", lwd = 2)

  # panel 4: QQ-plot (cek normalitas)
  qqnorm(obj@ehat, pch = 16, col = "purple",
         main = "Normal Q-Q Plot")
  qqline(obj@ehat, col = "red")

  par(mfrow = c(1, 1))
  invisible(obj)
})

5.2 Metode summary()

Menampilkan tabel koefisien (estimasi, standard error, t-hitung, p-value) serta statistik kelayakan model seperti \(R^2\), \(R^2\) adjusted, dan RMSE.

setMethod("summary", "ModelRegresi", function(object, ...) {

  n <- length(object@vekY)
  p <- length(object@beta)

  # hitung statistik model
  SST   <- sum((object@vekY - mean(object@vekY))^2)
  SSE   <- sum(object@ehat^2)
  R2    <- 1 - SSE / SST
  R2adj <- 1 - (SSE / (n - p)) / (SST / (n - 1))
  s2    <- SSE / (n - p)

  # standard error, t-hitung, p-value koefisien
  vb   <- s2 * solve(t(object@matX) %*% object@matX)
  se   <- sqrt(diag(vb))
  tval <- object@beta / se
  pval <- 2 * pt(-abs(tval), df = n - p)

  cat("=============================================\n")
  cat("Formula  :", object@rumus, "\n")
  cat("Metode   : Optimasi Numerik (optim)\n")
  cat("Konvergen:", object@conv, "| Iterasi:", object@iter, "\n")
  cat("=============================================\n\n")

  cat("Tabel Koefisien:\n")
  print(data.frame(
    Estimasi  = round(object@beta, 5),
    Std.Error = round(se, 5),
    t.hitung  = round(tval, 3),
    p.value   = round(pval, 4),
    row.names = names(object@beta)
  ))

  cat("\n---------------------------------------------\n")
  cat(sprintf("R-squared       : %.5f\n", R2))
  cat(sprintf("R-squared adj.  : %.5f\n", R2adj))
  cat(sprintf("RMSE            : %.5f\n", sqrt(s2)))
  cat(sprintf("n observasi     : %d\n", n))
  cat("=============================================\n")

  invisible(list(R2 = R2, R2adj = R2adj, RMSE = sqrt(s2)))
})

5.3 Metode residuals()

setMethod("residuals", "ModelRegresi", function(object, ...) object@ehat)

# tampilan default saat objek dipanggil
setMethod("show", "ModelRegresi", function(object) {
  cat("Objek S4 : ModelRegresi\n")
  cat("Formula  :", object@rumus, "\n")
  cat("Koefisien:\n")
  print(round(object@beta, 4))
  cat("Konvergen:", object@conv, "| Iterasi:", object@iter, "\n")
})

6 Langkah 5 — Definisi Kelas Turunan ModelPoli

Kelas ModelPoli mewarisi seluruh slot dan metode dari ModelRegresi. Perbedaannya terletak pada proses konstruksi: prediktor \(x\) diperluas secara otomatis menjadi \(x, x^2, x^3, \ldots, x^d\) sebelum dimasukkan ke dalam matriks desain. Metode summary() dan plot_model() dimodifikasi agar dapat menampilkan informasi yang lebih sesuai untuk model polinomial, sementara aksesor dari kelas induk (getKoef, getFit, getResid) tetap dapat digunakan langsung tanpa perlu didefinisikan ulang.

6.1 Definisi Kelas dan Konstruktor

setClass("ModelPoli",
  contains = "ModelRegresi",    # pewarisan dari ModelRegresi
  representation(
    deg  = "numeric",           # derajat polinomial
    xvar = "character"          # nama variabel prediktor asli
  )
)

ModelPoli <- function(rumus, data, deg = 2, metode = "OLS") {

  yvar <- all.vars(rumus)[1]
  xvar <- all.vars(rumus)[-1]

  if (length(xvar) != 1)
    stop("ModelPoli hanya mendukung satu variabel prediktor.")

  # perluas data: tambahkan kolom x^2, x^3, ..., x^deg
  d2 <- data
  for (k in 2:deg)
    d2[[paste0(xvar, k)]] <- data[[xvar]]^k

  # susun formula baru dengan semua suku polinomial
  suku_baru <- c(xvar, paste0(xvar, 2:deg))
  f_baru    <- as.formula(paste(yvar, "~", paste(suku_baru, collapse = "+")))

  mf <- model.frame(f_baru, data = d2)
  X  <- model.matrix(f_baru, data = d2)
  y  <- model.response(mf)
  p  <- ncol(X)

  SSE_fn <- function(b) sum((y - X %*% b)^2)

  if (metode == "OLS") {
    b_hat <- as.numeric(solve(t(X) %*% X) %*% t(X) %*% y)
    ok    <- TRUE
    niter <- 1L
  } else {
    hasil <- optim(rep(0, p), SSE_fn, method = metode,
                   control = list(maxit = 5000, reltol = 1e-10))
    b_hat <- hasil$par
    ok    <- hasil$convergence == 0
    niter <- hasil$counts[1]
  }

  names(b_hat) <- colnames(X)

  new("ModelPoli",
      beta  = b_hat,
      yhat  = as.numeric(X %*% b_hat),
      ehat  = as.numeric(y - X %*% b_hat),
      matX  = X, vekY = y,
      rumus = deparse(rumus),
      conv  = ok, iter = as.numeric(niter),
      deg   = deg, xvar = xvar)
}

6.2 Aksesor Tambahan dan Fungsi Prediksi

# aksesor derajat polinomial
setGeneric("getDeg", function(obj) standardGeneric("getDeg"))
[1] "getDeg"
setMethod("getDeg", "ModelPoli", function(obj) obj@deg)

# fungsi prediksi untuk data baru
setGeneric("predik", function(obj, baru, ...) standardGeneric("predik"))
[1] "predik"
setMethod("predik", "ModelPoli", function(obj, baru, ...) {
  xb  <- baru[[obj@xvar]]
  dfb <- setNames(data.frame(xb), obj@xvar)
  for (k in 2:obj@deg)
    dfb[[paste0(obj@xvar, k)]] <- xb^k
  Xb  <- cbind(1, as.matrix(dfb))
  colnames(Xb)[1] <- "(Intercept)"
  as.numeric(Xb %*% obj@beta)
})

6.3 Override Metode dari Kelas Induk

# override summary: tampilkan info derajat, lalu panggil summary induk
setMethod("summary", "ModelPoli", function(object, ...) {
  cat("=============================================\n")
  cat("Kelas    : ModelPoli (turunan ModelRegresi)\n")
  cat("Derajat  :", object@deg, "| Prediktor:", object@xvar, "\n")
  cat("=============================================\n\n")
  callNextMethod()   # memanggil summary() dari ModelRegresi
})

# override plot: scatter + kurva polinomial, dan residual vs fitted
setMethod("plot_model", "ModelPoli", function(obj, ...) {

  par(mfrow = c(1, 2), mar = c(4, 4, 3, 1))

  xv   <- obj@matX[, obj@xvar]
  urut <- order(xv)

  # panel kiri: scatter plot + kurva hasil fitting
  plot(xv, obj@vekY,
       main = paste("Regresi Polinomial Derajat", obj@deg),
       xlab = obj@xvar, ylab = "y",
       pch = 16, col = "gray55", cex = 0.8)
  lines(xv[urut], obj@yhat[urut], col = "red", lwd = 2)
  legend("topleft", legend = paste("derajat =", obj@deg),
         col = "red", lty = 1, bty = "n")

  # panel kanan: residual vs fitted
  plot(obj@yhat, obj@ehat,
       main = "Residual vs Fitted",
       xlab = "fitted", ylab = "residual",
       pch = 16, col = "steelblue", cex = 0.8)
  abline(h = 0, col = "red", lty = 2)

  par(mfrow = c(1, 1))
  invisible(obj)
})

setMethod("show", "ModelPoli", function(object) {
  cat("Objek S4 : ModelPoli (turunan ModelRegresi)\n")
  cat("Formula  :", object@rumus, "| Derajat:", object@deg, "\n")
  print(round(object@beta, 4))
})

7 Langkah 6 — Membangun Package KomstatK4P2

Setelah seluruh kelas dan metode selesai didefinisikan, program dikemas menjadi sebuah package R bernama KomstatK4P2. Proses pembangunan package dilakukan langsung dari chunk berikut menggunakan R CMD build.

Package ini terdiri dari:

File Keterangan
DESCRIPTION Metadata package (nama, versi, author, dependensi)
NAMESPACE Daftar fungsi dan kelas yang diekspor
R/ModelRegresi.R Source code kelas induk
R/ModelPoli.R Source code kelas turunan
pkg <- "KomstatK4P2"

# buat struktur folder
dir.create(file.path(pkg, "R"), recursive = TRUE, showWarnings = FALSE)

# --- DESCRIPTION ---
writeLines(c(
  "Package: KomstatK4P2",
  "Type: Package",
  "Title: Model Regresi Linier dan Polinomial berbasis OOP S4",
  "Version: 1.0.0",
  "Date: 2026-06-05",
  "Author: Kelompok 4 Paralel 2 Komputasi Statistika",
  "Maintainer: Kelompok 4 Paralel 2 <komstat.k4p2@email.com>",
  "Description: Implementasi Pemrograman Berorientasi Objek sistem S4 di R",
  "    untuk pemodelan regresi linier berganda (ModelRegresi) dan regresi",
  "    polinomial (ModelPoli). Estimasi koefisien menggunakan optimasi numerik",
  "    via optim() maupun solusi analitik OLS.",
  "License: GPL-3",
  "Encoding: UTF-8",
  "Imports: methods, stats, graphics",
  "Collate: 'ModelRegresi.R' 'ModelPoli.R'"
), file.path(pkg, "DESCRIPTION"))

# --- NAMESPACE ---
writeLines(c(
  "import(methods)",
  "importFrom(stats, model.frame, model.matrix, model.response,",
  "           optim, pt, dnorm, qqline, qqnorm, sd)",
  "importFrom(graphics, abline, curve, hist, legend, lines, par, plot)",
  "exportClasses(ModelRegresi, ModelPoli)",
  "exportMethods(show, summary, residuals,",
  "              plot_model, getKoef, getFit, getResid, getConv,",
  "              getDeg, predik)",
  "export(ModelRegresi, ModelPoli)"
), file.path(pkg, "NAMESPACE"))

# --- R/ModelRegresi.R ---
writeLines(c(
  '# ModelRegresi.R — Kelas Induk OOP S4',
  '# Kelompok 4 Paralel 2, Komputasi Statistika',
  '',
  'setClass("ModelRegresi",',
  '  representation(',
  '    beta  = "numeric", yhat  = "numeric", ehat  = "numeric",',
  '    matX  = "matrix",  vekY  = "numeric", rumus = "character",',
  '    conv  = "logical", iter  = "numeric"',
  '  )',
  ')',
  '',
  'ModelRegresi <- function(rumus, data, metode = "OLS") {',
  '  mf <- model.frame(rumus, data = data)',
  '  X  <- model.matrix(rumus, data = data)',
  '  y  <- model.response(mf)',
  '  p  <- ncol(X)',
  '  SSE_fn <- function(b) { resid <- y - X %*% b; sum(resid^2) }',
  '  if (metode == "OLS") {',
  '    b_hat <- as.numeric(solve(t(X) %*% X) %*% t(X) %*% y)',
  '    ok <- TRUE; niter <- 1L',
  '  } else {',
  '    hasil <- optim(rep(0,p), SSE_fn, method=metode,',
  '                   control=list(maxit=5000, reltol=1e-10))',
  '    b_hat <- hasil$par; ok <- hasil$convergence==0; niter <- hasil$counts[1]',
  '  }',
  '  names(b_hat) <- colnames(X)',
  '  new("ModelRegresi", beta=b_hat, yhat=as.numeric(X%*%b_hat),',
  '      ehat=as.numeric(y-X%*%b_hat), matX=X, vekY=y,',
  '      rumus=deparse(rumus), conv=ok, iter=as.numeric(niter))',
  '}',
  '',
  'setGeneric("getKoef",  function(obj) standardGeneric("getKoef"))',
  'setGeneric("getFit",   function(obj) standardGeneric("getFit"))',
  'setGeneric("getResid", function(obj) standardGeneric("getResid"))',
  'setGeneric("getConv",  function(obj) standardGeneric("getConv"))',
  'setMethod("getKoef",  "ModelRegresi", function(obj) obj@beta)',
  'setMethod("getFit",   "ModelRegresi", function(obj) obj@yhat)',
  'setMethod("getResid", "ModelRegresi", function(obj) obj@ehat)',
  'setMethod("getConv",  "ModelRegresi", function(obj)',
  '  list(konvergen=obj@conv, iterasi=obj@iter))',
  '',
  'setGeneric("plot_model", function(obj,...) standardGeneric("plot_model"))',
  'setMethod("plot_model", "ModelRegresi", function(obj,...) {',
  '  par(mfrow=c(2,2), mar=c(4,4,3,1))',
  '  plot(obj@vekY, obj@yhat, main="Aktual vs Fitted",',
  '       xlab="y aktual", ylab="y fitted", pch=16, col="steelblue", cex=0.8)',
  '  abline(0,1,col="red",lty=2)',
  '  plot(obj@yhat, obj@ehat, main="Residual vs Fitted",',
  '       xlab="y fitted", ylab="residual", pch=16, col="coral", cex=0.8)',
  '  abline(h=0,col="red",lty=2)',
  '  hist(obj@ehat, breaks=15, freq=FALSE, main="Distribusi Residual",',
  '       xlab="residual", col="lightblue", border="white")',
  '  curve(dnorm(x,mean(obj@ehat),sd(obj@ehat)), add=TRUE, col="darkblue", lwd=2)',
  '  qqnorm(obj@ehat, pch=16, col="purple", main="Normal Q-Q Plot")',
  '  qqline(obj@ehat, col="red")',
  '  par(mfrow=c(1,1)); invisible(obj)',
  '})',
  '',
  'setMethod("summary", "ModelRegresi", function(object,...) {',
  '  n <- length(object@vekY); p <- length(object@beta)',
  '  SST <- sum((object@vekY-mean(object@vekY))^2); SSE <- sum(object@ehat^2)',
  '  R2 <- 1-SSE/SST; R2adj <- 1-(SSE/(n-p))/(SST/(n-1)); s2 <- SSE/(n-p)',
  '  se <- sqrt(diag(s2*solve(t(object@matX)%*%object@matX)))',
  '  tval <- object@beta/se; pval <- 2*pt(-abs(tval), df=n-p)',
  '  cat("=============================================\n")',
  '  cat("Formula  :", object@rumus, "\n")',
  '  cat("Konvergen:", object@conv, "| Iterasi:", object@iter, "\n")',
  '  cat("=============================================\n\n")',
  '  print(data.frame(Estimasi=round(object@beta,5), Std.Error=round(se,5),',
  '                   t.hitung=round(tval,3), p.value=round(pval,4),',
  '                   row.names=names(object@beta)))',
  '  cat(sprintf("\nR2=%.5f | R2adj=%.5f | RMSE=%.5f | n=%d\n",',
  '              R2, R2adj, sqrt(s2), n))',
  '  invisible(list(R2=R2, R2adj=R2adj, RMSE=sqrt(s2)))',
  '})',
  '',
  'setMethod("residuals","ModelRegresi",function(object,...) object@ehat)',
  'setMethod("show","ModelRegresi",function(object) {',
  '  cat("Objek S4 : ModelRegresi\nFormula  :", object@rumus, "\nKoefisien:\n")',
  '  print(round(object@beta,4))',
  '  cat("Konvergen:", object@conv, "| Iterasi:", object@iter, "\n")',
  '})'
), file.path(pkg, "R", "ModelRegresi.R"))

# --- R/ModelPoli.R ---
writeLines(c(
  '# ModelPoli.R — Kelas Turunan OOP S4',
  '# Kelompok 4 Paralel 2, Komputasi Statistika',
  '',
  'setClass("ModelPoli",',
  '  contains = "ModelRegresi",',
  '  representation(deg="numeric", xvar="character")',
  ')',
  '',
  'ModelPoli <- function(rumus, data, deg=2, metode="OLS") {',
  '  yvar <- all.vars(rumus)[1]; xvar <- all.vars(rumus)[-1]',
  '  if (length(xvar)!=1) stop("ModelPoli hanya mendukung satu variabel prediktor.")',
  '  d2 <- data',
  '  for (k in 2:deg) d2[[paste0(xvar,k)]] <- data[[xvar]]^k',
  '  suku_baru <- c(xvar, paste0(xvar, 2:deg))',
  '  f_baru <- as.formula(paste(yvar,"~",paste(suku_baru,collapse="+")))',
  '  mf <- model.frame(f_baru,data=d2); X <- model.matrix(f_baru,data=d2)',
  '  y  <- model.response(mf); p <- ncol(X)',
  '  SSE_fn <- function(b) sum((y-X%*%b)^2)',
  '  if (metode=="OLS") {',
  '    b_hat <- as.numeric(solve(t(X)%*%X)%*%t(X)%*%y); ok<-TRUE; niter<-1L',
  '  } else {',
  '    hasil <- optim(rep(0,p),SSE_fn,method=metode,',
  '                   control=list(maxit=5000,reltol=1e-10))',
  '    b_hat<-hasil$par; ok<-hasil$convergence==0; niter<-hasil$counts[1]',
  '  }',
  '  names(b_hat) <- colnames(X)',
  '  new("ModelPoli", beta=b_hat, yhat=as.numeric(X%*%b_hat),',
  '      ehat=as.numeric(y-X%*%b_hat), matX=X, vekY=y, rumus=deparse(rumus),',
  '      conv=ok, iter=as.numeric(niter), deg=deg, xvar=xvar)',
  '}',
  '',
  'setGeneric("getDeg", function(obj) standardGeneric("getDeg"))',
  'setMethod("getDeg", "ModelPoli", function(obj) obj@deg)',
  '',
  'setGeneric("predik", function(obj,baru,...) standardGeneric("predik"))',
  'setMethod("predik", "ModelPoli", function(obj,baru,...) {',
  '  xb <- baru[[obj@xvar]]; dfb <- setNames(data.frame(xb), obj@xvar)',
  '  for (k in 2:obj@deg) dfb[[paste0(obj@xvar,k)]] <- xb^k',
  '  Xb <- cbind(1, as.matrix(dfb)); colnames(Xb)[1] <- "(Intercept)"',
  '  as.numeric(Xb %*% obj@beta)',
  '})',
  '',
  'setMethod("summary","ModelPoli",function(object,...) {',
  '  cat("=============================================\n")',
  '  cat("Kelas    : ModelPoli (turunan ModelRegresi)\n")',
  '  cat("Derajat  :", object@deg, "| Prediktor:", object@xvar, "\n")',
  '  cat("=============================================\n\n")',
  '  callNextMethod()',
  '})',
  '',
  'setMethod("plot_model","ModelPoli",function(obj,...) {',
  '  par(mfrow=c(1,2), mar=c(4,4,3,1))',
  '  xv <- obj@matX[,obj@xvar]; urut <- order(xv)',
  '  plot(xv, obj@vekY, main=paste("Regresi Polinomial Derajat",obj@deg),',
  '       xlab=obj@xvar, ylab="y", pch=16, col="gray55", cex=0.8)',
  '  lines(xv[urut], obj@yhat[urut], col="red", lwd=2)',
  '  legend("topleft", legend=paste("derajat =",obj@deg), col="red", lty=1, bty="n")',
  '  plot(obj@yhat, obj@ehat, main="Residual vs Fitted",',
  '       xlab="fitted", ylab="residual", pch=16, col="steelblue", cex=0.8)',
  '  abline(h=0, col="red", lty=2)',
  '  par(mfrow=c(1,1)); invisible(obj)',
  '})',
  '',
  'setMethod("show","ModelPoli",function(object) {',
  '  cat("Objek S4 : ModelPoli (turunan ModelRegresi)\n")',
  '  cat("Formula  :", object@rumus, "| Derajat:", object@deg, "\n")',
  '  print(round(object@beta,4))',
  '})'
), file.path(pkg, "R", "ModelPoli.R"))
# build package → hasilkan file .tar.gz
system("R CMD build KomstatK4P2", intern = TRUE)
[1] "* checking for file 'KomstatK4P2/DESCRIPTION' ... OK"                     
[2] "* preparing 'KomstatK4P2':"                                               
[3] "* checking DESCRIPTION meta-information ... OK"                           
[4] "* checking for LF line-endings in source and make files and shell scripts"
[5] "* checking for empty or unneeded directories"                             
[6] "* building 'KomstatK4P2_1.0.0.tar.gz'"                                    
# konfirmasi file package berhasil dibuat
pkg_file <- list.files(pattern = "KomstatK4P2.*\\.tar\\.gz")
if (length(pkg_file) > 0) {
  cat("Package berhasil dibuat:", pkg_file, "\n")
  cat("Ukuran file:", file.size(pkg_file), "bytes\n")
  cat("\nCara install:\n")
  cat('  install.packages("', pkg_file, '", repos=NULL, type="source")\n', sep="")
} else {
  cat("Package belum ditemukan di direktori ini.\n")
}
Package berhasil dibuat: KomstatK4P2_1.0.0.tar.gz 
Ukuran file: 2857 bytes

Cara install:
  install.packages("KomstatK4P2_1.0.0.tar.gz", repos=NULL, type="source")

8 Langkah 7 — Data Simulasi

Dibuat dua dataset simulasi untuk menguji kedua kelas di atas.

Dataset 1 — regresi linier berganda dengan model populasi: \[y = 3 + 2{,}5x_1 - 1{,}2x_2 + \varepsilon, \quad \varepsilon \sim N(0, 1{,}5^2)\]

Dataset 2 — regresi polinomial dengan model populasi: \[y = 1 - 0{,}5x + 2x^2 + \varepsilon, \quad \varepsilon \sim N(0, 0{,}5^2)\]

# dataset 1: regresi linier berganda
set.seed(42)
n  <- 100
x1 <- rnorm(n, mean = 5, sd = 2)
x2 <- rnorm(n, mean = 10, sd = 3)
y  <- 3 + 2.5*x1 - 1.2*x2 + rnorm(n, sd = 1.5)

dat <- data.frame(y, x1, x2)
head(dat)
           y       x1        x2
1  3.0299230 7.741917 13.602896
2 -2.5839290 3.870604 13.134253
3 10.6841809 5.726257  6.990374
4  3.0990870 6.265725 15.545446
5  5.8564335 5.808537  7.999680
6  0.8632443 4.787751 10.316541
# dataset 2: regresi polinomial
set.seed(7)
x  <- seq(-3, 3, length.out = 80)
y2 <- 1 - 0.5*x + 2*x^2 + rnorm(80, sd = 0.5)

dat2 <- data.frame(y2, x)
head(dat2)
        y2         x
1 21.64362 -3.000000
2 18.96378 -2.924051
3 18.30027 -2.848101
4 17.54958 -2.772152
5 16.40178 -2.696203
6 15.56794 -2.620253

9 Langkah 8 — Ilustrasi ModelRegresi

9.1 Membuat Objek

m_ols <- ModelRegresi(y ~ x1 + x2, data = dat, metode = "OLS")
m_ols
Objek S4 : ModelRegresi
Formula  : y ~ x1 + x2 
Koefisien:
(Intercept)          x1          x2 
     3.1151      2.3922     -1.1574 
Konvergen: TRUE | Iterasi: 1 
m_bfgs <- ModelRegresi(y ~ x1 + x2, data = dat, metode = "BFGS")
m_bfgs
Objek S4 : ModelRegresi
Formula  : y ~ x1 + x2 
Koefisien:
(Intercept)          x1          x2 
     3.1151      2.3922     -1.1574 
Konvergen: TRUE | Iterasi: 27 

9.2 Menggunakan Aksesor

getKoef(m_ols)
(Intercept)          x1          x2 
   3.115093    2.392218   -1.157353 
head(getFit(m_ols))
[1]  5.8920884 -2.8265516  8.7232149  0.1124996  7.7519228  2.6285531
head(getResid(m_ols))
[1] -2.8621654  0.2426227  1.9609659  2.9865874 -1.8954893 -1.7653088
getConv(m_bfgs)
$konvergen
[1] TRUE

$iterasi
[1] 27

9.3 Memanggil summary()

summary(m_ols)
=============================================
Formula  : y ~ x1 + x2 
Metode   : Optimasi Numerik (optim)
Konvergen: TRUE | Iterasi: 1 
=============================================

Tabel Koefisien:
            Estimasi Std.Error t.hitung p.value
(Intercept)  3.11509   0.67068    4.645       0
x1           2.39222   0.07341   32.588       0
x2          -1.15735   0.05636  -20.534       0

---------------------------------------------
R-squared       : 0.93702
R-squared adj.  : 0.93572
RMSE            : 1.52045
n observasi     : 100
=============================================

9.4 Memanggil plot_model()

plot_model(m_ols)
Diagnostik plot ModelRegresi (OLS)

Diagnostik plot ModelRegresi (OLS)

9.5 Memanggil residuals()

e <- residuals(m_ols)
cat("Rata-rata residual :", round(mean(e), 8), "(harusnya mendekati 0)\n")
Rata-rata residual : 0 (harusnya mendekati 0)
cat("Std. dev. residual :", round(sd(e), 4), "\n")
Std. dev. residual : 1.505 

10 Langkah 9 — Ilustrasi ModelPoli (Pewarisan)

10.1 Membuat Objek

mp2 <- ModelPoli(y2 ~ x, data = dat2, deg = 2)
mp3 <- ModelPoli(y2 ~ x, data = dat2, deg = 3)
mp2
Objek S4 : ModelPoli (turunan ModelRegresi)
Formula  : y2 ~ x | Derajat: 2 
(Intercept)           x          x2 
     1.0989     -0.5337      1.9998 

10.2 Menggunakan Aksesor

getDeg(mp2)
[1] 2
getKoef(mp2)
(Intercept)           x          x2 
  1.0989361  -0.5336739   1.9998032 

10.3 Memanggil summary() (Override + callNextMethod())

summary(mp2)
=============================================
Kelas    : ModelPoli (turunan ModelRegresi)
Derajat  : 2 | Prediktor: x 
=============================================

=============================================
Formula  : y2 ~ x 
Metode   : Optimasi Numerik (optim)
Konvergen: TRUE | Iterasi: 1 
=============================================

Tabel Koefisien:
            Estimasi Std.Error t.hitung p.value
(Intercept)  1.09894   0.08308   13.228       0
x           -0.53367   0.03158  -16.901       0
x2           1.99980   0.02013   99.327       0

---------------------------------------------
R-squared       : 0.99247
R-squared adj.  : 0.99228
RMSE            : 0.49532
n observasi     : 80
=============================================
$R2
[1] 0.992472

$R2adj
[1] 0.9922764

$RMSE
[1] 0.495324

10.4 Memanggil plot_model() (Override)

plot_model(mp2)
Plot ModelPoli derajat 2

Plot ModelPoli derajat 2

plot_model(mp3)
Plot ModelPoli derajat 3

Plot ModelPoli derajat 3

10.5 Prediksi Nilai Baru

x_baru <- data.frame(x = c(-2.5, -1, 0, 1, 2.5))
y_pred <- predik(mp2, x_baru)
y_asli <- 1 - 0.5*x_baru$x + 2*x_baru$x^2

data.frame(
  x          = x_baru$x,
  y_populasi = round(y_asli, 4),
  y_prediksi = round(y_pred, 4),
  selisih    = round(abs(y_pred - y_asli), 4)
)
     x y_populasi y_prediksi selisih
1 -2.5      14.75    14.9319  0.1819
2 -1.0       3.50     3.6324  0.1324
3  0.0       1.00     1.0989  0.0989
4  1.0       2.50     2.5651  0.0651
5  2.5      12.25    12.2635  0.0135

11 Langkah 10 — Verifikasi Pewarisan S4

cat("is(m_ols,  'ModelRegresi') :", is(m_ols,  "ModelRegresi"),  "\n")
is(m_ols,  'ModelRegresi') : TRUE 
cat("is(mp2,    'ModelRegresi') :", is(mp2,    "ModelRegresi"),  "\n")
is(mp2,    'ModelRegresi') : TRUE 
cat("is(mp2,    'ModelPoli')    :", is(mp2,    "ModelPoli"),     "\n")
is(mp2,    'ModelPoli')    : TRUE 
cat("is(m_ols,  'ModelPoli')    :", is(m_ols,  "ModelPoli"),     "\n")
is(m_ols,  'ModelPoli')    : FALSE 

Hasilnya menunjukkan bahwa mp2 dikenali sebagai ModelRegresi (karena mewarisi), sementara m_ols tidak dikenali sebagai ModelPoli. Pewarisan berjalan sesuai yang diharapkan.


12 Langkah 11 — Perbandingan Metode Optimasi

cat(sprintf("%-14s  %10s  %8s  %8s\n", "Metode", "Intercept", "x1", "x2"))
Metode           Intercept        x1        x2
cat(strrep("-", 46), "\n")
---------------------------------------------- 
for (met in c("OLS", "BFGS", "Nelder-Mead", "CG", "L-BFGS-B")) {
  m <- ModelRegresi(y ~ x1 + x2, data = dat, metode = met)
  b <- getKoef(m)
  cat(sprintf("%-14s  %10.5f  %8.5f  %8.5f\n", met, b[1], b[2], b[3]))
}
OLS                3.11509   2.39222  -1.15735
BFGS               3.11510   2.39222  -1.15735
Nelder-Mead        3.11509   2.39221  -1.15735
CG                 3.11487   2.39223  -1.15734
L-BFGS-B           3.11509   2.39222  -1.15735

Hasil estimasi dari berbagai metode optimasi menunjukkan nilai yang sangat mirip dengan solusi OLS. Temuan ini menunjukkan bahwa proses estimasi telah berjalan dengan baik.


13 Kesimpulan

Berdasarkan hasil implementasi, konsep OOP menggunakan sistem S4 berhasil diterapkan pada pemodelan regresi linier dan regresi polinomial.

Komponen Implementasi
Kelas induk ModelRegresi dengan 8 slot
Konstruktor ModelRegresi() — OLS analitik dan optim() numerik
Aksesor getKoef(), getFit(), getResid(), getConv()
Metode generik plot_model(), summary(), residuals()
Kelas turunan ModelPoli dengan contains = "ModelRegresi"
Pewarisan Slot dan aksesor induk digunakan langsung oleh ModelPoli
Override summary() dan plot_model() di ModelPoli menggunakan callNextMethod()
Metode baru predik() dan getDeg() khusus ModelPoli
Package R KomstatK4P2_1.0.0.tar.gz dibangun otomatis dari chunk di atas