這題只看 ESCS 一個連續變項。要做的事就是先把每個人的分數換成 z 分數,再算這組 z 分數的平均數和標準差,最後畫直方圖,看它長什麼樣子。這是在做標準化,看看每個人離這批人的平均有幾個標準差。
算法是:一個人的 ESCS,減掉這份樣本的平均數,再除以標準差。標準差用 R
的 sd(),分母是 n − 1。算完以後,整組 z 分數的平均數會落到
0,標準差會落到 1。題目要報到小數兩位,其實就是在檢查有沒有算對。0
就是這批學生的平均位置,1 就是比平均高一個標準差。
直方圖用來看高峰在哪、左右平不平、兩端有沒有拖很長。z 分數只是把單位換掉,形狀還是原來的 ESCS,所以圖不會因此變成常態分布。
ESCS(Index of economic, social and cultural status)是經濟、社會與文化地位的綜合指數。PISA 把它做成連續分數,OECD 平均大概在 0、標準差大概是 1,所以比平均低的人會出現負數。這裡的負數是正常分數,代表比國際平均低。
因為它是可以有小數的連續分數,我用平均數、標準差和直方圖來看。z 分數就是拿這個分數去減平均、再除以標準差,算出來還是連續的。除的時候標準差要大於 0;這份資料裡大家的 ESCS 有高有低,所以除得下去。
檔案標籤裡的 95、97、98、99 是跳答、不適用、無效、沒作答。這些如果留在資料裡,平均數和標準差會被拉得很誇張,z 分數也會跟著錯,所以分析前先改成遺漏。
haven 用來讀 SPSS 的
.sav。dplyr 用來挑欄位、改代碼、算 z
分數和做摘要。ggplot2 畫直方圖。knitr 的
kable() 把表排整齊。moments
算偏態,看圖是往左歪還是往右歪。
Windows 畫圖時不認得「微軟正黑體」這個名字,所以先用
windowsFonts() 登記成
JhengHei,圖上的中文才不會變成方框。theme_set()
是一次把後面所有圖的底色和字級設好。
library(haven)
library(dplyr)
library(ggplot2)
library(knitr)
library(moments)
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() 把
PISA_tawian2022_trimmed_lab.sav 讀進來。%>%
就是把左邊算出來的東西交給右邊。transmute() 只留
ESCS,其他欄位先拿掉。as.numeric() 把 SPSS
的標籤去掉,後面才能直接算平均數和標準差。if_else() 看到
95、97、98、99 就改成
NA。這份檔其實沒有這些代碼,還是先處理,免得以後被當成真的分數。
raw <- read_sav("PISA_tawian2022_trimmed_lab.sav")
dat <- raw %>%
transmute(
escs = if_else(ESCS %in% c(95, 97, 98, 99), NA_real_, as.numeric(ESCS))
)
cat("有效樣本:", sum(!is.na(dat$escs)),
";遺漏:", sum(is.na(dat$escs)), "\n")
## 有效樣本: 5599 ;遺漏: 0
先用 mean() 和 sd() 算出有效樣本的 ESCS
平均數和標準差,再代進公式。na.rm = TRUE
的意思是算的時候跳過遺漏值。公式裡用的是完整數字,畫面上的小數兩位只是給人看的,不能先四捨五入再去除。
mutate() 幫每個人加一欄 z_escs。z
是正的,代表這個人比這批學生的平均高;是負的,代表比平均低;數字離 0
越遠,就離平均越遠。下面先印出拿來計算的平均數和標準差,再列前 6
筆對一下公式。真正的摘要還是用全部人,這 6 筆只是範例。
小數兩位交給 fmt2()。z
分數的平均數在電腦裡會是一個非常接近 0 的小負數,直接四捨五入會印成
-0.00,看起來像算錯。所以這個函式如果看到
-0.00,就改印成
0.00。代進公式的還是沒有四捨五入的數。
fmt2 <- function(x) {
out <- sprintf("%.2f", round(as.numeric(x), 2))
out[out == "-0.00"] <- "0.00"
out
}
escs_mean <- mean(dat$escs, na.rm = TRUE)
escs_sd <- sd(dat$escs, na.rm = TRUE)
dat <- dat %>%
mutate(z_escs = (escs - escs_mean) / escs_sd)
cat("計算 z 用的 ESCS 平均數:", fmt2(escs_mean),
";標準差:", fmt2(escs_sd), "\n")
## 計算 z 用的 ESCS 平均數: -0.27 ;標準差: 0.91
example_z <- dat %>%
filter(!is.na(z_escs)) %>%
slice_head(n = 6) %>%
transmute(
ESCS = fmt2(escs),
z分數 = fmt2(z_escs)
)
kable(example_z, caption = "前 6 筆有效資料的 ESCS 與 z 分數(小數兩位,只用來對照公式)")
| ESCS | z分數 |
|---|---|
| -0.32 | -0.06 |
| 0.16 | 0.47 |
| -0.57 | -0.34 |
| -0.82 | -0.60 |
| 0.51 | 0.86 |
| 0.29 | 0.62 |
summarise() 把全部有效的 z
分數再算一次平均數和標準差。照公式,這兩個數應該是 0 和
1。最小值和最大值是看圖的兩端有多遠;中位數和偏態是看左右對不對稱。abs(z_escs) <= 1
是在數離平均不超過一個標準差的人,再換成百分比。moments::skewness()
接近 0 就是大致左右對稱;負的是左偏,正的是右偏。
z_stats <- dat %>%
summarise(
人數 = sum(!is.na(z_escs)),
平均數 = mean(z_escs, na.rm = TRUE),
標準差 = sd(z_escs, na.rm = TRUE),
最小值 = min(z_escs, na.rm = TRUE),
最大值 = max(z_escs, na.rm = TRUE),
中位數 = median(z_escs, na.rm = TRUE),
偏態 = moments::skewness(z_escs, na.rm = TRUE),
正負1以內 = mean(abs(z_escs) <= 1, na.rm = TRUE) * 100,
正負2以內 = mean(abs(z_escs) <= 2, na.rm = TRUE) * 100
)
kable(
z_stats %>%
select(人數, 平均數, 標準差, 最小值, 最大值, 中位數, 偏態) %>%
mutate(across(-人數, \(x) fmt2(x))),
caption = "ESCS 的 z 分數摘要(小數兩位)"
)
| 人數 | 平均數 | 標準差 | 最小值 | 最大值 | 中位數 | 偏態 |
|---|---|---|---|---|---|---|
| 5599 | 0.00 | 1.00 | -3.94 | 4.94 | 0.04 | -0.18 |
z
分數是連續的,所以用直方圖看分布,縱軸用次數,直接看每一段有幾個人。binwidth = 0.5
是每一柱寬 0.5 個標準差。這批分數大概從 −4 到
5,這個寬度看得到中間的山峰,兩端也不會被切得太碎。center = 0
是讓其中一柱以 0 當中心,平均數才會落在中間那一柱裡面。
geom_vline() 加兩條線幫忙看。紅虛線是平均數,也就是
0。青點線是中位數。兩條線幾乎疊在一起,就表示平均數和中位數很接近。labs()
是在寫圖的標題和軸的名字。
ggplot(dat, aes(x = z_escs)) +
geom_histogram(
binwidth = 0.5,
center = 0,
fill = "#4C78A8",
color = "white"
) +
geom_vline(xintercept = 0, linetype = "dashed", color = "#E45756") +
geom_vline(xintercept = z_stats$中位數, linetype = "dotted", color = "#72B7B2") +
labs(
title = "ESCS 的 z 分數直方圖",
subtitle = "每一柱寬 0.5;紅虛線 = 平均數 0;青點線 = 中位數",
x = "z 分數",
y = "次數"
)
有效樣本有 5599 人,沒有遺漏。z 分數的平均數是 0.00,標準差是 1.00,跟公式預期的一樣。拿來算 z 的 ESCS 平均數是 -0.27,標準差是 0.91。z 分數最小是 -3.94,最大是 4.94,中位數是 0.04,偏態是 -0.18。有 64.60% 的人落在 −1 到 1,97.04% 的人落在 −2 到 2。
直方圖是單峰,中間凸起來,0 附近最高,而且最高的那一柱在 0 的右邊一點,往兩邊就降下來。平均數比中位數低一點點,偏態是負的,所以整體大致對稱,只是略為左偏。右邊最遠有極少數人接近 5,左邊 −3 到 −1 的人比較多,把平均數輕輕往左拉。大部分人還是集中在 −2 到 2。z 分數只是把原來的 ESCS 改成「平均是 0、標準差是 1」這種單位,形狀還是原來那樣。
這份資料是 PISA 2022 的臺灣樣本,看的是 ESCS,也就是一個連續的社經地位指數。我先把 95、97、98、99 當成遺漏,實際上沒有人是這些代碼,有效人數還是 5599。z 分數用這份樣本的平均數 -0.27 和標準差 0.91 來算,公式是 (ESCS − 平均數) / 標準差。算出來整組的平均數是 0.00、標準差是 1.00,跟標準化該有的結果一樣:0 是這批學生的平均,1 是比平均高一個標準差。
直方圖長得像中間凸起來的一座山,高峰在 0 附近,還稍微偏到右邊,往兩邊下降。中位數是 0.04,偏態是 -0.18,所以大致對稱、略為左偏。大概 64.60% 的人離平均不到一個標準差,大概 97.04% 的人在兩個標準差以內。最小值是 -3.94,最大值是 4.94,兩端還是有離平均比較遠的人。
所以某位學生的 z 分數,就是在說他的經濟、社會與文化地位比這批臺灣學生的平均高或低幾個標準差。圖的形狀跟原來的 ESCS 一樣,標準化只是換了單位。