Business Question: Whisky Recommendation

Sebagai seorang data scientist di sebuah toko whisky, kita diminta untuk membuat product recommendation untuk whisky berdasarkan preferensi rasa masing-masing customer!

Tujuan: membentuk kelompok whisky yang memiliki karakteristik rasa khas pada tiap clusternya.

Read Data

Data yang digunakan berupa data penyulingan Malt Whisky dari 86 pabrik penyulingan, diperoleh dari penelitian Dr. Wisehart (Universitas St. Andrews). Setiap whisky diberi skor 0-4 dari 12 kategori cita rasa berdasarkan uji organoleptik:

whisky <- read.csv("data_input/whiskies.txt")
head(whisky)
##   RowID  Distillery Body Sweetness Smoky Medicinal Tobacco Honey Spicy Winey
## 1     1   Aberfeldy    2         2     2         0       0     2     1     2
## 2     2    Aberlour    3         3     1         0       0     4     3     2
## 3     3      AnCnoc    1         3     2         0       0     2     0     0
## 4     4      Ardbeg    4         1     4         4       0     0     2     0
## 5     5     Ardmore    2         2     2         0       0     1     1     1
## 6     6 ArranIsleOf    2         3     1         1       0     1     1     1
##   Nutty Malty Fruity Floral    Postcode Latitude Longitude
## 1     2     2      2      2  \tPH15 2EB   286580    749680
## 2     2     3      3      2  \tAB38 9PJ   326340    842570
## 3     2     2      3      2   \tAB5 5LI   352960    839320
## 4     1     2      1      0  \tPA42 7EB   141560    646220
## 5     2     3      1      1  \tAB54 4NH   355350    829140
## 6     0     1      1      2    KA27 8HJ   194050    649950

Data Cleansing

library(tidyverse)
## ── Attaching packages ─────────────────────────────────────── tidyverse 1.3.2 ──
## ✔ ggplot2 3.3.6      ✔ purrr   0.3.5 
## ✔ tibble  3.1.8      ✔ dplyr   1.0.10
## ✔ tidyr   1.2.1      ✔ stringr 1.4.1 
## ✔ readr   2.1.3      ✔ forcats 0.5.2 
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag()    masks stats::lag()
# meng-assign nilai dari kolom Distillery menjadi rownames
whisky <- whisky %>% 
  column_to_rownames(var = "Distillery")

# membuang kolom yang tidak digunakan
whisky_clean <- 
whisky %>% 
  select(-c("RowID", "Postcode", "Latitude", "Longitude"))

head(whisky_clean)
##             Body Sweetness Smoky Medicinal Tobacco Honey Spicy Winey Nutty
## Aberfeldy      2         2     2         0       0     2     1     2     2
## Aberlour       3         3     1         0       0     4     3     2     2
## AnCnoc         1         3     2         0       0     2     0     0     2
## Ardbeg         4         1     4         4       0     0     2     0     1
## Ardmore        2         2     2         0       0     1     1     1     2
## ArranIsleOf    2         3     1         1       0     1     1     1     0
##             Malty Fruity Floral
## Aberfeldy       2      2      2
## Aberlour        3      3      2
## AnCnoc          2      3      2
## Ardbeg          2      1      0
## Ardmore         3      1      1
## ArranIsleOf     1      1      2

Cek missing values:

colSums(is.na(whisky_clean))
##      Body Sweetness     Smoky Medicinal   Tobacco     Honey     Spicy     Winey 
##         0         0         0         0         0         0         0         0 
##     Nutty     Malty    Fruity    Floral 
##         0         0         0         0
anyNA(whisky_clean)
## [1] FALSE

Cek tipe data kolom:

# pastikan semua kolom bertipe numerik, karena k-means clustering dilakukan berdasarkan jarak
whisky_clean %>% 
  glimpse()
