## Them font chu
library(showtext)
## Loading required package: sysfonts
## Loading required package: showtextdb
font_add("Times New Roman", "times.ttf")  # Đường dẫn tới file font Times New Roman
font_add("Arial", "arial.ttf")
font_add("Times New Roman Bold", "timesbd.ttf")
showtext_auto()

# 1
library(ggplot2)
library(viridis)
## Loading required package: viridisLite
library(ggthemes)

QHTN (bố trí theo kiểu Box-wilson design) với các yếu tố lần lượt là Gocluon_R, KcTam_e, HsMasat_μ.

library(rsm)

# Box-Wilson Design bbd

QHTN <- bbd(
    k = 3,            # 3 yếu tố
   n0 = 3,            # 3 điểm trung tâm
    block = FALSE,     # No blocks 
    randomize = FALSE, # Not randomized
    coding = list(
        x1 ~ (KcTam_e - 25)/10,
        x2 ~ (Gocluon_R - 5)/2,
        x3 ~ (HsMasat_μ - 0.15)/0.05
    )
)

print(QHTN)
##    run.order std.order KcTam_e Gocluon_R HsMasat_μ
## 1          1         1      15         3      0.15
## 2          2         2      35         3      0.15
## 3          3         3      15         7      0.15
## 4          4         4      35         7      0.15
## 5          5         5      15         5      0.10
## 6          6         6      35         5      0.10
## 7          7         7      15         5      0.20
## 8          8         8      35         5      0.20
## 9          9         9      25         3      0.10
## 10        10        10      25         7      0.10
## 11        11        11      25         3      0.20
## 12        12        12      25         7      0.20
## 13        13        13      25         5      0.15
## 14        14        14      25         5      0.15
## 15        15        15      25         5      0.15
## 
## Data are stored in coded form using these coding formulas ...
## x1 ~ (KcTam_e - 25)/10
## x2 ~ (Gocluon_R - 5)/2
## x3 ~ (HsMasat_μ - 0.15)/0.05

Bước 2: Thí nghiệm thực tế và đưa dữ liệu vào R

## Ở đây mình chạy số liệu mô phỏng về LucF và MucdoBD

library(readxl)
data <- read_excel("~/1.Detai_Caohoc/MrXuan/Bao2/DataQHTN_ECAPPC_211025.xlsx", 
    sheet = "BoxBehnken", col_types = c("numeric", 
        "numeric", "numeric", "numeric", 
        "numeric"))
head(data)
## # A tibble: 6 × 5
##   KCTam_e GocLuon_R HesoMS_f LucF_Y1 CiBD_Y2
##     <dbl>     <dbl>    <dbl>   <dbl>   <dbl>
## 1      15         3     0.15    124.   0.499
## 2      35         3     0.15    131.   0.340
## 3      15         7     0.15    116.   0.485
## 4      35         7     0.15    117.   0.578
## 5      15         5     0.1     111.   0.544
## 6      35         5     0.1     113.   0.457
# Kiểm tra số dòng của QHTN và data_excel
if (nrow(data) != nrow(QHTN)) {
    stop("Số dòng của dữ liệu Excel không khớp với số dòng của QHTN!")}

# Gắn cột y1 và y2 từ data vào QHTN
QHTN$LucF_Y1 <- data$LucF_Y1
QHTN$CiBD_Y2 <- data$CiBD_Y2

