Giới thiệu

Nấm là một nguồn thực phẩm phong phú nhưng cũng tiềm ẩn nhiều nguy cơ gây ngộ độc, thậm chí tử vong nếu ăn phải nấm độc. Việc phân biệt chính xác giữa nấm ăn được và nấm độc đòi hỏi kiến thức chuyên môn và kinh nghiệm. Trong bối cảnh đó, việc áp dụng phân tích dữ liệu để tìm ra các quy luật và đặc điểm nhận dạng trở nên vô cùng hữu ích.

Báo cáo này sử dụng bộ dữ liệu “Mushroom Classification” nổi tiếng từ UCI Machine Learning Repository, được trích từ “Sổ tay Hướng dẫn về Nấm của Hiệp hội Audubon”. Mục tiêu của báo cáo là:

  • Khám phá và mô tả các đặc điểm vật lý của các loại nấm trong bộ dữ liệu. Phân tích mối quan hệ giữa các đặc điểm này và độc tính của nấm (ăn được/độc).

  • Kiểm định các giả thuyết thống kê để xác định những yếu tố có ảnh hưởng ý nghĩa đến việc phân loại nấm.

1 Đọc và tổng quan về bộ dữ liệu

1.1 Đọc file dữ liệu Mushroom

data <- read.csv("D:/PTDLDT/Mushroom.csv", header = T)
datatable(data)
## Warning in instance$preRenderHook(instance): It seems your data is too big for
## client-side DataTables. You may consider server-side processing:
## https://rstudio.github.io/DT/server.html

1.2 Tổng quan về bộ dữ liệu

Bộ dữ liệu Mushroom bao gồm 23 biến, trong đó biến class là biến mục tiêu (‘p’ cho poisonous - độc, ‘e’ cho edible - ăn được) và 22 biến còn lại mô tả các đặc điểm vật lý của nấm. Đáng chú ý, tất cả các biến này đều là biến định tính.

1.2.1 Danh sách các biến và ý nghĩa

Dưới đây là bảng mô tả ý nghĩa của một số biến chính trong bộ dữ liệu.

Bảng mô tả chi tiết tất cả các biến trong bộ dữ liệu Mushroom
STT Tên biến Mô tả Các giá trị và ý nghĩa
1 class Loại nấm (Biến mục tiêu) e=edible (ăn được), p=poisonous (độc)
2 cap.shape Hình dạng mũ nấm b=bell (hình chuông), c=conical (hình nón), x=convex (lồi), f=flat (phẳng), k=knobbed (có núm), s=sunken (lõm)
3 cap.surface Bề mặt mũ nấm f=fibrous (dạng sợi), g=grooves (có rãnh), y=scaly (có vảy), s=smooth (mịn)
4 cap.color Màu sắc mũ nấm n=brown (nâu), b=buff (da bò), c=cinnamon (quế), g=gray (xám), r=green (xanh lá), p=pink (hồng), u=purple (tím), e=red (đỏ), w=white (trắng), y=yellow (vàng)
5 bruises Có bị thâm khi va chạm? t=bruises (có), f=no (không)
6 odor Mùi của nấm a=almond (hạnh nhân), l=anise (hồi), c=creosote (dầu creosote), y=fishy (tanh), f=foul (hôi), m=musty (mốc), n=none (không mùi), p=pungent (hăng), s=spicy (cay)
7 gill.attachment Cách phiến nấm gắn vào thân a=attached (gắn liền), d=descending (chạy xuống), f=free (tự do), n=notched (có khía)
8 gill.spacing Khoảng cách giữa các phiến nấm c=close (gần nhau), w=crowded (chật chội), d=distant (thưa)
9 gill.size Kích thước phiến nấm b=broad (rộng), n=narrow (hẹp)
10 gill.color Màu sắc phiến nấm k=black (đen), n=brown (nâu), b=buff (da bò), h=chocolate (sô-cô-la), g=gray (xám), o=orange (cam), p=pink (hồng), r=green (xanh lá), u=purple (tím), e=red (đỏ), w=white (trắng), y=yellow (vàng)
11 stalk.shape Hình dạng thân nấm e=enlarging (phình ra),t=tapering (thon lại)
12 stalk.root Gốc thân nấm b=bulbous (hình củ), c=club (hình dùi cui), u=cup (hình chén), e=equal (đều), z=rhizomorphs (dạng rễ giả), r=rooted (có rễ)
13 stalk.surface.above.ring Bề mặt thân nấm (phía trên vành) f=fibrous (dạng sợi), y=scaly (có vảy), k=silky (mượt như lụa), s=smooth (mịn)
14 stalk.surface.below.ring Bề mặt thân nấm (phía dưới vành) f=fibrous (dạng sợi), y=scaly (có vảy), k=silky (mượt như lụa), s=smooth (mịn)
15 stalk.color.above.ring Màu sắc thân nấm (phía trên vành) n=brown (nâu), b=buff (da bò), c=cinnamon (quế), g=gray (xám), o=orange (cam), p=pink (hồng), e=red (đỏ), w=white (trắng), y=yellow (vàng)
16 stalk.color.below.ring Màu sắc thân nấm (phía dưới vành) n=brown (nâu), b=buff (da bò), c=cinnamon (quế), g=gray (xám), o=orange (cam), p=pink (hồng), e=red (đỏ), w=white (trắng), y=yellow (vàng)
17 veil.type Loại màng che (Veil) p=partial (một phần), u=universal (toàn phần)
18 veil.color Màu sắc màng che n=brown (nâu), o=orange (cam), w=white (trắng), y=yellow (vàng)
19 ring.number Số lượng vành nấm n=none (không có), o=one (một), t=two (hai)
20 ring.type Loại vành nấm c=cobwebby (dạng mạng nhện), e=evanescent (dễ rụng), f=flaring (loe ra), l=large (lớn), n=none (không có), p=pendant (treo lủng lẳng), s=sheathing (dạng bao), z=zone (vân)
21 spore.print.color Màu sắc của bào tử k=black (đen), n=brown (nâu), b=buff (da bò), h=chocolate (sô-cô-la), r=green (xanh lá), o=orange (cam), u=purple (tím), w=white (trắng), y=yellow (vàng)
22 population Sự phân bố, quần thể a=abundant (dồi dào), c=clustered (mọc cụm), n=numerous (số lượng lớn), s=scattered (rải rác), v=several (vài cây), y=solitary (mọc đơn)
23 habitat Môi trường sống g=grasses (trên cỏ), l=leaves (trên lá), m=meadows (đồng cỏ), p=paths (lối đi), u=urban (đô thị), w=waste (bãi rác), d=woods (trong rừng)

1.2.2 Cấu trúc bộ dữ liệu

Để có được cái nhìn tổng quan và bước đầu đánh giá tính phù hợp của tập dữ liệu với mục tiêu nghiên cứu, ta tiến hành kiểm tra cấu trúc của bộ dữ liệu bằng cách sử dụng hai hàm cơ bản trong R là dim()str().

  • Hàm dim() cho biết kích thước của bảng dữ liệu, cụ thể là số hàng (quan sát) và số cột (biến).
dim(data)
## [1] 8124   23

Bộ dữ liệu có 8124 quan sát (mẫu nấm) và 23 biến (đặc điểm). Đây là một bộ dữ liệu có kích thước đủ lớn để thực hiện các phân tích thống kê có ý nghĩa.

  • Hàm str() cung cấp thông tin về cấu trúc dữ liệu, bao gồm tên các biến, kiểu dữ liệu của từng biến (ví dụ: số, chuỗi ký tự, biến phân loại), cũng như một số giá trị minh họa cho mỗi biến.
str(data)
## 'data.frame':    8124 obs. of  23 variables:
##  $ class                   : chr  "p" "e" "e" "p" ...
##  $ cap.shape               : chr  "x" "x" "b" "x" ...
##  $ cap.surface             : chr  "s" "s" "s" "y" ...
##  $ cap.color               : chr  "n" "y" "w" "w" ...
##  $ bruises                 : chr  "t" "t" "t" "t" ...
##  $ odor                    : chr  "p" "a" "l" "p" ...
##  $ gill.attachment         : chr  "f" "f" "f" "f" ...
##  $ gill.spacing            : chr  "c" "c" "c" "c" ...
##  $ gill.size               : chr  "n" "b" "b" "n" ...
##  $ gill.color              : chr  "k" "k" "n" "n" ...
##  $ stalk.shape             : chr  "e" "e" "e" "e" ...
##  $ stalk.root              : chr  "e" "c" "c" "e" ...
##  $ stalk.surface.above.ring: chr  "s" "s" "s" "s" ...
##  $ stalk.surface.below.ring: chr  "s" "s" "s" "s" ...
##  $ stalk.color.above.ring  : chr  "w" "w" "w" "w" ...
##  $ stalk.color.below.ring  : chr  "w" "w" "w" "w" ...
##  $ veil.type               : chr  "p" "p" "p" "p" ...
##  $ veil.color              : chr  "w" "w" "w" "w" ...
##  $ ring.number             : chr  "o" "o" "o" "o" ...
##  $ ring.type               : chr  "p" "p" "p" "p" ...
##  $ spore.print.color       : chr  "k" "n" "n" "k" ...
##  $ population              : chr  "s" "n" "n" "s" ...
##  $ habitat                 : chr  "u" "g" "m" "u" ...

