A Compact Letter Display (CLD) is a statistical visualization technique used to summarize the outcomes of multiple pairwise comparisons, often following an ANOVA. It assigns letters to groups so that those sharing the same letter are not significantly different, while groups without overlapping letters are statistically distinct. This method is widely applied in research because it provides a clear, concise, and easily interpretable ranking of differences.

1 LOAD PACKAGES

library(agricolae)
library(ggplot2)
library(readxl)
library(car)
## Warning: package 'car' was built under R version 4.3.3
## Loading required package: carData
## Warning: package 'carData' was built under R version 4.3.2
library(multcompView)
library(dplyr)
## 
## Attaching package: 'dplyr'
## The following object is masked from 'package:car':
## 
##     recode
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union
library(tidyr)

2 IMPORT DATA

data_tebu <- read_excel("I:/My Drive/pak eka/Tebu/Tebu rekap th 1 dan 2/Pertumbuhanfisiologidantanah.xlsx")
data_tebu <- data.frame(data_tebu)
data_tebu$Perlakuan <- as.factor(data_tebu$Perlakuan)
data_tebu$Lokasi    <- as.factor(data_tebu$Lokasi)
data_tebu$Blok      <- as.factor(data_tebu$Blok)

3 MODEL ANOVA RCBD FAKTORIAL

model <- aov(Chltot ~ Blok + Perlakuan*Lokasi, data = data_tebu)

cat("===== ANOVA =====\n")
## ===== ANOVA =====
print(summary(model))
##                  Df Sum Sq Mean Sq F value   Pr(>F)    
## Blok              2 0.0687  0.0343   0.828 0.441056    
## Perlakuan         4 0.6415  0.1604   3.867 0.006634 ** 
## Lokasi            2 0.7591  0.3795   9.152 0.000284 ***
## Perlakuan:Lokasi  8 0.1947  0.0243   0.587 0.785517    
## Residuals        73 3.0274  0.0415                     
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
cat("\n===== ANOVA =====\n")
## 
## ===== ANOVA =====
anova_table <- Anova(model)
print(anova_table)
## Anova Table (Type II tests)
## 
## Response: Chltot
##                   Sum Sq Df F value    Pr(>F)    
## Blok             0.06866  2  0.8278 0.4410556    
## Perlakuan        0.64148  4  3.8669 0.0066341 ** 
## Lokasi           0.75908  2  9.1517 0.0002841 ***
## Perlakuan:Lokasi 0.19473  8  0.5869 0.7855173    
## Residuals        3.02744 73                      
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
# Ambil p-value
p_perlakuan  <- anova_table["Perlakuan", "Pr(>F)"]
p_lokasi     <- anova_table["Lokasi", "Pr(>F)"]
p_interaksi  <- anova_table["Perlakuan:Lokasi", "Pr(>F)"]

4 UJI ASUMSI

cat("\n===== UJI NORMALITAS =====\n")
## 
## ===== UJI NORMALITAS =====
shapiro <- shapiro.test(residuals(model))
print(shapiro)
## 
##  Shapiro-Wilk normality test
## 
## data:  residuals(model)
## W = 0.9913, p-value = 0.8222
cat("\n===== KOEFISIEN VARIASI =====\n")
## 
## ===== KOEFISIEN VARIASI =====
cv <- cv.model(model)
print(cv)
## [1] 17.72802

5 TUKEY HSD