# In kết quả để kiểm tra
print(QHTN)
##    run.order std.order KcTam_e Gocluon_R HsMasat_μ  LucF_Y1   CiBD_Y2
## 1          1         1      15         3      0.15 123.7659 0.4994021
## 2          2         2      35         3      0.15 130.7823 0.3400517
## 3          3         3      15         7      0.15 116.1473 0.4849841
## 4          4         4      35         7      0.15 116.7619 0.5778713
## 5          5         5      15         5      0.10 110.9146 0.5444828
## 6          6         6      35         5      0.10 113.0822 0.4565588
## 7          7         7      15         5      0.20 133.1021 0.6148683
## 8          8         8      35         5      0.20 133.7514 0.5768149
## 9          9         9      25         3      0.10 121.8491 0.3216295
## 10        10        10      25         7      0.10 107.1278 0.4771377
## 11        11        11      25         3      0.20 133.2195 0.3620474
## 12        12        12      25         7      0.20 130.8387 0.5030914
## 13        13        13      25         5      0.15 124.2060 0.4579854
## 14        14        14      25         5      0.15 125.6337 0.4834678
## 15        15        15      25         5      0.15 127.0610 0.5089501
## 
## Data are stored in coded form using these coding formulas ...
## x1 ~ (KcTam_e - 25)/10
## x2 ~ (Gocluon_R - 5)/2
## x3 ~ (HsMasat_μ - 0.15)/0.05

Bước 3: Tính toán Hàm hôi quy & model tối ưu hóa #1: Hàm hồi quy đủ hệ số (Full model)

ket_qua_model <- rsm((LucF_Y1) ~ SO(x1, x2, x3), 
                   data = QHTN)

summary(ket_qua_model)
## 
## Call:
## rsm(formula = (LucF_Y1) ~ SO(x1, x2, x3), data = QHTN)
## 
##              Estimate Std. Error  t value  Pr(>|t|)    
## (Intercept) 125.63356    1.06778 117.6587 8.411e-10 ***
## x1            1.30600    0.65388   1.9973 0.1022901    
## x2           -4.84264    0.65388  -7.4060 0.0007064 ***
## x3            9.74223    0.65388  14.8991 2.465e-05 ***
## x1:x2        -1.60043    0.92472  -1.7307 0.1440592    
## x1:x3        -0.37958    0.92472  -0.4105 0.6984543    
## x2:x3         3.08516    0.92472   3.3363 0.0206360 *  
## x1^2         -2.15771    0.96248  -2.2418 0.0750438 .  
## x2^2         -1.61149    0.96248  -1.6743 0.1549243    
## x3^2         -0.76328    0.96248  -0.7930 0.4637111    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Multiple R-squared:  0.9837, Adjusted R-squared:  0.9545 
## F-statistic: 33.63 on 9 and 5 DF,  p-value: 0.000602
## 
## Analysis of Variance Table
## 
## Response: (LucF_Y1)
##                 Df Sum Sq Mean Sq F value    Pr(>F)
## FO(x1, x2, x3)   3 960.54  320.18 93.6077 8.194e-05
## TWI(x1, x2, x3)  3  48.89   16.30  4.7649   0.06283
## PQ(x1, x2, x3)   3  25.86    8.62  2.5204   0.17199
## Residuals        5  17.10    3.42                  
## Lack of fit      3  13.03    4.34  2.1309   0.33523
## Pure error       2   4.08    2.04                  
## 
## Stationary point of response surface:
##        x1        x2        x3 
##  1.735140 -3.565064 -1.254566 
## 
## Stationary point in original units:
##     KcTam_e   Gocluon_R   HsMasat_μ 
## 42.35139736 -2.13012879  0.08727168 
## 
## Eigenanalysis:
## eigen() decomposition
## $values
## [1]  0.5655325 -1.9867145 -3.1112960
## 
## $vectors
##          [,1]       [,2]       [,3]
## x1  0.2338790  0.8179920 -0.5255375
## x2 -0.6178802 -0.2922928 -0.7299240
## x3 -0.7506828  0.4954331  0.4370600
# pred.r_squared
PRESS <- sum((residuals(ket_qua_model)/(1-lm.influence(ket_qua_model)$hat))^2)
SST   <- sum((ket_qua_model$fitted.values + residuals(ket_qua_model) - mean(ket_qua_model$model[,1]))^2)
predR2 <- 1 - PRESS/SST
#cat("Predicted R-squared:", round(predR2, 4), "\n")

cat(sprintf("Std. Dev.: %.4f   Mean: %.4f   C.V.%%: %.2f%%   Predicted R-squared: %.4f\n", sigma(ket_qua_model), mean(ket_qua_model$model[,1]), sigma(ket_qua_model)/mean(ket_qua_model$model[,1])*100, round(predR2, 4)))
## Std. Dev.: 1.8494   Mean: 123.2162   C.V.%: 1.50%   Predicted R-squared: 0.7932