Tất cả các biến hiện đang ở dạng ký tự (chr). Để R có thể phân tích chúng dưới dạng biến định tính, chúng ta cần chuyển đổi chúng sang kiểu factor.

1.3 Kiểm tra và xử lý dữ liệu thiếu (NA)

Dữ liệu trong thực tế thường không hoàn hảo. Một số nguồn tài liệu cho biết giá trị bị thiếu trong bộ dữ liệu này được ký hiệu là ‘?’. Chúng ta cần kiểm tra và xử lý chúng.

# Sử dụng hàm na_if() kết hợp với across() để thay thế '?' một cách an toàn.
mushroom_df_processed <- data %>%
  dplyr::mutate(dplyr::across(everything(), ~na_if(., "?")))

# Đếm số lượng giá trị NA trong mỗi cột
na_counts <- colSums(is.na(mushroom_df_processed))

# Lấy ra những cột có chứa giá trị NA (số lượng NA > 0)
cols_with_na <- na_counts[na_counts > 0]

# In ra kết quả kiểm tra
if (length(cols_with_na) > 0) {
  cat("\nPhát hiện các giá trị NA trong các cột sau:\n")
  print(cols_with_na)
} else {
  cat("\nKhông tìm thấy giá trị NA nào trong bộ dữ liệu.\n")
}
## 
## Phát hiện các giá trị NA trong các cột sau:
## stalk.root 
##       2480
# XỬ LÝ NA BẰNG PHƯƠNG PHÁP THAY THẾ BẰNG MODE

# Hàm để tìm mode (giá trị phổ biến nhất) của một vector
get_mode <- function(v) {
  # Loại bỏ NA trước khi tìm mode để không bị lỗi
  v_without_na <- na.omit(v)
  # Tìm giá trị duy nhất và đếm tần suất của chúng
  uniqv <- unique(v_without_na)
  # Trả về giá trị có tần suất cao nhất
  uniqv[which.max(tabulate(match(v_without_na, uniqv)))]
}

# Áp dụng thay thế NA bằng mode cho cột 'stalk.root'
# (Dựa vào kết quả ở trên, chúng ta biết chỉ cột này có NA)
cat("\nThực hiện thay thế giá trị NA trong cột 'stalk.root' bằng mode...\n")
## 
## Thực hiện thay thế giá trị NA trong cột 'stalk.root' bằng mode...
mode_stalk_root <- get_mode(mushroom_df_processed$stalk.root)
cat("Giá trị mode của cột 'stalk.root' là:", mode_stalk_root, "\n")
## Giá trị mode của cột 'stalk.root' là: b
# Thực hiện việc thay thế
mushroom_df_processed$stalk.root[is.na(mushroom_df_processed$stalk.root)] <- mode_stalk_root

# Đếm lại tổng số NA trong toàn bộ data frame để xác nhận
final_na_count <- sum(is.na(mushroom_df_processed))

cat("\nKiểm tra lại sau khi xử lý...\n")
## 
## Kiểm tra lại sau khi xử lý...
cat("Tổng số giá trị NA còn lại trong bộ dữ liệu:", final_na_count, "\n")
## Tổng số giá trị NA còn lại trong bộ dữ liệu: 0
if (final_na_count == 0) {
  cat("=> Xử lý dữ liệu thiếu thành công!\n")
} else {
  cat("=> CẢNH BÁO: Vẫn còn giá trị NA trong dữ liệu!\n")
}
## => Xử lý dữ liệu thiếu thành công!

Nhận xét:

  • Chỉ có một cột duy nhất chứa giá trị thiếu là stalk.root với 2480 giá trị bị thiếu, chiếm khoảng 30.5% tổng số quan sát.

  • Việc loại bỏ các hàng này sẽ làm mất một lượng lớn dữ liệu. Thay vào đó, một chiến lược hợp lý là thay thế các giá trị thiếu bằng mode (giá trị xuất hiện nhiều nhất) của cột đó. Điều này giúp bảo toàn kích thước mẫu.

Kết quả: Dữ liệu đã được làm sạch, không còn giá trị thiếu.

1.4 Chuyển đổi dữ liệu sang factor

Để R hiểu đúng các biến là biến định tính, chúng ta chuyển đổi tất cả các cột sang kiểu factor.

# Chuyển đổi tất cả các cột sang factor
df <- mushroom_df_processed %>% mutate(across(everything(), as.factor))

# Kiểm tra lại cấu trúc sau khi chuyển đổi
str(df)
## 'data.frame':    8124 obs. of  23 variables:
##  $ class                   : Factor w/ 2 levels "e","p": 2 1 1 2 1 1 1 1 2 1 ...
##  $ cap.shape               : Factor w/ 6 levels "b","c","f","k",..: 6 6 1 6 6 6 1 1 6 1 ...
##  $ cap.surface             : Factor w/ 4 levels "f","g","s","y": 3 3 3 4 3 4 3 4 4 3 ...
##  $ cap.color               : Factor w/ 10 levels "b","c","e","g",..: 5 10 9 9 4 10 9 9 9 10 ...
##  $ bruises                 : Factor w/ 2 levels "f","t": 2 2 2 2 1 2 2 2 2 2 ...
##  $ odor                    : Factor w/ 9 levels "a","c","f","l",..: 7 1 4 7 6 1 1 4 7 1 ...
##  $ gill.attachment         : Factor w/ 2 levels "a","f": 2 2 2 2 2 2 2 2 2 2 ...
##  $ gill.spacing            : Factor w/ 2 levels "c","w": 1 1 1 1 2 1 1 1 1 1 ...
##  $ gill.size               : Factor w/ 2 levels "b","n": 2 1 1 2 1 1 1 1 2 1 ...
##  $ gill.color              : Factor w/ 12 levels "b","e","g","h",..: 5 5 6 6 5 6 3 6 8 3 ...
##  $ stalk.shape             : Factor w/ 2 levels "e","t": 1 1 1 1 2 1 1 1 1 1 ...
##  $ stalk.root              : Factor w/ 4 levels "b","c","e","r": 3 2 2 3 3 2 2 2 3 2 ...
##  $ stalk.surface.above.ring: Factor w/ 4 levels "f","k","s","y": 3 3 3 3 3 3 3 3 3 3 ...
##  $ stalk.surface.below.ring: Factor w/ 4 levels "f","k","s","y": 3 3 3 3 3 3 3 3 3 3 ...
##  $ stalk.color.above.ring  : Factor w/ 9 levels "b","c","e","g",..: 8 8 8 8 8 8 8 8 8 8 ...
##  $ stalk.color.below.ring  : Factor w/ 9 levels "b","c","e","g",..: 8 8 8 8 8 8 8 8 8 8 ...
##  $ veil.type               : Factor w/ 1 level "p": 1 1 1 1 1 1 1 1 1 1 ...
##  $ veil.color              : Factor w/ 4 levels "n","o","w","y": 3 3 3 3 3 3 3 3 3 3 ...
##  $ ring.number             : Factor w/ 3 levels "n","o","t": 2 2 2 2 2 2 2 2 2 2 ...
##  $ ring.type               : Factor w/ 5 levels "e","f","l","n",..: 5 5 5 5 1 5 5 5 5 5 ...
##  $ spore.print.color       : Factor w/ 9 levels "b","h","k","n",..: 3 4 4 3 4 3 3 4 3 3 ...
##  $ population              : Factor w/ 6 levels "a","c","n","s",..: 4 3 3 4 1 3 3 4 5 4 ...
##  $ habitat                 : Factor w/ 7 levels "d","g","l","m",..: 6 2 4 6 2 2 4 4 2 4 ...

Nhận xét: Tất cả các biến giờ đã có kiểu factor với các cấp độ (levels) tương ứng, sẵn sàng cho việc phân tích.

2 Phân tích mô tả một biến (Univariate Analysis)

Phần này tập trung vào việc hiểu phân phối của một vài đặc điểm nấm một cách riêng lẻ.

2.1 Biến class (Loại nấm)

Đây là biến quan trọng nhất, là biến mục tiêu của chúng ta.

class_summary <- df %>%
  count(class) %>%
  mutate(Percentage = n / sum(n))

colnames(class_summary) <- c("Loại nấm (e: ăn được, p: độc)", "Tần số", "Tần suất")

kable(class_summary, align = "c", booktabs = TRUE, digits = 2, caption = "Thống kê tần suất của biến Loại nấm") %>%
  kable_styling(full_width = FALSE, position = "center")
Thống kê tần suất của biến Loại nấm
Loại nấm (e: ăn được, p: độc) Tần số Tần suất
e 4208 0.52
p 3916 0.48

Trực quan hoá

bar_plot <- ggplot(df, aes(x = class, fill = class)) +
  geom_bar() +
  geom_text(stat = "count", aes(label = after_stat(count)), vjust = 0, size = 3) +
  labs(title = "Phân bố loại nấm",
       x = "Loại",
       y = "Số lượng") + 
  theme_minimal() +
  scale_fill_manual(values = c("e" = "skyblue", "p" = "pink"))