cat("\n===== TUKEY INTERAKSI =====\n")
## 
## ===== TUKEY INTERAKSI =====
tukey_interaksi <- HSD.test(model, c("Perlakuan","Lokasi"), group=TRUE)
print(tukey_interaksi)
## $statistics
##      MSerror Df     Mean       CV      MSD
##   0.04147183 73 1.148726 17.72802 0.412714
## 
## $parameters
##    test           name.t ntr StudentizedRange alpha
##   Tukey Perlakuan:Lokasi  15         4.964188  0.05
## 
## $means
##                Chltot       std r       Min      Max       Q25       Q50
## P1:Jogja    1.3011425 0.3100000 6 1.0380000 1.906000 1.1528964 1.2046800
## P1:Lampung  1.0662412 0.2831909 6 0.7290000 1.454000 0.8321656 1.1472023
## P1:Sidoarjo 1.0719959 0.1407018 6 0.8890000 1.231547 0.9571513 1.1033026
## P2:Jogja    1.4980052 0.2673649 6 1.2219270 1.897000 1.3329607 1.3926173
## P2:Lampung  1.2050177 0.2417494 6 0.8900000 1.517000 1.0571431 1.1707272
## P2:Sidoarjo 1.1310683 0.1628781 6 0.9307143 1.296477 1.0117500 1.1395000
## P3:Jogja    1.2510288 0.2044012 6 1.0308942 1.517545 1.0740501 1.2505000
## P3:Lampung  1.1891969 0.0780820 6 1.1079489 1.304000 1.1299885 1.1724771
## P3:Sidoarjo 1.1534083 0.0945739 6 1.0310000 1.248531 1.0845000 1.1575744
## P4:Jogja    1.1570971 0.1092100 6 1.0161205 1.280000 1.0785974 1.1555000
## P4:Lampung  1.0297394 0.1700942 6 0.7380000 1.237956 0.9985000 1.0293806
## P4:Sidoarjo 1.0613027 0.2028594 6 0.7330000 1.342000 0.9947229 1.0869486
## P5:Jogja    1.1782625 0.1722347 6 1.0200039 1.494841 1.0785000 1.1213650
## P5:Lampung  1.0176214 0.1283306 6 0.8260000 1.165420 0.9426482 1.0301541
## P5:Sidoarjo 0.9197597 0.2858427 6 0.4150000 1.216685 0.8347500 0.9969759
##                  Q75
## P1:Jogja    1.289565
## P1:Lampung  1.181467
## P1:Sidoarjo 1.173118
## P2:Jogja    1.676250
## P2:Lampung  1.389459
## P2:Sidoarjo 1.268164
## P3:Jogja    1.393750
## P3:Lampung  1.239959
## P3:Sidoarjo 1.238614
## P4:Jogja    1.250749
## P4:Lampung  1.123980
## P4:Sidoarjo 1.137940
## P5:Jogja    1.216182
## P5:Lampung  1.112929
## P5:Sidoarjo 1.083429
## 
## $comparison
## NULL
## 
## $groups
##                Chltot groups
## P2:Jogja    1.4980052      a
## P1:Jogja    1.3011425     ab
## P3:Jogja    1.2510288     ab
## P2:Lampung  1.2050177     ab
## P3:Lampung  1.1891969     ab
## P5:Jogja    1.1782625     ab
## P4:Jogja    1.1570971     ab
## P3:Sidoarjo 1.1534083     ab
## P2:Sidoarjo 1.1310683     ab
## P1:Sidoarjo 1.0719959      b
## P1:Lampung  1.0662412      b
## P4:Sidoarjo 1.0613027      b
## P4:Lampung  1.0297394      b
## P5:Lampung  1.0176214      b
## P5:Sidoarjo 0.9197597      b
## 
## attr(,"class")
## [1] "group"

6 SUMMARY DATA

data_summary <- data_tebu %>%
  group_by(Perlakuan, Lokasi) %>%
  summarise(
    mean = mean(Chltot, na.rm = TRUE),
    sd   = sd(Chltot, na.rm = TRUE),
    .groups = "drop"
  )

7 HURUF PERLAKUAN (BESAR)

if(p_perlakuan < 0.05){
  tukey_P <- HSD.test(model, "Perlakuan", group=TRUE)
  huruf_P <- tukey_P$groups
  huruf_P$Perlakuan <- rownames(huruf_P)
  huruf_P <- huruf_P %>%
    mutate(huruf_P = toupper(groups)) %>%
    select(Perlakuan, huruf_P)
} else {
  huruf_P <- data.frame(
    Perlakuan = levels(data_tebu$Perlakuan),
    huruf_P = ""
  )
}

8 HURUF LOKASI (kecil)

if(p_lokasi < 0.05){
  tukey_L <- HSD.test(model, "Lokasi", group=TRUE)
  huruf_L <- tukey_L$groups
  huruf_L$Lokasi <- rownames(huruf_L)
  huruf_L <- huruf_L %>%
    mutate(huruf_L = tolower(groups)) %>%
    select(Lokasi, huruf_L)
} else {
  huruf_L <- data.frame(
    Lokasi = levels(data_tebu$Lokasi),
    huruf_L = ""
  )
}

9 GABUNGKAN HURUF

data_summary <- data_summary %>%
  left_join(huruf_P, by="Perlakuan") %>%
  left_join(huruf_L, by="Lokasi")

10 TANDA INTERAKSI (*)

if(p_interaksi < 0.05){
  data_summary$star <- "*"
} else {
  data_summary$star <- ""
}

data_summary$label_signif <- paste0(
  data_summary$huruf_P,
  data_summary$huruf_L,
  data_summary$star
)

11 GGPLOT FINAL

ggplot(data_summary, 
       aes(x = Perlakuan, 
           y = mean, 
           fill = Lokasi)) +
  
  geom_bar(stat = "identity",
           position = position_dodge(0.9),
           alpha = 0.85) +
  
  geom_errorbar(aes(ymin = mean - sd,
                    ymax = mean + sd),
                width = 0.2,
                position = position_dodge(0.9)) +
  
  geom_text(aes(label = label_signif,
                y = mean + sd + 0.05*max(mean)),
            position = position_dodge(0.9),
            size = 3,
            fontface = "bold") +
  
  labs(x = "Perlakuan",
       y = "Klorofil Total (mg/g)",
       fill = "Lokasi") +
  
  theme_bw() +
  theme(panel.grid = element_blank(),
        text = element_text(size = 12))