## Rows: 86
## Columns: 12
## $ Body      <int> 2, 3, 1, 4, 2, 2, 0, 2, 2, 2, 4, 3, 4, 2, 3, 2, 1, 2, 2, 1, …
## $ Sweetness <int> 2, 3, 3, 1, 2, 3, 2, 3, 2, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 1, …
## $ Smoky     <int> 2, 1, 2, 4, 2, 1, 0, 1, 1, 2, 2, 1, 2, 1, 2, 2, 1, 2, 3, 2, …
## $ Medicinal <int> 0, 0, 0, 4, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, …
## $ Tobacco   <int> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, …
## $ Honey     <int> 2, 4, 2, 0, 1, 1, 1, 2, 1, 0, 2, 3, 2, 2, 3, 2, 0, 1, 2, 2, …
## $ Spicy     <int> 1, 3, 0, 2, 1, 1, 1, 1, 0, 2, 1, 2, 2, 2, 1, 2, 1, 2, 2, 2, …
## $ Winey     <int> 2, 2, 0, 0, 1, 1, 0, 2, 0, 0, 3, 1, 0, 0, 1, 1, 1, 2, 1, 1, …
## $ Nutty     <int> 2, 2, 2, 1, 2, 0, 2, 2, 2, 2, 3, 0, 2, 0, 2, 2, 0, 2, 1, 2, …
## $ Malty     <int> 2, 3, 2, 2, 3, 1, 2, 2, 2, 1, 0, 2, 2, 2, 3, 2, 2, 2, 1, 2, …
## $ Fruity    <int> 2, 3, 3, 1, 1, 1, 3, 2, 2, 2, 1, 2, 2, 3, 2, 2, 2, 2, 1, 2, …
## $ Floral    <int> 2, 2, 2, 0, 1, 2, 3, 1, 2, 1, 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, …

Exploratory Data Analysis (EDA)

Cek skala antar variabel:

summary(whisky_clean)
##       Body        Sweetness         Smoky         Medicinal     
##  Min.   :0.00   Min.   :1.000   Min.   :0.000   Min.   :0.0000  
##  1st Qu.:2.00   1st Qu.:2.000   1st Qu.:1.000   1st Qu.:0.0000  
##  Median :2.00   Median :2.000   Median :1.000   Median :0.0000  
##  Mean   :2.07   Mean   :2.291   Mean   :1.535   Mean   :0.5465  
##  3rd Qu.:2.00   3rd Qu.:3.000   3rd Qu.:2.000   3rd Qu.:1.0000  
##  Max.   :4.00   Max.   :4.000   Max.   :4.000   Max.   :4.0000  
##     Tobacco           Honey           Spicy           Winey       
##  Min.   :0.0000   Min.   :0.000   Min.   :0.000   Min.   :0.0000  
##  1st Qu.:0.0000   1st Qu.:1.000   1st Qu.:1.000   1st Qu.:0.0000  
##  Median :0.0000   Median :1.000   Median :1.000   Median :1.0000  
##  Mean   :0.1163   Mean   :1.244   Mean   :1.384   Mean   :0.9767  
##  3rd Qu.:0.0000   3rd Qu.:2.000   3rd Qu.:2.000   3rd Qu.:1.0000  
##  Max.   :1.0000   Max.   :4.000   Max.   :3.000   Max.   :4.0000  
##      Nutty           Malty           Fruity          Floral     
##  Min.   :0.000   Min.   :0.000   Min.   :0.000   Min.   :0.000  
##  1st Qu.:1.000   1st Qu.:1.000   1st Qu.:1.000   1st Qu.:1.000  
##  Median :2.000   Median :2.000   Median :2.000   Median :2.000  
##  Mean   :1.465   Mean   :1.802   Mean   :1.802   Mean   :1.698  
##  3rd Qu.:2.000   3rd Qu.:2.000   3rd Qu.:2.000   3rd Qu.:2.000  
##  Max.   :4.000   Max.   :3.000   Max.   :3.000   Max.   :4.000
whisky_clean <- 
whisky_clean %>% 
  select(-Tobacco)

Kenapa kita coba hilangkan kolom Tobacco, karena jika kita lihat dari hasil summarynya, nilai mean sangat kecil dan bahkan sampai 3rd Q nilainya 0.

Determine Optimum k

Semakin tinggi k, maka:

Kalau begitu apakah kita selalu memilih k = banyak observasi? Bagaimana menentukan k optimum?

  1. Kebutuhan dari segi bisnis, data dibutuhkan menjadi berapa kelompok; atau Contoh: Misalkan kita bekerja di sebuah Bank, dan kita diminta untuk mengelompokan data nasabah kita menjadi ke dua kelompok, Nasabah Prioritas & Nasaban Tidak Prioritas.

  2. Secara statistik: Elbow method, visualisasi dengan fviz_nbclust() dari package factoextra

library(factoextra)
## Warning: package 'factoextra' was built under R version 4.2.2
## Welcome! Want to learn more? See two factoextra-related books at https://goo.gl/ve3WBa
fviz_nbclust(x = whisky_clean, 
             FUNcluster = kmeans,
             method = "wss")

Nilai K yang akan kita coba manfaatkan adalah 4, berarti hasil akhir dari clustering kita akan sebanyak 4 kelompok.

Catatan:

Membuat Clustering K-means

Kita bisa melakukan K-means Clustering menggunakan fungsi kmeans().

Parameter:

Penting: perlu dilakukan set.seed() karena terdapat random initialization pada tahap awal k-means

# Please type your code down here
RNGkind(sample.kind = "Rounding")
## Warning in RNGkind(sample.kind = "Rounding"): non-uniform 'Rounding' sampler
## used
set.seed(100)

whisky_kmeans <- kmeans(x = whisky_clean,
                        centers = 4)

Hasil dari kmeans():

  1. Banyaknya pengulangan (iterasi) algoritma k-means sampai dihasilkan cluster yang stabil
# Please run the code down below
whisky_kmeans$iter
## [1] 3
  1. Banyaknya observasi pada tiap cluster
# Please run the code down below
whisky_kmeans$size
## [1] 28  6 37 15
  1. Letak pusat cluster/centroid, biasa digunakan untuk profiling cluster (akan dibahas lebih dalam di bagian akhir)
# Please run the code down below
whisky_kmeans$centers
##       Body Sweetness    Smoky  Medicinal     Honey    Spicy     Winey    Nutty
## 1 2.678571  2.392857 1.428571 0.07142857 1.8928571 1.642857 1.8214286 1.892857
## 2 3.666667  1.500000 3.666667 3.33333333 0.1666667 1.666667 0.5000000 1.166667
## 3 1.432432  2.486486 1.054054 0.24324324 0.9729730 1.108108 0.4594595 1.162162
## 4 1.866667  1.933333 2.066667 1.06666667 1.1333333 1.466667 0.8666667 1.533333
##      Malty   Fruity    Floral
## 1 2.071429 2.107143 1.7857143
## 2 1.333333 1.166667 0.1666667
## 3 1.675676 1.972973 2.1081081
## 4 1.800000 1.066667 1.1333333
  1. Label cluster untuk tiap observasi
# Please run the code down below
head(whisky_kmeans$cluster)
##   Aberfeldy    Aberlour      AnCnoc      Ardbeg     Ardmore ArranIsleOf 
##           1           1           3           2           4           3
  1. Withinss, Betweenss, Totss
# Please run the code down below
whisky_kmeans$withinss
## [1] 136.64286  23.00000 160.43243  76.53333
whisky_kmeans$betweenss
## [1] 260.3914
whisky_kmeans$totss
## [1] 657
whisky_kmeans$betweenss / whisky_kmeans$totss
## [1] 0.3963339

Interpretation/Cluster Profiling

# memasukkan label cluster ke data awal dengan nama kolom kelompok
whisky_clean$kelompok <- whisky_kmeans$cluster

# melakukan profiling dengan summarise data
whisky_centroid <- whisky_clean %>% 
  group_by(kelompok) %>% # melakukan grouping pada setiap cluster
  summarise_all(mean) # dihitung rata2nya pada tiap cluster

whisky_centroid
## # A tibble: 4 × 12
##   kelompok  Body Sweetness Smoky Medicinal Honey Spicy Winey Nutty Malty Fruity
##      <int> <dbl>     <dbl> <dbl>     <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>  <dbl>
## 1        1  2.68      2.39  1.43    0.0714 1.89   1.64 1.82   1.89  2.07   2.11
## 2        2  3.67      1.5   3.67    3.33   0.167  1.67 0.5    1.17  1.33   1.17
## 3        3  1.43      2.49  1.05    0.243  0.973  1.11 0.459  1.16  1.68   1.97
## 4        4  1.87      1.93  2.07    1.07   1.13   1.47 0.867  1.53  1.8    1.07
## # … with 1 more variable: Floral <dbl>