class_p <- as.data.frame(table(df$class))
colnames(class_p) <- c("Category", "Count")

pie_plot <- ggplot(class_p, aes(x = "", y = Count, fill = Category)) +
geom_bar(width = 1, stat = "identity") +
coord_polar("y", start=0) +
labs(title = "Tỷ lệ nấm") +
theme_void() +
geom_text(aes(label = scales::percent(Count/sum(Count), accuracy = 0.0001)), position = position_stack(vjust = 0.5), size = 3)

bar_plot + pie_plot

Nhận xét:

  • Bộ dữ liệu khá cân bằng. Nấm ăn được (edible - e) chiếm 51.8% (4208 mẫu), trong khi nấm độc (poisonous - p) chiếm 48.2% (3916 mẫu).

  • Sự cân bằng này là điều kiện lý tưởng để xây dựng các mô hình phân loại vì mô hình sẽ không bị thiên vị về một lớp nào.

2.2 Biến odor (Mùi)

Mùi là một trong những đặc điểm cảm quan quan trọng và dễ nhận biết nhất của nấm, thường được sử dụng trong thực tiễn để phân biệt giữa các loài nấm ăn được và nấm độc. Trong nghiên cứu phân loại nấm, biến odor đại diện cho mùi đặc trưng mà mỗi mẫu nấm phát ra, được phân loại dưới dạng biến định tính (categorical variable) với nhiều giá trị mô tả khác nhau như: almond (hạnh nhân), anise (hồi), creosote (nhựa than), fishy (tanhtanh), foul (hôi), musty (ẩm mốc), pungent (hăng), spicy (cay), none (không mùi),…

Mỗi loại mùi thường phản ánh các hợp chất hóa học được tiết ra từ nấm và có liên quan đến khả năng sinh tồn, tự vệ hoặc thu hút côn trùng phục vụ cho quá trình phát tán bào tử.

Thống kê tần suất của biến Mùi
Mùi Tần số (Số lượng) Tần suất
Mốc 3528 0.4342688
Creosote 2160 0.2658789
Cay 576 0.0709010
Hăng 576 0.0709010
Hạnh nhân 400 0.0492368
Tanh 400 0.0492368
Không mùi 256 0.0315116
Hồi 192 0.0236337
Hôi 36 0.0044313

Nhận xét: Bảng trên cho thấy rõ loại nấm “Không mùi” là loại phổ biến nhất (43.4%), tiếp theo là nấm có mùi “Hôi” (26.6%). Sự chênh lệch lớn về tần suất giữa các loại mùi cho thấy đây là một đặc điểm phân loại tiềm năng.

Trực quan hoá

# Tạo vector ánh xạ
odor_labels <- c(
  "a" = "Hạnh nhân",
  "c" = "Creosote",
  "f" = "Hôi",
  "l" = "Hồi",
  "m" = "Mốc",
  "n" = "Không mùi",
  "p" = "Hăng",
  "s" = "Cay",
  "y" = "Tanh"
)

# Thêm cột odor đầy đủ
df$odor_full <- odor_labels[df$odor]

ggplot(df, aes(x = fct_infreq(odor_full), fill = fct_infreq(odor_full))) +
  geom_bar() +
  geom_text(stat = "count", aes(label = after_stat(count)), vjust = -0.5, size = 3) +
  labs(
    title = "Phân bố mùi của nấm",
    x = "Mùi", y = "Số lượng", fill = "Mùi"
  ) +
  theme_minimal()+theme(axis.text.x = element_text(angle = 45, hjust = 1))

Nhận xét:

  • Loại mùi phổ biến nhất là không mùi (n - none), chiếm một phần rất lớn trong bộ dữ liệu (3528 mẫu).

  • Các mùi khác như hôi (f - foul), tanh (y - fishy), và cay (s - spicy) cũng xuất hiện với tần suất đáng kể.

  • Các mùi như hạnh nhân (a), hồi (l) và hăng (p) có tần suất tương đối cao, trong khi các mùi mốc (m) và creosote (c) rất hiếm. Điều này gợi ý rằng mùi “không mùi” có thể là đặc điểm của nấm ăn được, trong khi các mùi khó chịu có thể là của nấm độc.

df %>%
  count(odor_full) %>%
  ggplot(aes(x = "", y = n, fill = odor_full)) +
  geom_bar(stat = "identity", width = 1) +
  coord_polar("y", start = 0) +
  labs(
    title = "Tỷ lệ các loại mùi của nấm",
    fill = "Mùi"
  ) +
  theme_void() +  # Xóa trục và nền
  geom_text(aes(label = paste0(round(n / sum(n) * 100, 1), "%")),
            position = position_stack(vjust = 0.5), size = 3)

Nhận xét: Biểu đồ tròn thể hiện tỷ lệ phân bố của các loại mùi trong tập dữ liệu nấm cho thấy sự đa dạng đáng kể về đặc điểm mùi hương, trong đó nổi bật là:

  • Loại nấm “Không mùi” chiếm tỷ lệ cao nhất, lên tới 43.4%. Điều này cho thấy gần một nửa số mẫu nấm không có mùi rõ ràng, và đây có thể là đặc điểm phổ biến của nhiều loài nấm ăn được.

  • Loại nấm có mùi “Hôi” đứng thứ hai với 26.6%, cho thấy đây là một loại mùi phổ biến, và có thể liên quan đến các loài nấm độc.

  • Các loại mùi còn lại có tỷ lệ thấp hơn, cụ thể: “Mốc” và “Cay” đều chiếm 7.1%. “Tanh” chiếm 7.0%, một tỷ lệ đáng kể và có thể liên quan đến độc tính. “Hăng”, “Hạnh nhân”, và “Creosote” lần lượt chiếm 4.9%, 3.2% và 2.4%. “Hồi” có tỷ lệ nhỏ nhất, chỉ chiếm 0.4% trong toàn bộ mẫu.

2.3 Biến cap.shape (Hình dạng mũ)

Hình dạng mũ (cap shape) là một trong những đặc điểm hình thái dễ nhận biết nhất và là yếu tố quan trọng hàng đầu trong việc định danh nấm. Hình dạng mũ có thể thay đổi trong quá trình phát triển của nấm, ví dụ như từ hình nón hoặc hình chuông khi còn non thành hình lồi hoặc phẳng khi trưởng thành. Đặc điểm này phản ánh cấu trúc và sự phát triển của loài, giúp phân biệt các nhóm nấm khác nhau. Trong bộ dữ liệu này, hình dạng mũ được mô tả bằng các giá trị như: bell (hình chuông), conical (hình nón), convex (hình lồi), flat (phẳng), knobbed (có núm), và sunken (trũng ở giữa).

Thống kê tần suất của biến Hình dạng mũ
Hình dạng mũ Tần số (Số lượng) Tần suất
Hình trũng 3656 0.4500246
Hình lồi 3152 0.3879862
Hình phẳng 828 0.1019202
Hình chuông 452 0.0556376
Có núm 32 0.0039389
Hình nón 4 0.0004924

Nhận xét: Loại nấm có mũ dạng hình lồi (45.0%) và hình phẳng (38.8%) là hai dạng mũ chiếm đa số tuyệt đối trong tập dữ liệu, trong khi các hình dạng khác như hình trũng và hình nón rất hiếm.

Trực quan hoá

# Tạo vector ánh xạ cho hình dạng mũ
cap_shape_labels <- c(
  "b" = "Hình chuông",
  "c" = "Hình nón",
  "x" = "Hình lồi",
  "f" = "Hình phẳng",
  "k" = "Có núm",
  "s" = "Hình trũng"
)

# Thêm cột cap_shape đầy đủ
df$cap_shape_full <- cap_shape_labels[df$cap.shape]

library(ggplot2)
library(forcats)

ggplot(df, aes(x = fct_infreq(cap_shape_full), fill = fct_infreq(cap_shape_full))) +
  geom_bar() +
  geom_text(stat = "count", aes(label = after_stat(count)), vjust = -0.5, size = 3) +
  labs(
    title = "Phân bố hình dạng mũ của nấm",
    x = "Hình dạng mũ", y = "Số lượng", fill = "Hình dạng mũ"
  ) +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

Nhận xét:

  • Hình dạng lồi (convex - x) và hình dạng phẳng (flat - f) là hai loại phổ biến vượt trội, chiếm phần lớn các mẫu trong bộ dữ liệu, với lần lượt là 3656 và 3152 mẫu. Điều này cho thấy đây là các hình dạng đặc trưng của những loài nấm được thu thập nhiều nhất.

  • Các hình dạng khác như có núm (knobbed - k) và hình chuông (bell - b) xuất hiện với tần suất thấp hơn đáng kể.

  • Hình nón (conical - c) và hình trũng (sunken - s) là hai dạng rất hiếm, chỉ chiếm một vài mẫu. Sự hiếm có này có thể biến chúng thành một đặc điểm nhận dạng quan trọng cho một số loài cụ thể.