#2: Hàm hồi quy (đã kiểm định Studien Ttest và Fisher Ftest)

#Hoặc 
ket_qua_model <- rsm((LucF_Y1) ~ FO(x1, x2, x3)+TWI(x1,x2,x3)+PQ(x1,x2,x3), 
                   data = QHTN)

summary(ket_qua_model)
## 
## Call:
## rsm(formula = (LucF_Y1) ~ FO(x1, x2, x3) + TWI(x1, x2, x3) + 
##     PQ(x1, x2, x3), data = QHTN)
## 
##              Estimate Std. Error  t value  Pr(>|t|)    
## (Intercept) 125.63356    1.06778 117.6587 8.411e-10 ***
## x1            1.30600    0.65388   1.9973 0.1022901    
## x2           -4.84264    0.65388  -7.4060 0.0007064 ***
## x3            9.74223    0.65388  14.8991 2.465e-05 ***
## x1:x2        -1.60043    0.92472  -1.7307 0.1440592    
## x1:x3        -0.37958    0.92472  -0.4105 0.6984543    
## x2:x3         3.08516    0.92472   3.3363 0.0206360 *  
## x1^2         -2.15771    0.96248  -2.2418 0.0750438 .  
## x2^2         -1.61149    0.96248  -1.6743 0.1549243    
## x3^2         -0.76328    0.96248  -0.7930 0.4637111    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Multiple R-squared:  0.9837, Adjusted R-squared:  0.9545 
## F-statistic: 33.63 on 9 and 5 DF,  p-value: 0.000602
## 
## Analysis of Variance Table
## 
## Response: (LucF_Y1)
##                 Df Sum Sq Mean Sq F value    Pr(>F)
## FO(x1, x2, x3)   3 960.54  320.18 93.6077 8.194e-05
## TWI(x1, x2, x3)  3  48.89   16.30  4.7649   0.06283
## PQ(x1, x2, x3)   3  25.86    8.62  2.5204   0.17199
## Residuals        5  17.10    3.42                  
## Lack of fit      3  13.03    4.34  2.1309   0.33523
## Pure error       2   4.08    2.04                  
## 
## Stationary point of response surface:
##        x1        x2        x3 
##  1.735140 -3.565064 -1.254566 
## 
## Stationary point in original units:
##     KcTam_e   Gocluon_R   HsMasat_μ 
## 42.35139736 -2.13012879  0.08727168 
## 
## Eigenanalysis:
## eigen() decomposition
## $values
## [1]  0.5655325 -1.9867145 -3.1112960
## 
## $vectors
##          [,1]       [,2]       [,3]
## x1  0.2338790  0.8179920 -0.5255375
## x2 -0.6178802 -0.2922928 -0.7299240
## x3 -0.7506828  0.4954331  0.4370600
# Thực hiện stepwise regression dựa trên AIC
# Sử dụng direction = "both" để thử cả forward và backward selection
ket_qua_model_opt <- step(ket_qua_model, direction = "both", trace = 1)
## Start:  AIC=21.97
## (LucF_Y1) ~ FO(x1, x2, x3) + TWI(x1, x2, x3) + PQ(x1, x2, x3)
## 
##                   Df Sum of Sq    RSS    AIC
## <none>                          17.10 21.967
## - PQ(x1, x2, x3)   3     25.86  42.96 29.785
## - TWI(x1, x2, x3)  3     48.89  66.00 36.223
## - FO(x1, x2, x3)   3    960.54 977.65 76.656
# Hiển thị tóm tắt mô hình tối ưu
summary(ket_qua_model_opt)
## 
## Call:
## rsm(formula = (LucF_Y1) ~ FO(x1, x2, x3) + TWI(x1, x2, x3) + 
##     PQ(x1, x2, x3), data = QHTN)
## 
##              Estimate Std. Error  t value  Pr(>|t|)    
## (Intercept) 125.63356    1.06778 117.6587 8.411e-10 ***
## x1            1.30600    0.65388   1.9973 0.1022901    
## x2           -4.84264    0.65388  -7.4060 0.0007064 ***
## x3            9.74223    0.65388  14.8991 2.465e-05 ***
## x1:x2        -1.60043    0.92472  -1.7307 0.1440592    
## x1:x3        -0.37958    0.92472  -0.4105 0.6984543    
## x2:x3         3.08516    0.92472   3.3363 0.0206360 *  
## x1^2         -2.15771    0.96248  -2.2418 0.0750438 .  
## x2^2         -1.61149    0.96248  -1.6743 0.1549243    
## x3^2         -0.76328    0.96248  -0.7930 0.4637111    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Multiple R-squared:  0.9837, Adjusted R-squared:  0.9545 
## F-statistic: 33.63 on 9 and 5 DF,  p-value: 0.000602
## 
## Analysis of Variance Table
## 
## Response: (LucF_Y1)
##                 Df Sum Sq Mean Sq F value    Pr(>F)
## FO(x1, x2, x3)   3 960.54  320.18 93.6077 8.194e-05
## TWI(x1, x2, x3)  3  48.89   16.30  4.7649   0.06283
## PQ(x1, x2, x3)   3  25.86    8.62  2.5204   0.17199
## Residuals        5  17.10    3.42                  
## Lack of fit      3  13.03    4.34  2.1309   0.33523
## Pure error       2   4.08    2.04                  
## 
## Stationary point of response surface:
##        x1        x2        x3 
##  1.735140 -3.565064 -1.254566 
## 
## Stationary point in original units:
##     KcTam_e   Gocluon_R   HsMasat_μ 
## 42.35139736 -2.13012879  0.08727168 
## 
## Eigenanalysis:
## eigen() decomposition
## $values
## [1]  0.5655325 -1.9867145 -3.1112960
## 
## $vectors
##          [,1]       [,2]       [,3]
## x1  0.2338790  0.8179920 -0.5255375
## x2 -0.6178802 -0.2922928 -0.7299240
## x3 -0.7506828  0.4954331  0.4370600
# Lấy phương trình hồi quy từ mô hình tối ưu
formula_opt <- formula(ket_qua_model_opt)
cat("Phương trình hồi quy tối ưu:\n", as.character(formula_opt)[3], "\n")
## Phương trình hồi quy tối ưu:
##  FO(x1, x2, x3) + TWI(x1, x2, x3) + PQ(x1, x2, x3)
# Kiểm tra các hệ số có ý nghĩa (p-value < 0.05)
coef_summary <- summary(ket_qua_model_opt)$coefficients
significant_coefs <- coef_summary[coef_summary[, "Pr(>|t|)"] < 0.05, , drop = FALSE]
print("Các hệ số có ý nghĩa thống kê (p-value < 0.05):")
## [1] "Các hệ số có ý nghĩa thống kê (p-value < 0.05):"
print(significant_coefs)
##               Estimate Std. Error    t value     Pr(>|t|)
## (Intercept) 125.633561  1.0677792 117.658747 8.410982e-10
## x2           -4.842645  0.6538785  -7.406031 7.064389e-04
## x3            9.742233  0.6538785  14.899148 2.464679e-05
## x2:x3         3.085157  0.9247239   3.336301 2.063595e-02
par(mfrow = c(2, 3))         # 2 x 3 pictures on one plot
persp(
    ket_qua_model,           # Our model
    ~ x1 + x2 + x3,     # A formula to obtain the 6 possible graphs
    col = topo.colors(100),  # Color palette
    contours = "colors",     # Include contours with the same color palette
    # xlabs = c("Nhiệt độ (°C)", 
    #           "Glucse (g/L)", 
    #           "Peptone (g/L)", 
    #           "Thời gian (giờ)" ), 
    # zlab = "Mật độ tế bào (log CFU/mL)",
    expand = 1,
    cex = 0.8
)
## Warning in title(sub = dat$labs[5], ...): "expand" is not a graphical parameter
## Warning in title(sub = dat$labs[5], ...): "expand" is not a graphical parameter
## Warning in title(sub = dat$labs[5], ...): "expand" is not a graphical parameter