Profiling:

Cluster 1: - Paling tinggi di cita rasa: Body - Paling rendah di cita rasa: Medicinal

Cluster 2: - Paling tinggi di cita rasa: Body & Smoky - Paling rendah di cita rasa: Floral & Honey

Cluster 3: - Paling tinggi di cita rasa: Sweetness - Paling rendah di cita rasa: Medicinal

Cluster 4: - Paling tinggi di cita rasa: Smoky - Paling rendah di cita rasa: Winey

Profiling 2.0

Kita selain melihat nilai yang paling menonjol pada masing-masing clusternya, kita juga harus memperhatikan secara menyeluruh. Sebagai contoh, kita melihat rasa body yang paling kuat dari keseluruhan clusternya. (Ka Dicky)

# Optional
whisky_centroid %>% 
  pivot_longer(-kelompok) %>% 
  group_by(name) %>% 
  summarise(cluster_min = which.min(value),
            cluster_max = which.max(value))
## # A tibble: 11 × 3
##    name      cluster_min cluster_max
##    <chr>           <int>       <int>
##  1 Body                3           2
##  2 Floral              2           3
##  3 Fruity              4           1
##  4 Honey               2           1
##  5 Malty               2           1
##  6 Medicinal           1           2
##  7 Nutty               3           1
##  8 Smoky               3           2
##  9 Spicy               3           2
## 10 Sweetness           2           3
## 11 Winey               3           1

Product Recommender

Misal ada 1 pelanggan yang menyukai whisky Laphroig datang ke toko kita, namun stok whisky tersebut sedang kosong. Kira-kira whisky apa yang akan kita rekomendasikan?

# cek Laphroig ada di kelompok mana?
whisky_clean["Laphroig",]
##          Body Sweetness Smoky Medicinal Honey Spicy Winey Nutty Malty Fruity
## Laphroig    4         2     4         4     0     0     1     1     1      0
##          Floral kelompok
## Laphroig      0        2
# cek whisky apa saja yang masuk ke kelompok tersebut
whisky_clean %>% 
  filter(kelompok %in% 2)
##           Body Sweetness Smoky Medicinal Honey Spicy Winey Nutty Malty Fruity
## Ardbeg       4         1     4         4     0     2     0     1     2      1
## Caol Ila     3         1     4         2     0     2     0     2     1      1
## Clynelish    3         2     3         3     0     2     0     1     1      2
## Lagavulin    4         1     4         4     0     1     2     1     1      1
## Laphroig     4         2     4         4     0     0     1     1     1      0
## Talisker     4         2     3         3     1     3     0     1     2      2
##           Floral kelompok
## Ardbeg         0        2
## Caol Ila       1        2
## Clynelish      0        2
## Lagavulin      0        2
## Laphroig       0        2
## Talisker       0        2

Jawaban:

Combining PCA with K-means

Untuk visualisasi clustering kita dapat menggunakan biplot. Hal ini akan membantu dalam mengetahui pola data yang ada pada data, sehingga kita bisa lebih yakin tentang hasil clustering yang di dapat (bentuk persebaran cluster nya seperti apa).

Dalam melakukan hal tersebut, kita akan menggunakan fungsi fviz_cluster() dari package factoextra. Berikut beberapa parameter yang kita akan digunakan

# Please type your code down here

library(factoextra)

fviz_cluster(object = whisky_kmeans, # object kmeans
             data = whisky_clean %>% select(-kelompok)) # data variable numerik

Additional: Kita akan membuat K-Means baru dengan nilai K = 3

Step 1: Membuat K-Means

set.seed(100)
RNGkind(sample.kind = "Rounding")
## Warning in RNGkind(sample.kind = "Rounding"): non-uniform 'Rounding' sampler
## used
whisky_kmeans2 <- kmeans(x = whisky_clean %>% select(-kelompok),
                         centers = 3)
fviz_cluster(object = whisky_kmeans2, # object kmeans
             data = whisky_clean %>% select(-kelompok))

Classification dengan Descition tree