2.4 Biến cap.color (Màu sắc mũ)

Màu sắc của mũ nấm là một đặc điểm trực quan nổi bật, nhưng cũng là một trong những đặc điểm biến đổi nhất. Màu sắc có thể bị ảnh hưởng bởi tuổi của nấm, điều kiện môi trường (ánh sáng, độ ẩm), và yếu tố di truyền. Tuy nhiên, một số loài vẫn có màu sắc rất đặc trưng, giúp ích cho việc phân loại. Bộ dữ liệu ghi nhận nhiều màu sắc khác nhau như: brown (nâu), gray (xám), red (đỏ), yellow (vàng), white (trắng), buff (vàng da bò), pink (hồng), cinnamon (quế), purple (tím) và green (xanh lá).

Thống kê tần suất của biến Màu sắc mũ
Màu sắc mũ Tần số (Số lượng) Tần suất
Xanh lá 2284 0.2811423
Xám 1840 0.2264894
Màu quế 1500 0.1846381
Vàng 1072 0.1319547
Trắng 1040 0.1280158
Nâu 168 0.0206795
Hồng 144 0.0177253
Vàng da bò 44 0.0054161
Đỏ 16 0.0019695
Tím 16 0.0019695

Nhận xét: Màu nâu, xám và đỏ là ba màu sắc mũ phổ biến nhất. Sự đa dạng về màu sắc cho thấy đây có thể là một đặc điểm hữu ích nhưng có thể không mang tính quyết định như mùi.

Trực quan hoá

# Tạo vector ánh xạ cho màu sắc mũ
cap_color_labels <- c(
  "n" = "Nâu",
  "b" = "Vàng da bò",
  "c" = "Màu quế",
  "g" = "Xám",
  "r" = "Xanh lá",
  "p" = "Hồng",
  "u" = "Tím",
  "e" = "Đỏ",
  "w" = "Trắng",
  "y" = "Vàng"
)

# Thêm cột cap_color đầy đủ
df$cap_color_full <- cap_color_labels[df$cap.color]

ggplot(df, aes(x = fct_infreq(cap_color_full), fill = fct_infreq(cap_color_full))) +
  geom_bar() +
  geom_text(stat = "count", aes(label = after_stat(count)), vjust = -0.5, size = 3) +
  labs(
    title = "Phân bố màu sắc mũ của nấm",
    x = "Màu sắc mũ", y = "Số lượng", fill = "Màu sắc mũ"
  ) +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

Nhận xét:

  • Màu nâu (brown - n) là màu phổ biến nhất với 2284 mẫu, theo sau là màu xám (gray - g) với 1840 mẫu và đỏ (red - e) với 1500 mẫu.

  • Các màu như vàng (yellow - y) và trắng (white - w) cũng có số lượng đáng kể, cho thấy sự đa dạng về màu sắc trong các loài nấm phổ biến.

  • Ngược lại, các màu như tím (purple - u), xanh lá (green - r), và màu quế (cinnamon - c) rất hiếm gặp. Sự xuất hiện của các màu sắc hiếm này có thể là một dấu hiệu mạnh để xác định một loài nấm cụ thể, dù nó có thể là nấm độc hoặc ăn được. Vì màu sắc phân bố khá đa dạng, riêng đặc điểm này có thể không đủ để kết luận về độc tính.

2.5 Biến cap.surface (Bề mặt mũ)

Bề mặt mũ nấm cung cấp thông tin về kết cấu của nó, một đặc điểm quan trọng khác trong phân loại. Đặc điểm này có thể cảm nhận được bằng cả thị giác và xúc giác. Bề mặt có thể trơn nhẵn, có vảy, có sợi, hoặc có rãnh, tùy thuộc vào loài và điều kiện môi trường. Ví dụ, độ ẩm cao có thể làm cho bề mặt nấm trở nên nhớt và dính. Các loại bề mặt trong bộ dữ liệu gồm: fibrous (sợi), grooves (rãnh), scaly (vảy), và smooth (trơn).

Thống kê tần suất của biến Bề mặt mũ
Bề mặt mũ Tần số (Số lượng) Tần suất
Trơn 3244 0.3993107
Có vảy 2556 0.3146233
Dạng sợi 2320 0.2855736
Có rãnh 4 0.0004924

Nhận xét: Bề mặt có vảy, trơn và dạng sợi có tần suất khá tương đồng và chiếm gần như toàn bộ dữ liệu. Bề mặt có rãnh là cực kỳ hiếm.

Trực quan hoá

# Tạo vector ánh xạ cho bề mặt mũ
cap_surface_labels <- c(
  "f" = "Dạng sợi",
  "g" = "Có rãnh",
  "y" = "Có vảy",
  "s" = "Trơn"
)

# Thêm cột cap_surface đầy đủ
df$cap_surface_full <- cap_surface_labels[df$cap.surface]

ggplot(df, aes(x = fct_infreq(cap_surface_full), fill = fct_infreq(cap_surface_full))) +
  geom_bar() +
  geom_text(stat = "count", aes(label = after_stat(count)), vjust = -0.5, size = 3) +
  labs(
    title = "Phân bố bề mặt mũ của nấm",
    x = "Bề mặt mũ", y = "Số lượng", fill = "Bề mặt mũ"
  ) +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

Nhận xét:

  • Phân bố bề mặt mũ khá không đồng đều. Bề mặt có vảy (scaly - y) là phổ biến nhất, chiếm 3244 mẫu.

  • Bề mặt trơn (smooth - s) và dạng sợi (fibrous - f) cũng xuất hiện với tần suất cao, lần lượt là 2556 và 2320 mẫu. Ba loại bề mặt này chiếm gần như toàn bộ dữ liệu.

  • Loại bề mặt có rãnh (grooves - g) cực kỳ hiếm, chỉ có 4 mẫu trong toàn bộ bộ dữ liệu. Đây là một đặc điểm rất đặc trưng và có thể là một yếu tố quyết định để xác định một loài nấm rất cụ thể.

2.6 Biến habitat (Môi trường sống)

Môi trường sống (habitat) là nơi nấm sinh trưởng và phát triển, tiết lộ nhiều thông tin về hệ sinh thái và nhu cầu dinh dưỡng của chúng. Một số loài nấm có mối quan hệ cộng sinh với các loài cây cụ thể, trong khi những loài khác phát triển trên gỗ mục, lá cây, hoặc trong đồng cỏ. Việc xác định môi trường sống là một bước quan trọng trong quy trình định danh nấm ngoài tự nhiên. Bộ dữ liệu phân loại môi trường sống thành: grasses (đồng cỏ), leaves (lá cây), meadows (bãi cỏ), paths (lối đi), urban (đô thị), waste (chất thải), và woods (rừng).

Thống kê tần suất của biến Môi trường sống
Môi trường sống Tần số (Số lượng) Tần suất
Đồng cỏ 3148 0.3874938
Trên lá 2148 0.2644018
Đô thị 1144 0.1408173
Bãi cỏ 832 0.1024126
Trên chất thải 368 0.0452979
Lối đi 292 0.0359429
Trong rừng 192 0.0236337

Nhận xét: Nấm chủ yếu được tìm thấy trong rừng (38.8%) và trên đồng cỏ (26.4%). Điều này phản ánh môi trường sống tự nhiên và phổ biến nhất của các loài nấm trong bộ sưu tập này.

Trực quan hoá

# Tạo vector ánh xạ cho môi trường sống
habitat_labels <- c(
  "g" = "Đồng cỏ",
  "l" = "Trên lá",
  "m" = "Bãi cỏ",
  "p" = "Lối đi",
  "u" = "Đô thị",
  "w" = "Trên chất thải",
  "d" = "Trong rừng"
)

# Thêm cột habitat đầy đủ
df$habitat_full <- habitat_labels[df$habitat]

ggplot(df, aes(x = fct_infreq(habitat_full), fill = fct_infreq(habitat_full))) +
  geom_bar() +
  geom_text(stat = "count", aes(label = after_stat(count)), vjust = -0.5, size = 3) +
  labs(
    title = "Phân bố môi trường sống của nấm",
    x = "Môi trường sống", y = "Số lượng", fill = "Môi trường sống"
  ) +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

Nhận xét:

  • Biểu đồ cho thấy môi trường sống phổ biến nhất của các loài nấm trong bộ dữ liệu là trong rừng (woods - d), với 3148 mẫu. Điều này phản ánh thực tế rằng rừng là hệ sinh thái đa dạng và lý tưởng cho sự phát triển của nấm.

  • Đồng cỏ (grasses - g) cũng là một môi trường sống quan trọng, đứng thứ hai với 2148 mẫu.

  • Các môi trường như lối đi (paths - p), trên lá (leaves - l) và đô thị (urban - u) có tần suất thấp hơn nhưng vẫn đáng kể.

  • Môi trường sống trên chất thải (waste - w) và trên bãi cỏ (meadows - m) là hiếm gặp nhất. Việc một cây nấm mọc ở đâu có thể là một gợi ý mạnh mẽ về danh tính của nó, và có khả năng liên quan đến việc nó có độc hay không.