Đồ thị tương quan giữa x1 và x2

persp(
    ket_qua_model,            # Our model
    ~ x2 + x3,    # A formula to obtain the 6 possible graphs
    col = topo.colors(100), # Color palette
    contours = "colors",     # Include contours with the same color palette
    xlabs = c("\n\nNhiệt độ (°C)", 
              "\n\nGlucose (g/L)"), 
    # zlab = "\n\nMật độ tế bào (log CFU/mL)", # Dùng \n để cách hàng
    cex.lab = 0.9,
    expand = 0.8,
    # theta = 30, phi = 30 # Xoay đồ thị,
    ticktype = "detailed",
    nticks = 4
)
## Warning in title(sub = dat$labs[5], ...): "expand" is not a graphical parameter
## Warning in title(sub = dat$labs[5], ...): "nticks" is not a graphical parameter
par(xpd = NA, srt  = 95)  ## disable clipping and set string rotation
text(-0.28, 0.02,"Mật độ tế bào (log CFU/mL)")

################################################################################
# Biểu đồ 7
library(plotly)
## 
## 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(viridis)
library(showtext)

# Font chữ
font_add("Times New Roman", "times.ttf")
showtext_auto()

# --- Tạo lưới x2, x3 ở dạng mã hoá ---
x2_seq_coded <- seq(min(QHTN$x2), max(QHTN$x2), length.out = 80)
x3_seq_coded <- seq(min(QHTN$x3), max(QHTN$x3), length.out = 80)
R_seq_coded <- seq(min(data$GocLuon_R), max(data$GocLuon_R), length.out = 80)
f_seq_coded <- seq(min(data$HesoMS_f), max(data$HesoMS_f), length.out = 80)