Decision Tree

Decision Tree merupakan tree-based model yang cukup sederhana dengan performa yang robust/powerful untuk prediksi. Decision Tree menghasilkan visualisasi berupa pohon keputusan yang dapat diinterpretasi dengan mudah.

library(rpart)
library(rpart.plot)
## Warning: package 'rpart.plot' was built under R version 4.2.2
# Cross Validation
RNGkind(sample.kind = "Rounding")
## Warning in RNGkind(sample.kind = "Rounding"): non-uniform 'Rounding' sampler
## used
set.seed(100)
data <- whisky_clean
data <- data %>% mutate(kelompok = as.factor(kelompok))
index <- sample(nrow(data), nrow(data) * 0.8)
train_tree <- data[index,]
test_tree <- data[-index,]
library(rpart)
library(rpart.plot)
m<-rpart(kelompok~.,data=train_tree)
rpart.plot(m)

plotcp(m)

### Modelling

Buat model untuk memprediksi diabetes menggunakan seluruh prediktor, dengan menggunakan fungsi ctree() dari library(partykit). Pada fungsi tersebut terdapat 2 parameter wajib, yaitu parameter formula & data.

# Please type your code down below
library(partykit)
## Warning: package 'partykit' was built under R version 4.2.2
## Loading required package: grid
## Loading required package: libcoin
## Warning: package 'libcoin' was built under R version 4.2.2
## Loading required package: mvtnorm
model <- 
ctree(formula = kelompok ~ .,
      data = train_tree)

Prediction

Lakukan prediksi ke data train dan test, dengan menampilkan label prediksinya!

  • Fungsi: predict()
  • Type:
    • "response" = label kelas (default threshold 0.5)
    • "prob" = peluang ke kelas negatif dan positif
# prediksi ke data train
train_pred <- predict(object = model, 
                      newdata = train_tree, 
                      type = "response")

# prediksi ke data test\
test_pred <- predict(object = model, 
                     newdata = test_tree, 
                     type = "response")

Model evaluation

Pada evaluasi model kali ini, kita akan membandingkan peforma pada data train dan data test.

library(caret)
## Loading required package: lattice
## 
## Attaching package: 'caret'
## The following object is masked from 'package:purrr':
## 
##     lift
# Confusion Matrix: data train
confusionMatrix(data = train_pred,
                reference = train_tree$kelompok,
                positive = "pos")
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction  1  2  3  4
##          1 12  0  0  0
##          2  0  5  0  5
##          3 10  0 25  2
##          4  2  0  2  5
## 
## Overall Statistics
##                                           
##                Accuracy : 0.6912          
##                  95% CI : (0.5674, 0.7976)
##     No Information Rate : 0.3971          
##     P-Value [Acc > NIR] : 8.884e-07       
##                                           
##                   Kappa : 0.5508          
##                                           
##  Mcnemar's Test P-Value : NA              
## 
## Statistics by Class:
## 
##                      Class: 1 Class: 2 Class: 3 Class: 4
## Sensitivity            0.5000  1.00000   0.9259  0.41667
## Specificity            1.0000  0.92063   0.7073  0.92857
## Pos Pred Value         1.0000  0.50000   0.6757  0.55556
## Neg Pred Value         0.7857  1.00000   0.9355  0.88136
## Prevalence             0.3529  0.07353   0.3971  0.17647
## Detection Rate         0.1765  0.07353   0.3676  0.07353
## Detection Prevalence   0.1765  0.14706   0.5441  0.13235
## Balanced Accuracy      0.7500  0.96032   0.8166  0.67262
# Confusion Matrix: data test
confusionMatrix(data = test_pred,
                reference = test_tree$kelompok,
                positive = "pos")
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction 1 2 3 4
##          1 2 0 0 0
##          2 0 1 0 1
##          3 0 0 6 0
##          4 2 0 4 2
## 
## Overall Statistics
##                                          
##                Accuracy : 0.6111         
##                  95% CI : (0.3575, 0.827)
##     No Information Rate : 0.5556         
##     P-Value [Acc > NIR] : 0.41           
##                                          
##                   Kappa : 0.4522         
##                                          
##  Mcnemar's Test P-Value : NA             
## 
## Statistics by Class:
## 
##                      Class: 1 Class: 2 Class: 3 Class: 4
## Sensitivity            0.5000  1.00000   0.6000   0.6667
## Specificity            1.0000  0.94118   1.0000   0.6000
## Pos Pred Value         1.0000  0.50000   1.0000   0.2500
## Neg Pred Value         0.8750  1.00000   0.6667   0.9000
## Prevalence             0.2222  0.05556   0.5556   0.1667
## Detection Rate         0.1111  0.05556   0.3333   0.1111
## Detection Prevalence   0.1111  0.11111   0.3333   0.4444
## Balanced Accuracy      0.7500  0.97059   0.8000   0.6333