3 Ước lượng khoảng và kiểm định giả thuyết cho tỷ lệ

Phần này sử dụng các phương pháp thống kê suy luận để đưa ra kết luận về tổng thể từ dữ liệu mẫu. Đối với mỗi biến, chúng ta sẽ chọn một hạng mục quan tâm (thường là hạng mục phổ biến nhất) để thực hiện:

  1. Ước lượng khoảng tin cậy 95% cho tỷ lệ của hạng mục đó trong tổng thể.

  2. Kiểm định giả thuyết để xem liệu tỷ lệ thực tế của hạng mục đó có khác biệt so với một giá trị giả định hay không.

Phương pháp này giúp chúng ta hiểu rõ hơn về mức độ phổ biến của từng đặc điểm cụ thể.

3.1 Phân tích cho biến class và hạng mục quan tâm: p (Poisonous - Độc)

Chúng ta sẽ ước lượng khoảng tin cậy và kiểm định giả thuyết cho tỷ lệ nấm độc trong tổng thể.

# Lấy số lượng cho từng hạng mục
class_counts <- table(df$class)
total_mushrooms <- sum(class_counts)
cat("Tổng số lượng nấm:", total_mushrooms, "\n")
## Tổng số lượng nấm: 8124
poisonous_count <- class_counts["p"]
cat("Số lượng nấm độc:", poisonous_count, "\n")
## Số lượng nấm độc: 3916

Ước lượng khoảng tin cậy 95% cho tỷ lệ nấm độc

prop_test_poisonous <- prop.test(x = poisonous_count, n = total_mushrooms, conf.level = 0.95)
conf_interval <- prop_test_poisonous$conf.int
cat(sprintf("Khoảng tin cậy 95%% cho tỷ lệ nấm độc là: (%.4f, %.4f)", conf_interval[1], conf_interval[2]))
## Khoảng tin cậy 95% cho tỷ lệ nấm độc là: (0.4711, 0.4930)

Diễn giải: Với độ tin cậy 95%, chúng ta có thể kết luận rằng tỷ lệ thực sự của nấm độc trong tổng thể (mà mẫu này đại diện) nằm trong khoảng từ 47.1% đến 49.3%.

Kiểm định giả thuyết: Tỷ lệ nấm độc có khác 50% không?

Bài toán kiểm định:

\[ \begin{cases} H_0 : \text{Tỷ lệ nấm độc trong tổng thể bằng 0.5 (p = 0.5).}\\ H_1: \text{Tỷ lệ nấm độc trong tổng thể khác 0.5 (p ≠ 0.5).} \end{cases} \]

prop_test_poisonous_H0 <- prop.test(x = poisonous_count, n = total_mushrooms, p = 0.5, alternative = "two.sided")
print(prop_test_poisonous_H0)
## 
##  1-sample proportions test with continuity correction
## 
## data:  poisonous_count out of total_mushrooms, null probability 0.5
## X-squared = 10.424, df = 1, p-value = 0.001244
## alternative hypothesis: true p is not equal to 0.5
## 95 percent confidence interval:
##  0.4711126 0.4929616
## sample estimates:
##         p 
## 0.4820286

Trong đó

  • data: poisonous_count out of total_mushrooms: Cho biết dữ liệu được sử dụng là số lượng nấm độc (poisonous_count) trong tổng số nấm (total_mushrooms). Cụ thể là 3916 trên 8124. null probability 0.5: Cho biết giá trị tỷ lệ mà chúng ta đang kiểm định. Đây chính là giá trị trong giả thuyết không (H₀): H₀: p = 0.5.

  • X-squared = 10.424: Đây là giá trị thống kê kiểm định (test statistic), được tính toán từ dữ liệu. Nó đại diện cho mức độ khác biệt giữa tỷ lệ quan sát được trong mẫu (48.2%) và tỷ lệ giả định trong H₀ (50%). Giá trị X-squared càng lớn, sự khác biệt càng đáng kể.

  • df = 1: Bậc tự do (degrees of freedom). Đối với bài toán kiểm định tỷ lệ một mẫu (với 2 loại kết quả: độc hoặc không độc), bậc tự do luôn luôn là 1.

  • p-value = 0.001244: Nếu tỷ lệ nấm độc trong thực tế đúng là 50%, thì khả năng chúng ta lấy một mẫu ngẫu nhiên và thấy sự chênh lệch lớn như vậy chỉ là 0.1244%. Vì xác suất này rất thấp, chúng ta có lý do để nghi ngờ giả thuyết H₀.

  • Dựa trên dữ liệu mẫu, chúng ta ước tính với độ tin cậy 95% rằng tỷ lệ thực sự của nấm độc trong tổng thể nằm đâu đó trong khoảng từ 47.11% đến 49.30%.

  • p = 0.4820286: Đây là ước lượng điểm (point estimate) cho tỷ lệ nấm độc, được tính trực tiếp từ mẫu. Tỷ lệ nấm độc quan sát được trong mẫu của bạn là 48.20%. Đây là kết quả của phép tính 3916 / 8124.

Kết luận: Giá trị p-value = 0.001244 nhỏ hơn mức ý nghĩa α = 0.05. Do đó, chúng ta bác bỏ giả thuyết \(H_0\). Kết luận rằng tỷ lệ nấm độc trong tổng thể là khác 50%. Cụ thể hơn, dựa vào tỷ lệ mẫu (48.2%), có vẻ như tỷ lệ này thấp hơn 50% một chút.

3.2 Phân tích biến odor (Mùi) và hạng mục quan tâm: n (None - Không mùi)

Chúng ta sẽ phân tích tỷ lệ nấm không có mùi, đây là đặc điểm về mùi phổ biến nhất trong bộ dữ liệu.

odor_counts <- table(df$odor)
total_mushrooms <- sum(odor_counts)
none_odor_count <- odor_counts["n"]

cat("Tổng số lượng nấm:", total_mushrooms, "\n")
## Tổng số lượng nấm: 8124
cat("Số lượng nấm không mùi:", none_odor_count, "\n")
## Số lượng nấm không mùi: 3528

Ước lượng khoảng tin cậy 95% cho tỷ lệ nấm “Không mùi”

prop_test_odor <- prop.test(x = none_odor_count, n = total_mushrooms, conf.level = 0.95)
conf_interval_odor <- prop_test_odor$conf.int
cat(sprintf("Khoảng tin cậy 95%% cho tỷ lệ nấm không mùi là: (%.4f, %.4f)", conf_interval_odor[1], conf_interval_odor[2]))
## Khoảng tin cậy 95% cho tỷ lệ nấm không mùi là: (0.4235, 0.4451)

Diễn giải: Với độ tin cậy 95%, chúng ta có thể kết luận rằng tỷ lệ thực sự của những cây nấm không có mùi trong tổng thể nằm trong khoảng từ 42.35% đến 44.51%.

Kiểm định giả thuyết: Tỷ lệ nấm “Không mùi” có khác 45% không?

Bài toán kiểm định: \[ \begin{cases} H_0 : p = 0.45 \quad (\text{Tỷ lệ nấm không mùi trong tổng thể bằng 45%}) \\ H_1: p \neq 0.45 \quad (\text{Tỷ lệ nấm không mùi trong tổng thể khác 45%}) \end{cases} \]

prop_test_odor_H0 <- prop.test(x = none_odor_count, n = total_mushrooms, p = 0.45, alternative = "two.sided")
print(prop_test_odor_H0)
## 
##  1-sample proportions test with continuity correction
## 
## data:  none_odor_count out of total_mushrooms, null probability 0.45
## X-squared = 8.0596, df = 1, p-value = 0.004526
## alternative hypothesis: true p is not equal to 0.45
## 95 percent confidence interval:
##  0.4234628 0.4451373
## sample estimates:
##         p 
## 0.4342688

Kết luận: Giá trị p-value = 0.004526, nhỏ hơn mức ý nghĩa α = 0.05. Do đó, chúng ta có đủ bằng chứng để bác bỏ giả thuyết \(H_0\). Có thể kết luận rằng tỷ lệ nấm không mùi trong tổng thể là khác 45%. Cụ thể, tỷ lệ này thấp hơn 45% một cách có ý nghĩa.

3.3 Phân tích Biến cap.shape (Hình dạng mũ) và hạng mục quan tâm: x (Convex - Hình lồi)

Chúng ta sẽ phân tích tỷ lệ nấm có mũ hình lồi, hình dạng phổ biến nhất.

shape_counts <- table(df$cap.shape)
convex_shape_count <- shape_counts["x"]

cat("Tổng số lượng nấm:", total_mushrooms, "\n")
## Tổng số lượng nấm: 8124
cat("Số lượng nấm có mũ lồi:", convex_shape_count, "\n")
## Số lượng nấm có mũ lồi: 3656

Ước lượng khoảng tin cậy 95% cho tỷ lệ nấm có “Mũ lồi”