grid <- expand.grid(x1 = 0, x3 = x2_seq_coded, x2 = x3_seq_coded)
grid_real <-expand.grid(x1 = 25, x2 = R_seq_coded, x3 = f_seq_coded)
grid$F_pred <- predict(ket_qua_model, newdata = grid)


# --- Chuyển thành ma trận z ---
z_mat <- matrix(grid$F_pred, nrow = length(x2_seq_coded), ncol = length(x3_seq_coded))

# --- Tick marks tự động theo giá trị thực ---
x2_ticks <- pretty(grid_real$x2)
x3_ticks <- pretty(grid_real$x3)
z_ticks  <- c(110,115,120,125,130,135)  # chỉ hiện 110->135

# format 2 chữ số thập phân
x3_ticktext <- sprintf("%.2f", x3_ticks)

# --- Surface 3D ---
fig <- plot_ly(
  x = unique(grid_real$x2),
  y = unique(grid_real$x3),
  z = z_mat,
  type = "surface",
  colors = viridis(120, option = "turbo"),
  showscale = FALSE,
  colorbar = list(
    len = 0.3,         # ← chiều CAO thanh màu (0–1)
    thickness = 25,     # ← độ RỘNG thanh màu (pixels)
    x = 0.85,           # ← vị trí ngang (1 = mép phải đồ thị)
    y = 0.4,           # ← vị trí dọc (0=bottom, 1=top)
    yanchor = "middle", # giữ tâm thanh
    
    # ---- ÉP plotly phải hiển thị đầy đủ text ----
    tickmode = "array",
    tickvals = z_ticks,
    ticktext = z_ticks,
    tickfont = list(family = "Times New Roman", size = 12),
    
    # ---- Title cho colorbar ----
    title = list(
      text = "Force F (ton)",
      font = list(family = "Times New Roman", size = 12),
      side = "top",      # ← Thêm dòng này: vị trí title (top/bottom)
      standoff = 20     # ← Thêm dòng này: khoảng cách từ title đến colorbar (pixels)
    )),
    
    contours = list(
    x = list(show=TRUE, start = min(R_seq_coded), end = max(R_seq_coded), size = 0.2),
    y = list(show=TRUE, start = min(f_seq_coded), end = max(f_seq_coded), size = 0.005)))

