也是一篇舊文,大概是 2007 被問到這問題時的回答。雖然現在由於計算科技的進步,一切自動化,人工決定分組都沒必要了,但回味一下老骨董的想法有時也是有意思的。
另外,文末再填一小段個人對取組距和組限的問題的淺見。
關於公式:
組數 = 1+log(n)/log(2) = 1+3.22log(n),
經查證, 稱為 Sturges' rule, 是 Sturges (1926) 發表
於 JASA, V.21, pp.65-66.
Sturges 是立基於對稱二項分布近似常態分布, 而取每一
組次數 C(k-1,j), j=0,1,...,k-1.
但為甚麼要取 C(k-1,j) 為組次數, 而不是 m*C(k-1,j)?
因為 C(k-1,j) 與對稱二項分布機率成比例, 而乘一個常
數 m 仍然一樣. 結果變成
組數 = 1+log(n/m)/log(2) = 1+3.22log(n/m).
除了 Sturges' rule, 最受廣泛注意的大概是 Scott(1979)
發表於 Biometrika, V.66, pp.605–610 的公式
組距(h)=3.5s/n^{1/3}, s = standard deviation
是要使 AMISE 最小的組距. 這也是在常態分布下導出的;
但它仍然有弱點被批評 (例如 Wand (1997), AMS, V.51,
pp.59-64.)
Freedman and Diaconis (1981) 提出 h=2(IQR)/n^{1/3},
以四分位距取代標準差. 若資料近常態, 這組距比 Scott
的小, 大約是80%. 不過似乎較少被提到?
以上資料主要參考
Hyndman1, Rob J. (1995)
The problem with Sturges' rule for constructing histograms.
http://www-personal.buseco.monash.edu.au/~hyndman/papers/sturges.pdf
雖然花更多時間的是雜亂未記錄整理而無法在此提及的搜
尋結果.
2022.1.8
公式決定的組數通常不是定論,還要考慮取組距、定組限,才能真正做好資料分組準備。
附帶說明一下:這裡考慮「連續型資料分組」,組限與組界一致,例如 "0-5, 5-10, 10-15,...", 不像某些初統教本取的 "3-4, 5-6,..." 組限 "(3,4), (5,6),..." 而組界 "2.5, 4.5, 6.5, ..." 與組限不一致的情形。
個人認為:分組時組距不是粗暴地:組距= 全距 ÷ 組數,而是一套過程:
初定組數 → 初定組距 → 決定適當組距 → 決定組限 → 確定分組方式
其中「決定適當組距」是一個有點主觀的環節,個人認為組距與組限都需要取「簡單數字」。最主要關鍵是組距,1, 2, 5, 10 等最理想,退而求其次,4, 8 也不錯,再退而求其次,可以考慮如 2.5, 3, 6, 12, 15... 但像 11, 13, 17,... 之類的絕不考慮。至於組限,如是從 0 開始,h, 2h, 3h,... (h 是組距);如不是從 0 開始,也是取 h 的倍數。
學 R 以後,發現 R 的內定分組方式很優。我沒有去查 R 內定方式的細節(註:R 似乎是取首位有效數字是 1, 2, 5,乘上 10 的乘冪為座標軸標記,稱為 pretty number, 而底下可能再 5 等分或對半分為組距。),不過自己寫了一個小函數,當組距初定之後,可以用這函數決定實際組距,進而決定確實分組方式。當然,如果是人工決定分組方式,就用不上了,因為人工選擇更能適當決定如何分組。
由於函數設定取兩位數簡單數字,前面 1,2,...8 根本沒用。當然這函數至少可做兩處簡單修改:
(1) 加一引數決定取一位或二位數為簡單數字, 如 ten=TRUE/FALSE.
(2) 加一引數強制取較大/較小簡單數字,因結果沃定了組數多寡。例如 larger=TRUE/FALSE/NULL.
甘脆也附上修改過的...
simplify <- function(x) {
# t 中是個人認定的「簡單數字」.
t <- c(1,2,2.5,3,4,5,6,8,10,12,15,20,25,30,40,50,60,80,100)
tl <- length(t) # t 的長度
t1 <- t[1:tl-1]
t2 <- t[2:tl]
d <- 10^(floor(log10(x))-1)
x <- x/d # 取 x 的最前兩位有效數字
t3 <- (t1 <= x) & (t2 > x)
t3 <- ifelse(x/t1[t3] <= t2[t3]/x, t1[t3], t2[t3])
return(t3*d)
}
simplify <- function(x, ten=TRUE, larger=NULL) {
# t 中是個人認定的「簡單數字」.
t <- c(1,2,2.5,3,4,5,6,8,10,12,15,20,25,30,40,50,60,80,100)
tl <- length(t) # t 的長度
t1 <- t[1:tl-1]
t2 <- t[2:tl]
d <- 10^(floor(log10(x))-ifelse(ten,1,0))
x <- x/d # 取 x 的最前兩位有效數字
t3 <- (t1 <= x) & (t2 > x)
if(is.null(larger) || is.na(larger)) {
t3 <- ifelse(x/t1[t3] <= t2[t3]/x, t1[t3], t2[t3])
} else
t3 <- ifelse(larger, t2[t3], t1[t3])
return(t3*d)
}