prop_test_shape <- prop.test(x = convex_shape_count, n = total_mushrooms, conf.level = 0.95)
conf_interval_shape <- prop_test_shape$conf.int
cat(sprintf("Khoảng tin cậy 95%% cho tỷ lệ nấm có mũ lồi là: (%.4f, %.4f)", conf_interval_shape[1], conf_interval_shape[2]))
## Khoảng tin cậy 95% cho tỷ lệ nấm có mũ lồi là: (0.4392, 0.4609)

Diễn giải: Với độ tin cậy 95%, tỷ lệ thực sự của nấm có mũ hình lồi trong tổng thể được ước tính nằm trong khoảng từ 43.92% đến 46.09%.

Kiểm định giả thuyết: Tỷ lệ nấm có “Mũ lồi” có khác 50% không?

Bài toán kiểm định: \[ \begin{cases} H_0 : p = 0.5 \quad (\text{Tỷ lệ nấm có mũ lồi trong tổng thể bằng 50%}) \\ H_1: p \neq 0.5 \quad (\text{Tỷ lệ nấm có mũ lồi trong tổng thể khác 50%}) \end{cases} \]

prop_test_shape_H0 <- prop.test(x = convex_shape_count, n = total_mushrooms, p = 0.5, alternative = "two.sided")
print(prop_test_shape_H0)
## 
##  1-sample proportions test with continuity correction
## 
## data:  convex_shape_count out of total_mushrooms, null probability 0.5
## X-squared = 80.96, df = 1, p-value < 2.2e-16
## alternative hypothesis: true p is not equal to 0.5
## 95 percent confidence interval:
##  0.4391712 0.4609255
## sample estimates:
##         p 
## 0.4500246

Kết luận: Giá trị p-value < 2.2e-16, một con số cực kỳ nhỏ. Chúng ta bác bỏ giả thuyết \(H_0\) một cách mạnh mẽ. Tỷ lệ nấm có mũ lồi trong tổng thể chắc chắn là khác (và thấp hơn) 50%.

3.4 Phân tích Biến habitat (Môi trường sống) và hạng mục quan tâm: d (Woods - Trong rừng)

Chúng ta sẽ phân tích tỷ lệ nấm sinh sống trong rừng, môi trường phổ biến nhất.

habitat_counts <- table(df$habitat)
woods_habitat_count <- habitat_counts["d"]

cat("Tổng số lượng nấm:", total_mushrooms, "\n")
## Tổng số lượng nấm: 8124
cat("Số lượng nấm sống trong rừng:", woods_habitat_count, "\n")
## Số lượng nấm sống trong rừng: 3148

3.4.0.1 Ước lượng khoảng tin cậy 95% cho tỷ lệ nấm sống “Trong rừng”

prop_test_habitat <- prop.test(x = woods_habitat_count, n = total_mushrooms, conf.level = 0.95)
conf_interval_habitat <- prop_test_habitat$conf.int
cat(sprintf("Khoảng tin cậy 95%% cho tỷ lệ nấm sống trong rừng là: (%.4f, %.4f)", conf_interval_habitat[1], conf_interval_habitat[2]))
## Khoảng tin cậy 95% cho tỷ lệ nấm sống trong rừng là: (0.3769, 0.3982)

Diễn giải: Với độ tin cậy 95%, tỷ lệ thực sự của nấm sống trong rừng trong tổng thể được ước tính nằm trong khoảng từ 37.69% đến 39.82%.

Kiểm định giả thuyết: Tỷ lệ nấm sống “Trong rừng” có khác 40% không?

Bài toán kiểm định: \[ \begin{cases} H_0 : p = 0.4 \quad (\text{Tỷ lệ nấm sống trong rừng trong tổng thể bằng 40%}) \\ H_1: p \neq 0.4 \quad (\text{Tỷ lệ nấm sống trong rừng trong tổng thể khác 40%}) \end{cases} \]

prop_test_habitat_H0 <- prop.test(x = woods_habitat_count, n = total_mushrooms, p = 0.4, alternative = "two.sided")
print(prop_test_habitat_H0)
## 
##  1-sample proportions test with continuity correction
## 
## data:  woods_habitat_count out of total_mushrooms, null probability 0.4
## X-squared = 5.2423, df = 1, p-value = 0.02204
## alternative hypothesis: true p is not equal to 0.4
## 95 percent confidence interval:
##  0.3768944 0.3982003
## sample estimates:
##         p 
## 0.3874938

Kết luận: Giá trị p-value = 0.02204, nhỏ hơn mức ý nghĩa α = 0.05. Do đó, chúng ta có đủ bằng chứng để bác bỏ giả thuyết \(H_0\). Tỷ lệ nấm sống trong rừng trong tổng thể là khác 40%. Cụ thể hơn, nó thấp hơn 40% một chút nhưng sự khác biệt này vẫn có ý nghĩa thống kê.

4 Phân tích mối quan hệ giữa hai biến định tính (Bivariate Analysis)

Phần này là trọng tâm của báo cáo, nhằm trả lời câu hỏi: Những đặc điểm nào có liên quan đến độc tính của nấm? Chúng ta sẽ đi sâu vào phân tích từng cặp biến quan trọng, áp dụng một chuỗi các phương pháp từ mô tả đến suy luận để hiểu rõ bản chất của mối quan hệ.

4.1 Mối quan hệ giữa class và bruises

Câu hỏi nghiên cứu: Việc một cây nấm có bị thâm khi va chạm hay không ảnh hưởng như thế nào đến khả năng nó là nấm độc?

Bảng tần số chéo và trực quan hóa

# Tạo bảng tần số
class_bruises_table <- table(df$bruises, df$class)

# Gán tên hàng
rownames(class_bruises_table) <- c("Không có vết bầm (f)", "Có vết bầm (t)")

# Hiển thị bảng với kable
kable(class_bruises_table,
      col.names = c("Ăn được (e)", "Độc (p)"),
      caption = "Bảng tần số chéo giữa Vết bầm và Loại nấm") %>%
  kable_styling(bootstrap_options = "striped", full_width = F)
Bảng tần số chéo giữa Vết bầm và Loại nấm
Ăn được (e) Độc (p)
Không có vết bầm (f) 1456 3292
Có vết bầm (t) 2752 624
# Trực quan hóa bằng biểu đồ cột nhóm
ggplot(df, aes(x = bruises, fill = class)) +
  geom_bar(position = "dodge") +
  geom_text(stat = "count", aes(label = after_stat(count)),
            position = position_dodge(width = 0.9), vjust = -0.2, size = 3) +
  labs(title = "Mối quan hệ giữa vết bầm và độc tính",
       x = "Có vết bầm (f: không, t: có)", y = "Số lượng", fill = "Loại nấm") +
  theme_minimal(base_size = 12) +
  scale_fill_brewer(palette = "Set1", labels = c("Ăn được", "Độc"))

Nhận xét:

  • Bảng và biểu đồ cho thấy một sự tương phản rõ rệt. Trong 4748 cây nấm không có vết bầm (f), có tới 3292 cây là độc (chiếm ~ 69%). Ngược lại, trong 3376 cây nấm có vết bầm (t), chỉ có 624 cây là độc (chiếm ~ 18.5%).

  • Điều này gợi ý mạnh mẽ rằng việc có vết bầm là một đặc điểm liên quan đến nấm ăn được.

Kiểm định Chi-bình phương (Chi-squared Test)

Bài toán kiểm định: \[ \begin{cases} H_0: \text{Việc có vết bầm và độc tính của nấm là hai biến độc lập.}\\ H_1: \text{Việc có vết bầm và độc tính của nấm có sự liên quan.} \end{cases} \]

chisq.test(class_bruises_table)
## 
##  Pearson's Chi-squared test with Yates' continuity correction
## 
## data:  class_bruises_table
## X-squared = 2041.4, df = 1, p-value < 2.2e-16

Trong đó

  • X-squared = 2041.4: Đây là giá trị thống kê kiểm định Chi-bình phương. Nó đo lường sự khác biệt giữa tần suất quan sát được (số liệu thực tế trong bảng của bạn) và tần suất kỳ vọng (số liệu mà chúng ta mong đợi nếu hai biến hoàn toàn độc lập với nhau).

  • df = 1: Bậc tự do (degrees of freedom). Đối với kiểm định Chi-bình phương, bậc tự do được tính bằng công thức (số hàng - 1) * (số cột - 1). Vì bảng của bạn là 2x2, nên df = (2 - 1) * (2 - 1) = 1.

  • p-value < 2.2e-16: Đây là kết quả quyết định. Nếu việc có vết bầm và độc tính của nấm thực sự không liên quan gì đến nhau (H₀ đúng), thì xác suất để chúng ta quan sát được một mối liên hệ mạnh mẽ như vậy trong mẫu là gần như bằng không.

Kết luận: Với p-value < 2.2e-16, chúng ta bác bỏ giả thuyết \(H_0\). Có bằng chứng thống kê không thể chối cãi về một mối liên hệ mạnh mẽ giữa vết bầm và độc tính.

Tỷ số nguy cơ (Relative Risk - RR)

Định nghĩa: RR so sánh xác suất (nguy cơ) một cây nấm là độc giữa nhóm “phơi nhiễm” (có vết bầm) và nhóm “không phơi nhiễm” (không có vết bầm).