# --- Layout: tick marks, labels, font, box ---
fig <- fig %>% layout(
  scene = list(
    xaxis = list(
      title = "Corner radius R (mm)",
      tickmode = "array",
      tickvals = x2_ticks,
      ticktext = x2_ticks,          # hiển thị giá trị thực
      tickangle = -30,              # xoay tick-label
      showline = TRUE, linewidth = 2, mirror = TRUE,
      ticks = "outside",            # tick marks
      ticklen = 5,                  # chiều dài tick
      titlefont = list(family="Times New Roman", size=12),
      tickfont  = list(family="Times New Roman", size=12)
    ),
    yaxis = list(
      title = "Friction coefficient μ",
      tickmode = "array",
      tickvals = x3_ticks,
      ticktext = x3_ticktext,
      tickangle = -20,
      showline = TRUE, linewidth = 2, mirror = TRUE,
      ticks = "outside", ticklen = 5,
      titlefont = list(family="Times New Roman", size=12),
      tickfont  = list(family="Times New Roman", size=12)
    ),
    zaxis = list(
      title = "Force F (ton)",
      tickmode = "array",
      tickvals = z_ticks,            # chỉ hiện các giá trị mong muốn
      ticktext = z_ticks,
      showline = TRUE, linewidth = 2, mirror = TRUE,
      ticks = "outside", ticklen = 5,
      titlefont = list(family="Times New Roman", size=12),
      tickfont  = list(family="Times New Roman", size=12)
    ),
    aspectmode = "cube",             # hộp chữ nhật
    bgcolor = "white",
    # --- Thêm fix camera ---
    camera = list(
      eye = list(
        x = 3 * cos(-250*pi/360) * cos(50*pi/360),  # scale 2 để vừa khít
        y = 3 * sin(-250*pi/360) * cos(50*pi/360),
        z = 3 * sin(50*pi/360)
      )
    )
  ),
font = list(family = "Times New Roman"),
dragmode = FALSE,  # không cho xoay/pan/zoom
margin = list(
  l = 0,    # left margin (tương ứng với 4 trong mar)
  r = 2,   # right margin (tương ứng với 0.5 trong mar)
  t = 0,  # top margin (tương ứng với 1 trong mar)
  b = 2   # bottom margin (tương ứng với 3.5 trong mar)
)
#annotations = list(
  #list(
    #text = "Slice at Distance e = 25 (mm)",
    #xref = "paper",
    #yref = "paper",
    #x = 0.4,
    #y = 0.08,
    #xanchor = "left",
    #yanchor = "top",
    #font = list(family = "Times New Roman", size = 12),
    #showarrow = FALSE
    #bgcolor = "white",
    #bordercolor = "black",
    #borderwidth = 1
  )



fig

