這次要回答兩個問題:台灣學生的社經地位(ESCS)和數學成績(PV1MATH)有沒有關係?學生年齡(AGE)和社經地位(ESCS)有沒有關係?兩組都是「兩個連續變項,看它們一起變動的方向和強度」,所以我用 Pearson 積差相關係數 r,並且畫散佈圖。
r 的範圍是 −1 到
+1。正的代表一個變項高的時候另一個也傾向高,負的代表一高一低,接近 0
就是幾乎沒有直線關係。r
只描述直線關係,也不能直接說誰造成誰,所以一定要搭配散佈圖一起看。我另外用
cor.test() 做顯著性檢定,並報 95% 信賴區間。
ESCS 是經濟、社會與文化地位的綜合指數,PISA 把它做成連續分數,OECD 平均約 0、標準差約 1,負數代表比國際平均低,是正常分數。PV1MATH 是數學成就的第一個可能值,也是連續分數,不是 0 到 100 的百分制,所以分數可以超過 500。
AGE 是學生的年齡,單位是「年」,帶小數(例如 15.83 歲)。PISA 只抽 15 歲 3 個月到 16 歲 2 個月之間的學生,所以 AGE 的全距很窄(大約 15.33 到 16.33),這會讓它和其他變項的相關很容易很小。
三個變項都是連續的量,適合 Pearson 相關。檔案標籤裡
95、97、98、99(或
9995、9997、9998、9999)是跳答、不適用、無效、沒作答,留在資料裡會讓 r
完全失真,所以分析前先改成遺漏,計算時用成對刪除(use = "complete.obs")。
haven 讀 SPSS 的 .sav;dplyr
整理資料;ggplot2 畫散佈圖;knitr 的
kable() 排版表格。Windows 要先用
windowsFonts()
登記「微軟正黑體」,圖上的中文才不會變成方框,theme_set()
一次設好後面所有圖的外觀。
library(haven)
library(dplyr)
library(ggplot2)
library(knitr)
if (.Platform$OS.type == "windows") {
windowsFonts(JhengHei = windowsFont("Microsoft JhengHei"))
base_family <- "JhengHei"
} else {
base_family <- ""
}
theme_set(
theme_minimal(base_size = 12, base_family = base_family) +
theme(
plot.title = element_text(face = "bold"),
panel.grid.minor = element_blank()
)
)
read_sav() 讀入資料,transmute() 只留
AGE、ESCS、PV1MATH 三欄。as.numeric() 去掉 SPSS
標籤。if_else() 把特殊代碼改成
NA。這份檔其實沒有這些代碼,還是先處理,以免以後被當成真的分數。fmt2()
把數字固定印成小數兩位(四捨五入),並避免印出 -0.00。
fmt2 <- function(x) {
out <- sprintf("%.2f", round(as.numeric(x), 2))
out[out == "-0.00"] <- "0.00"
out
}
raw <- read_sav("PISA_tawian2022_trimmed_lab.sav")
dat <- raw %>%
transmute(
age = if_else(AGE %in% c(9995, 9997, 9998, 9999), NA_real_, as.numeric(AGE)),
escs = if_else(ESCS %in% c(95, 97, 98, 99), NA_real_, as.numeric(ESCS)),
pv1math = if_else(PV1MATH %in% c(9995, 9997, 9998, 9999), NA_real_, as.numeric(PV1MATH))
)
cat("總人數:", nrow(dat), "\n")
## 總人數: 5599
cat("遺漏值(age / escs / pv1math):",
sum(is.na(dat$age)), "/", sum(is.na(dat$escs)), "/", sum(is.na(dat$pv1math)), "\n")
## 遺漏值(age / escs / pv1math): 0 / 0 / 0
先用 summarise()
看每個變項的人數、平均數、標準差、最小值和最大值,確認資料範圍合理,也確認
AGE 的全距確實很窄。
desc <- bind_rows(
dat %>% summarise(變項 = "ESCS", 人數 = sum(!is.na(escs)), 平均數 = mean(escs, na.rm = TRUE),
標準差 = sd(escs, na.rm = TRUE), 最小值 = min(escs, na.rm = TRUE), 最大值 = max(escs, na.rm = TRUE)),
dat %>% summarise(變項 = "PV1MATH", 人數 = sum(!is.na(pv1math)), 平均數 = mean(pv1math, na.rm = TRUE),
標準差 = sd(pv1math, na.rm = TRUE), 最小值 = min(pv1math, na.rm = TRUE), 最大值 = max(pv1math, na.rm = TRUE)),
dat %>% summarise(變項 = "AGE", 人數 = sum(!is.na(age)), 平均數 = mean(age, na.rm = TRUE),
標準差 = sd(age, na.rm = TRUE), 最小值 = min(age, na.rm = TRUE), 最大值 = max(age, na.rm = TRUE))
)
kable(desc %>% mutate(across(-c(變項, 人數), fmt2)), caption = "三個變項的描述統計(小數兩位)")
| 變項 | 人數 | 平均數 | 標準差 | 最小值 | 最大值 |
|---|---|---|---|---|---|
| ESCS | 5599 | -0.27 | 0.91 | -3.84 | 4.21 |
| PV1MATH | 5599 | 537.82 | 109.73 | 219.06 | 917.38 |
| AGE | 5599 | 15.81 | 0.29 | 15.33 | 16.33 |
cor.test() 一次給出 r、t 檢定、自由度、p 值和 95%
信賴區間。method = "pearson" 指定 Pearson
相關。公式左右各放一個變項,R
會自動排除任何一邊遺漏的人。下面把兩組結果整理成一張表;r^2
是決定係數,代表其中一個變項的變異有多少比例可以被另一個用直線解釋。p
值太小時用 < .001 表示。
fmt_p <- function(p) ifelse(p < .001, "< .001", paste0("= ", sub("^0", "", sprintf("%.3f", p))))
ct1 <- cor.test(~ escs + pv1math, data = dat, method = "pearson")
ct2 <- cor.test(~ age + escs, data = dat, method = "pearson")
cor_tab <- tibble(
組別 = c("第一組:ESCS 與 PV1MATH", "第二組:AGE 與 ESCS"),
n = c(ct1$parameter + 2, ct2$parameter + 2),
r = c(ct1$estimate, ct2$estimate),
`r²` = c(ct1$estimate^2, ct2$estimate^2),
`95% CI 下界` = c(ct1$conf.int[1], ct2$conf.int[1]),
`95% CI 上界` = c(ct1$conf.int[2], ct2$conf.int[2]),
t = c(ct1$statistic, ct2$statistic),
p = c(ct1$p.value, ct2$p.value)
)
kable(
cor_tab %>% mutate(across(c(r, `r²`, starts_with("95"), t), fmt2), p = fmt_p(p)),
caption = "Pearson 相關係數摘要(小數兩位)"
)
| 組別 | n | r | r² | 95% CI 下界 | 95% CI 上界 | t | p |
|---|---|---|---|---|---|---|---|
| 第一組:ESCS 與 PV1MATH | 5599 | 0.39 | 0.15 | 0.37 | 0.41 | 31.95 | < .001 |
| 第二組:AGE 與 ESCS | 5599 | 0.00 | 0.00 | -0.03 | 0.02 | -0.33 | = .740 |
r1 <- ct1$estimate; r2 <- ct2$estimate
兩個變項都是連續的,所以用散佈圖:一個點代表一位學生,橫軸放解釋用的變項,縱軸放被解釋的變項。第一組 x 是 ESCS、y 是 PV1MATH;第二組 x 是 AGE、y 是 ESCS。
人數有 5 千多人,點會疊在一起,所以 alpha = 0.25
讓點半透明,越深的地方人越多。geom_smooth(method = "lm")
加一條最小平方法的迴歸直線,用來看趨勢是向上還是向下;再用
labs() 把 r 寫在副標題。
AGE 因為只記到月(約 0.08 歲),點會排成一直條一直條,所以第二組用
geom_jitter()
在水平方向加一點點隨機位移,只是讓點散開,不改變計算結果(相關係數仍用原始資料)。
ggplot(dat, aes(x = escs, y = pv1math)) +
geom_point(alpha = 0.25, color = "#4C78A8", size = 1.2) +
geom_smooth(method = "lm", se = TRUE, color = "#E45756", fill = "#E45756", alpha = 0.2) +
labs(
title = "ESCS 與 PISA 數學成績(PV1MATH)的散佈圖",
subtitle = paste0("Pearson r = ", fmt2(r1), ";紅線 = 線性迴歸線"),
x = "社經地位 ESCS",
y = "數學成績 PV1MATH"
)
ggplot(dat, aes(x = age, y = escs)) +
geom_jitter(width = 0.02, height = 0, alpha = 0.25, color = "#4C78A8", size = 1.2) +
geom_smooth(method = "lm", se = TRUE, color = "#E45756", fill = "#E45756", alpha = 0.2) +
labs(
title = "年齡 AGE 與社經地位 ESCS 的散佈圖",
subtitle = paste0("Pearson r = ", fmt2(r2), ";紅線 = 線性迴歸線(點在水平方向略微抖動)"),
x = "年齡 AGE(歲)",
y = "社經地位 ESCS"
)
第一組:ESCS 與 PV1MATH 的 Pearson 相關係數 r = 0.39(n = 5599,95% CI [0.37, 0.41],p < .001),是中等的正相關;r² = 0.15,也就是數學成績約有 15.43% 的變異可以由 ESCS 的直線關係解釋。
第二組:AGE 與 ESCS 的 Pearson 相關係數 r = 0.00(n = 5599,95% CI [-0.03, 0.02],p = .740),是幾乎沒有的線性相關,未達統計顯著;r² = 0.00,也就是年齡幾乎解釋不了 ESCS 的變異。
第一組(社經地位與數學成績):兩個變項都是連續分數,所以用 Pearson 相關。結果 r = 0.39,方向為正,強度屬於中等。散佈圖也看得出來,點雲整體由左下往右上,迴歸線向上:社經地位越高的台灣學生,PISA 數學成績傾向越高。不過點雲很寬,同樣 ESCS 的學生成績落差很大,社經地位只解釋了約 15.43% 的變異,所以它是一個有關但不是唯一的因素。另外這是相關,不代表社經地位「造成」成績高低。
第二組(年齡與社經地位):結果 r = 0.00,強度幾乎沒有直線關係。散佈圖中不同年齡的點雲高度幾乎一樣,迴歸線接近水平。這個結果合理,因為 PISA 抽的學生都在約 15.3 到 16.3 歲之間,年齡只差不到一歲,而且年齡不是由家庭背景決定的,所以沒有理由會有明顯的直線關係。檢定結果也未達統計顯著,95% 信賴區間包含 0,沒有證據顯示兩者有直線關係。
整體結論:台灣學生的社經地位與數學成績有中等的正相關(r = 0.39),而年齡與社經地位幾乎沒有直線關係(r = 0.00)。