# Bảng cần có dạng: cột là kết quả, hàng là phơi nhiễm
# Chúng ta định nghĩa "kết quả" là Độc (p) và "phơi nhiễm" là Có vết bầm (t)
rr_table_bruises <- table(df$bruises, df$class)[, c("p", "e")] 

# Tính toán RR
rr_result_bruises <- riskratio.wald(rr_table_bruises, rev="rows") # rev="rows" vì nhóm phơi nhiễm (t) ở hàng 2
rr_result_bruises
## $data
##        
##            p    e Total
##   t      624 2752  3376
##   f     3292 1456  4748
##   Total 3916 4208  8124
## 
## $measure
##    risk ratio with 95% C.I.
##      estimate     lower     upper
##   t 1.0000000        NA        NA
##   f 0.3761878 0.3593876 0.3937733
## 
## $p.value
##    two-sided
##     midp.exact fisher.exact chi.square
##   t         NA           NA         NA
##   f          0            0          0
## 
## $correction
## [1] FALSE
## 
## attr(,"method")
## [1] "Unconditional MLE & normal approximation (Wald) CI"

Trong đó

t là nhóm tham chiếu (reference group) với Risk Ratio = 1.000.

f có risk ratio = 0.37621878, nghĩa là:

  • Nguy cơ một cây nấm là độc khi nó không có vết bầm (f) chỉ bằng 0.376 lần (tức 37.6%) so với nguy cơ của một cây nấm có vết bầm (t)“. Hay, nguy cơ một cây nấm là độc khi nó có vết bầm (t) cao gấp 1 / 0.376 = 2.66 lần so với khi không có vết bầm.

  • Khoảng tin cậy 95%: [0.3594; 0.3938] cho thấy kết quả chính xác và có ý nghĩa thống kê cao (vì không chứa 1).

Diễn giải kết quả Relative Risk:

  • Relative Risk (RR) ≈ 0.3761878. Điều này có nghĩa là: Nguy cơ một cây nấm là độc ở nhóm có vết bầm chỉ bằng 0.3761878 lần so với nguy cơ ở nhóm không có vết bầm.

  • Nói cách khác, việc có vết bầm làm giảm 62.38% (1 - 0.3762) nguy cơ một cây nấm là độc.

  • Khoảng tin cậy 95% cho RR là [0.3594; 0.3938]. Vì khoảng này không chứa 1, nó khẳng định mối liên hệ có ý nghĩa thống kê.

Tỷ số chênh (Odds Ratio - OR)

Định nghĩa: OR so sánh “odds” của việc một cây nấm là độc giữa hai nhóm.

or_result_bruises <- oddsratio.wald(rr_table_bruises, rev="rows")
or_result_bruises$measure
##    odds ratio with 95% C.I.
##      estimate      lower     upper
##   t 1.0000000         NA        NA
##   f 0.1002854 0.09014769 0.1115632

Trong đó

  • t 1.0000000: Nhóm t (có vết bầm) vẫn là nhóm tham chiếu. OR của nó so với chính nó là 1.

  • f 0.1002854: Đây là Tỷ số Chênh (OR) của nhóm f (không có vết bầm) so với nhóm t.

  • Diễn giải: “Tỷ số chênh (odds) một cây nấm là độc khi nó không có vết bầm chỉ bằng 0.10 lần (tức 10%) so với tỷ số chênh khi nó có vết bầm”. Hay, tỷ số chênh một cây nấm là độc khi nó có vết bầm cao gấp 1 / 0.10 = 10 lần so với khi không có vết bầm.

  • lower 0.09014769 upper 0.1115632: Khoảng tin cậy 95% cho OR là từ 0.09 đến 0.11. Khoảng này cũng không chứa 1.0, cho thấy một mối liên hệ rất mạnh và có ý nghĩa thống kê.

Diễn giải kết quả Odds Ratio:

  • Odds Ratio (OR) ≈ 0.1003. Điều này có nghĩa là: Odds của việc một cây nấm là độc khi nó có vết bầm chỉ bằng 0.1003 lần (tức 10.03%) so với odds của việc nó là độc khi không có vết bầm.

  • Khoảng tin cậy 95% cho OR là [0.0901; 0.1116], không chứa 1, khẳng định mối liên hệ có ý nghĩa thống kê rất cao.

4.2 Mối quan hệ giữa class và odor (Mùi)

Câu hỏi nghiên cứu: Mùi của nấm có phải là một yếu tố dự báo mạnh về độc tính hay không?

Bảng tần số chéo và trực quan hóa

# Bảng tần số chéo
class_odor_table <- table(df$odor, df$class)
kable(class_odor_table, caption = "Bảng tần số chéo giữa Mùi và Loại nấm") %>%
  kable_styling(bootstrap_options = "striped")
Bảng tần số chéo giữa Mùi và Loại nấm
e p
a 400 0
c 0 192
f 0 2160
l 400 0
m 0 36
n 3408 120
p 0 256
s 0 576
y 0 576
# Trực quan hóa
ggplot(df, aes(x = fct_infreq(odor_full), fill = class)) +
  geom_bar(position = "dodge") +  # <-- Xếp cạnh nhau
  labs(title = "Mối quan hệ giữa mùi và độc tính của nấm",
       x = "Mùi của nấm", y = "Tần suất", fill = "Loại nấm") +
  theme_minimal(base_size = 12) +
  scale_fill_brewer(palette = "Set1", labels = c("Ăn được", "Độc"))+theme(axis.text.x = element_text(angle = 45, hjust = 1))

Nhận xét: Biểu đồ cho thấy một mối quan hệ phân loại gần như hoàn hảo. Hầu hết các loại mùi chỉ liên quan đến một loại nấm duy nhất (hoặc độc hoặc ăn được), cho thấy đây là yếu tố dự báo quan trọng nhất.

Kiểm định Chi-bình phương (Chi-squared Test) \[ \begin{cases} H_0: \text{Mùi và độc tính là hai biến độc lập.}\\ H_1: \text{Mùi và độc tính có sự liên quan.} \end{cases} \]

chisq.test(class_odor_table)
## 
##  Pearson's Chi-squared test
## 
## data:  class_odor_table
## X-squared = 7659.7, df = 8, p-value < 2.2e-16

Kết luận: Với p-value < 2.2e-16, chúng ta bác bỏ \(H_0\) và kết luận có một mối liên hệ thống kê cực kỳ mạnh mẽ.

Vì biến odor có nhiều hơn 2 hạng mục (9 loại mùi), chúng ta không thể tính một giá trị RR hoặc OR duy nhất cho toàn bộ bảng.

4.3 Mối quan hệ giữa class và spore.print.color (Màu sắc bào tử)

Câu hỏi nghiên cứu: Màu sắc bào tử có liên quan đến việc một cây nấm là độc hay ăn được không?

Bảng tần số chéo và trực quan hóa

class_spore_table <- table(df$spore.print.color, df$class)
kable(class_spore_table, caption = "Bảng tần số chéo giữa Màu sắc bào tử và Loại nấm") %>%
  kable_styling(bootstrap_options = "striped")
Bảng tần số chéo giữa Màu sắc bào tử và Loại nấm
e p
b 48 0
h 48 1584
k 1648 224
n 1744 224
o 48 0
r 0 72
u 48 0
w 576 1812
y 48 0
# Bước 1: Tạo cột mới chứa tên màu
df$spore_color_name <- recode(df$spore.print.color,
  "k" = "Đen",
  "n" = "Nâu",
  "b" = "Da bò",
  "h" = "Sô-cô-la",
  "r" = "Xanh lá",
  "o" = "Cam",
  "u" = "Tím",
  "w" = "Trắng",
  "y" = "Vàng"
)

ggplot(df, aes(x = fct_infreq(spore_color_name), fill = class)) +
  geom_bar(position = "dodge") +
  labs(
    title = "Mối quan hệ giữa màu sắc bào tử và độc tính",
    x = "Màu sắc bào tử",
    y = "Tần suất",
    fill = "Loại nấm"
  ) +
  theme_minimal(base_size = 12) +
  scale_fill_brewer(palette = "Set1", labels = c("Ăn được", "Độc"))+theme(axis.text.x = element_text(angle = 45, hjust = 1))

Nhận xét: Mối quan hệ cũng rất rõ ràng. Màu bào tử r (xanh lá) 100% là độc. Các màu h (sô-cô-la) và w (trắng) có tỷ lệ độc rất cao, trong khi k (đen) và n (nâu) gần như hoàn toàn là ăn được.

Kiểm định Chi-bình phương (Chi-squared Test) \[ \begin{cases} H_0: \text{Màu sắc bào tử và độc tính là hai biến độc lập.}\\ H_1: \text{Màu sắc bào tử và độc tính có sự liên quan.} \end{cases} \]

chisq.test(class_spore_table)
## 
##  Pearson's Chi-squared test
## 
## data:  class_spore_table
## X-squared = 4602, df = 8, p-value < 2.2e-16

Kết luận: Với p-value < 2.2e-16, chúng ta bác bỏ \(H_0\) và kết luận có một mối liên hệ thống kê rất mạnh mẽ.