Đồ thị tương quan giữa x1 và x2 (#2)

# Load thư viện cần thiết
library(rsm)
library(plotly)

# Tạo dữ liệu lưới để dự đoán y dựa trên x1 và x2, giữ x3 cố định
x2_range <- seq(min(QHTN$x2), max(QHTN$x2), length.out = 200)
x3_range <- seq(min(QHTN$x3), max(QHTN$x3), length.out = 200)
x1_fixed <- 0  # Giữ x3 cố định tại -1

# Tạo lưới dữ liệu
grid <- expand.grid(x1 = x1_fixed, x2 = x2_range, x3 = x3_range)
y_pred <- predict(ket_qua_model, newdata = grid)

# Chuyển đổi y_pred thành ma trận để vẽ surface plot
y_matrix <- matrix(y_pred, nrow = length(x2_range), ncol = length(x3_range))

# Cách 1 Tạo các điểm chia cho trục x (10 khoảng)
#x_ticks <- seq(-1.215, 1.215, length.out = 11)  # 11 điểm để tạo 10 khoảng

# Cách 2 Xác định các giá trị nhãn cụ thể cho trục x
x_ticks <- c(-1, -0.5, 0, 0.5, 1)
# Tùy chỉnh nhãn cho trục x
x_ticktext <- c("-1", "-0.5", "0", "0.5", "1")

# Vẽ biểu đồ 3D bằng plotly
plot_ly(x = x2_range, y = x3_range, z = y_matrix, type = "surface") %>%
  layout(
    title = "Ảnh hưởng của x1 và x2 đến y khi x3 = 0",
    scene = list(
      xaxis = list(title = "x1",range = c(0, 1),  # Thiết lập phạm vi trục x
        tickvals = x_ticks,        # Các điểm chia trên trục x
        ticktext = x_ticktext),
      yaxis = list(title = "x2",range = c(-1, 1),  # Thiết lập phạm vi trục x
        tickvals = x_ticks,        # Các điểm chia trên trục x
        ticktext = x_ticktext),
      zaxis = list(title = "y", range = c(min(y_pred), max(y_pred))),
      camera = list(eye = list(x = -1.5, y = -1.5, z = 1.5))
    )
  )

Đồ thị đường đồng mức

par(mfrow = c(2,3))       # 2 x 3 pictures on one plot
contour(
  ket_qua_model,            # Our model
  ~ x1 + x2 + x3,    # A formula to obtain the 6 possible graphs 
  image = TRUE,           # If image = TRUE, apply color to each contour
  )

Tìm điểm tối ưu theo Central Composite Design chuẩn (CCD)

opt_point <- summary(ket_qua_model)$canonical$xs
# opt_point
 
op_point_ru <- code2val(
    opt_point,                     # Optimal point in coded units
    codings = codings(QHTN)  # Formulas to convert to factor units
)
op_point_ru
##     KcTam_e   Gocluon_R   HsMasat_μ 
## 42.35139736 -2.13012879  0.08727168

Tìm điểm tối ưu theo QH Trực giao Cấp 2 (BBD)

# Lấy điểm tối ưu trong đơn vị mã hóa (coded units) với giới hạn
# Sử dụng optim để tìm điểm tối ưu lớn nhất trong khoảng (-1, 1)
objective <- function(x) {
  -predict(ket_qua_model, newdata = data.frame(x1 = x[1], x2 = x[2], x3 = x[3]))
}

# Tìm điểm tối ưu lớn nhất với giới hạn
opt_point <- optim(
  par = c(0, 0, 0),              # Điểm khởi đầu
  fn = objective,                 # Hàm mục tiêu (lấy âm để tìm max)
  method = "L-BFGS-B",            # Phương pháp với ràng buộc
  lower = c(-1, -1, -1),  # Giới hạn dưới
  upper = c(1, 1, 1)  # Giới hạn trên
)$par

cat("Điểm tối ưu trong đơn vị mã hóa (coded units):", round(opt_point, 3), "\n")
## Điểm tối ưu trong đơn vị mã hóa (coded units): 0.511 -0.799 1
# Chuyển đổi sang đơn vị thực tế (factor units)
op_point_ru <- code2val(
    opt_point,                     # Optimal point in coded units
    codings = codings(QHTN)  # Formulas to convert to factor units
)
cat("Điểm tối ưu trong đơn vị thực tế:", round(op_point_ru, 3), "\n")
## Điểm tối ưu trong đơn vị thực tế: 0.511 -0.799 1
# Giá trị y tại điểm tối ưu
y_opt <- predict(ket_qua_model, newdata = data.frame(x1 = opt_point[1], x2 = opt_point[2], x3 = opt_point[3]))
cat("Giá trị y tại điểm tối ưu:", round(y_opt, 2), "\n")
## Giá trị y tại điểm tối ưu: 135.55

Dự đoán kết quả đầu ra từ điểm tối ưu

opt_point_df <- data.frame(  # predict() needs a data frame with the points 
    x1 = opt_point[1],         # to be predicted 
    x2 = opt_point[2],
    x3 = opt_point[3]
)

best_response <- predict(
    ket_qua_model,           # Our model
    opt_point_df             # Data frame with points to be predicted 
)

names(best_response) <- "Luc F"
best_response
##    Luc F 
## 135.5514