perbedaan accuracy train dan test hanya 8%

Pruning and Tree Size

Decision Tree perlu tahu kapan berhenti membuat cabang sehingga pohon lebih sederhana. Pemotongan cabang disebut sebagai Pruning. Secara umum, terbagi atas 2 cara:

  • Pre-Pruning: set parameter di awal -> buat model
  • Post-Pruning: buat model (parameter default) -> atur ulang parameter bila diperlukan
model_tuned <- ctree(formula = kelompok ~ .,
                              data = train_tree,
                              control = ctree_control(mincriterion = 0.30,
                                                      minsplit = 9,
                                                      minbucket = 2))
# prediksi ke data train
train_pred_tuned <- predict(object = model_tuned,
                            newdata = train_tree,
                            type = "response")

# prediksi ke data test
test_pred_tuned <- predict(object = model_tuned,
                            newdata = test_tree,
                            type = "response")
# Data train
confusionMatrix(data = train_pred_tuned,
                reference = train_tree$kelompok,
                positive = "pos")
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction  1  2  3  4
##          1 23  0  1  1
##          2  0  4  0  0
##          3  1  0 26  3
##          4  0  1  0  8
## 
## Overall Statistics
##                                           
##                Accuracy : 0.8971          
##                  95% CI : (0.7993, 0.9576)
##     No Information Rate : 0.3971          
##     P-Value [Acc > NIR] : < 2.2e-16       
##                                           
##                   Kappa : 0.8458          
##                                           
##  Mcnemar's Test P-Value : NA              
## 
## Statistics by Class:
## 
##                      Class: 1 Class: 2 Class: 3 Class: 4
## Sensitivity            0.9583  0.80000   0.9630   0.6667
## Specificity            0.9545  1.00000   0.9024   0.9821
## Pos Pred Value         0.9200  1.00000   0.8667   0.8889
## Neg Pred Value         0.9767  0.98438   0.9737   0.9322
## Prevalence             0.3529  0.07353   0.3971   0.1765
## Detection Rate         0.3382  0.05882   0.3824   0.1176
## Detection Prevalence   0.3676  0.05882   0.4412   0.1324
## Balanced Accuracy      0.9564  0.90000   0.9327   0.8244
# Data test
confusionMatrix(data = test_pred_tuned,
                reference = test_tree$kelompok,
                positive = "pos")
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction 1 2 3 4
##          1 4 0 1 1
##          2 0 1 0 0
##          3 0 0 9 1
##          4 0 0 0 1
## 
## Overall Statistics
##                                           
##                Accuracy : 0.8333          
##                  95% CI : (0.5858, 0.9642)
##     No Information Rate : 0.5556          
##     P-Value [Acc > NIR] : 0.0135          
##                                           
##                   Kappa : 0.7245          
##                                           
##  Mcnemar's Test P-Value : NA              
## 
## Statistics by Class:
## 
##                      Class: 1 Class: 2 Class: 3 Class: 4
## Sensitivity            1.0000  1.00000   0.9000  0.33333
## Specificity            0.8571  1.00000   0.8750  1.00000
## Pos Pred Value         0.6667  1.00000   0.9000  1.00000
## Neg Pred Value         1.0000  1.00000   0.8750  0.88235
## Prevalence             0.2222  0.05556   0.5556  0.16667
## Detection Rate         0.2222  0.05556   0.5000  0.05556
## Detection Prevalence   0.3333  0.05556   0.5556  0.05556
## Balanced Accuracy      0.9286  1.00000   0.8875  0.66667