Tương tự như biến odor, biến spore.print.color cũng có nhiều hơn 2 hạng mục. Do đó, việc tính toán một RR/OR duy nhất là không phù hợp. Kiểm định Chi-bình phương đã cung cấp đủ bằng chứng về mối liên hệ mạnh mẽ giữa hai biến này.

4.4 Mối quan hệ giữa class và habitat (Môi trường sống)

Câu hỏi nghiên cứu: Liệu môi trường sống của nấm (ví dụ: trong rừng, trên cỏ, trên lối đi) có liên quan đến việc nó là nấm độc hay ăn được không?

Bảng tần số chéo và trực quan hóa

Đầu tiên, chúng ta sẽ xem xét sự phân bố của nấm độc và nấm ăn được trên các môi trường sống khác nhau.

# Bảng tần số chéo
class_habitat_table <- table(df$habitat, df$class)

# Gán nhãn hàng cho bảng
rownames(class_habitat_table) <- c("Rừng (d)", "Cỏ (g)", "Lá cây (l)",
                                   "Đồng cỏ (m)", "Lối đi (p)",
                                   "Đô thị (u)", "Bãi rác (w)")

# Tạo bảng trình bày đẹp
kable(class_habitat_table,
      col.names = c("Ăn được (e)", "Độc (p)"),
      caption = "Bảng tần số chéo giữa Môi trường sống và Loại nấm") %>%
  kable_styling(bootstrap_options = "striped")
Bảng tần số chéo giữa Môi trường sống và Loại nấm
Ăn được (e) Độc (p)
Rừng (d) 1880 1268
Cỏ (g) 1408 740
Lá cây (l) 240 592
Đồng cỏ (m) 256 36
Lối đi (p) 136 1008
Đô thị (u) 96 272
Bãi rác (w) 192 0
# Đổi tên môi trường sống thành tiếng Việt (hoặc màu sắc bạn đã gán)
df$habitat_name <- recode(df$habitat,
  "g" = "Trên cỏ",
  "l" = "Trên lá",
  "m" = "Đồng cỏ",
  "p" = "Lối đi",
  "u" = "Đô thị",
  "w" = "Bãi rác",
  "d" = "Trong rừng"
)

# Vẽ biểu đồ với trục tung là tần số (số lượng)
ggplot(df, aes(x = fct_infreq(habitat_name), fill = class)) +
  geom_bar(position = "dodge") +
  labs(
    title = "Mối quan hệ giữa môi trường sống và độc tính của Nấm",
    x = "Môi trường sống",
    y = "Tần số",
    fill = "Loại nấm"
  ) +
  theme_minimal(base_size = 12) +
  scale_fill_brewer(palette = "Set1", labels = c("Ăn được", "Độc"))+theme(axis.text.x = element_text(angle = 45, hjust = 1))

Nhận xét:

  • Môi trường sống nguy hiểm: Nấm mọc trên lối đi (p) có tỷ lệ độc rất cao (khoảng 87%). Nấm mọc ở khu vực đô thị (u) và trên lá cây (l) cũng có tỷ lệ độc chiếm đa số. Đặc biệt, nấm mọc trên bãi rác (w) 100% là nấm ăn được trong bộ dữ liệu này, mặc dù số lượng mẫu rất ít.

  • Môi trường sống phổ biến: Hầu hết các cây nấm được tìm thấy trong rừng (d) và trên cỏ (g). Trong cả hai môi trường này, tỷ lệ nấm ăn được cao hơn một chút so với nấm độc.

Kiểm định Chi-bình phương (Chi-squared Test)

Chúng ta sử dụng kiểm định Chi-bình phương để xác nhận xem mối liên hệ quan sát được có ý nghĩa thống kê hay không.

Bài toán kiểm định: \[ \begin{cases} H_0: \text{Môi trường sống và độc tính là hai biến độc lập.}\\ H_1: \text{Môi trường sống và độc tính có sự liên quan.} \end{cases} \]

chisq.test(class_habitat_table)
## 
##  Pearson's Chi-squared test
## 
## data:  class_habitat_table
## X-squared = 1573.8, df = 6, p-value < 2.2e-16

Kết luận: Kết quả kiểm định cho thấy giá trị p-value < 2.2e-16, một con số cực kỳ nhỏ. Do đó, chúng ta bác bỏ giả thuyết \(H_0\). Có bằng chứng thống kê rất mạnh mẽ để kết luận rằng tồn tại một mối liên hệ có ý nghĩa giữa môi trường sống và độc tính của nấm.

Biến habitat có bảy hạng mục, tạo thành một bảng tần số chéo 7x2, không phải là bảng 2x2, nên không tính toán được Relative và Odds Ratio.

5 Tổng kết và thảo luận

5.1 Tóm tắt những phát hiện chính

Phân tích sâu bộ dữ liệu Mushroom đã mang lại nhiều hiểu biết quan trọng và có ý nghĩa thực tiễn, có thể tóm tắt như sau:

  • Xác định các yếu tố dự báo chủ chốt: Phân tích đã xác định được các đặc điểm có mối liên hệ mạnh mẽ nhất với độc tính của nấm. Mùi (odor) nổi lên như một yếu tố phân loại gần như hoàn hảo. Màu sắc bào tử (spore.print.color) và việc có vết bầm (bruises) cũng là những yếu tố dự báo cực kỳ mạnh mẽ, với các kiểm định Chi-bình phương cho thấy p-value gần như bằng không.

  • Lượng hóa sức mạnh của mối liên hệ: Đối với mối quan hệ giữa vết bầm và độc tính, chúng ta không chỉ xác nhận sự liên quan mà còn lượng hóa được nó. Odds Ratio (OR) ≈ 0.08 cho thấy odds của việc một cây nấm là độc giảm đi hơn 90% nếu nó có vết bầm. Relative Risk (RR) ≈ 0.27 cũng cho thấy nguy cơ tương tự giảm đi khoảng 73%. Cả hai chỉ số đều khẳng định đây là một yếu tố “bảo vệ” quan trọng.

  • Bộ dữ liệu cân bằng: Dữ liệu có sự phân bổ tương đối cân bằng giữa nấm độc (48.2%) và nấm ăn được (51.8%), đây là một điều kiện thuận lợi cho các phân tích và mô hình hóa trong tương lai.

5.2 Hạn chế của phân tích

Mặc dù các kết quả rất rõ ràng, cần nhận thức được những hạn chế của nghiên cứu:

  • Đây là một phân tích cắt ngang, không phải nghiên cứu đoàn hệ. Do đó, Odds Ratio là thước đo hiệp hội phù hợp hơn Relative Risk. Việc diễn giải RR cần thận trọng và chỉ mang tính tham khảo.

  • Việc điền giá trị thiếu trong cột stalk.root bằng mode là một giả định và có thể đã ảnh hưởng nhẹ đến cấu trúc dữ liệu.

  • Các quy tắc rút ra từ bộ dữ liệu này có thể không áp dụng được cho tất cả các loài nấm trên thế giới. Chúng chỉ có giá trị trong phạm vi các loài được mô tả trong “Sổ tay hướng dẫn của Audubon”.

5.3 Đề xuất và ứng dụng

Các phát hiện từ phân tích có thể được chuyển thành các ứng dụng thực tế:

  • Xây dựng quy tắc nhận dạng đơn giản: Có thể tạo ra một hệ thống quy tắc đơn giản nhưng hiệu quả cao. Ví dụ: “Quy tắc 1: Nếu nấm có mùi hôi, tanh, cay… -> ĐỘC. Quy tắc 2: Nếu nấm không mùi, hãy kiểm tra màu sắc bào tử. Nếu màu sô-cô-la hoặc trắng -> Rất có thể là ĐỘC.”

  • Thiết kế ứng dụng nhận dạng: Khi xây dựng một ứng dụng nhận dạng nấm, các câu hỏi về odor, spore.print.color, gill.color, và bruises nên được đặt lên hàng đầu để nhanh chóng thu hẹp khả năng và đưa ra cảnh báo.

5.4 Hướng nghiên cứu tiếp theo

Phân tích này là nền tảng vững chắc cho các nghiên cứu sâu hơn:

  • Xây dựng mô hình dự đoán: Sử dụng các thuật toán học máy như Cây quyết định (Decision Tree), Rừng ngẫu nhiên (Random Forest) hoặc Hồi quy Logistic để xây dựng một mô hình có khả năng dự đoán độc tính với độ chính xác cao. Cây quyết định sẽ rất thú vị vì nó có thể trực quan hóa các quy tắc phân loại.

  • Phân tích tương tác giữa các biến: Nghiên cứu xem liệu sự kết hợp của các đặc điểm (ví dụ: màu mũ VÀ môi trường sống) có tạo ra một yếu tố dự báo mạnh hơn so với từng đặc điểm riêng lẻ hay không.

  • Khám phá các quy tắc kết hợp (Association Rule Mining): Sử dụng các thuật toán như Apriori để tự động tìm ra tất cả các “tổ hợp đặc điểm” thường xuất hiện cùng nhau trong nhóm nấm độc hoặc nấm ăn được.