How to find out the max two values of one subject using SAS & R
有時候我們在摘要數據時,會想知道在每個觀測者(subject) 或每個物種 (species) 的多筆資料中,誰是觀測值最高的前幾名 ? (有時候是最低的幾名) 這時候該怎麼做好呢 ?
以 SAS 內建的資料集 Fish 為例。
Sashelp.Fish 這個資料集的內容是一個芬蘭西南部的湖泊-蘭厄爾邁湖 (Längelmävesi) 的魚類調查資料,共蒐集了 159 隻魚,包含了七種魚類的體長、體重等資料。

首先,先認識一下資料的欄位及其屬性:
proc contents data=sashelp.fish; run;
proc contents data=sashelp.fish ORDER=VARNUM; run; /*ORDER = VARNUM →按原本資料的變項順序排列*/
title "The First Five Observations Out of 159";
proc print data=sashelp.fish(obs=5) noobs;
run;
title "The Species Variable";
proc freq data=sashelp.fish;
tables Species;
run;

↑↑可以看到變項名稱、類型和欄位長度等資訊,其中變項名稱是按字母先後順序排列 (預設值)。

↑↑有時候原始數據的變項排列順序是有意義的,例如我們習慣把 ID、年齡性別、身高體重依序排列,方便瀏覽變項,這時候只要在procedure 後面加上 ORDER= VARNUM,結果就會按照原本變項的排列順序呈現。


藉由前五筆資料大致了解資料的形態,以及七種魚各自的數量與百分比。其中,體長資料有三種,分別為標準體長 Length1、尾叉長 Length2 以及全長 Length3。最常使用的為標準體長。

(Citation: http://www.fsl.orst.edu)
大概認識 Fish 這個資料集的內容之後,我們把注意力拉回到問題本身:「如何找出每種魚的標準體長數值 (Length1) 中最大的前二個值 ? 」
方法一:SAS data step
proc sort data=sashelp.fish out=fish nodupkey; by Species descending Length1; run; /*按物種及體長排序,且去重複*/
data fish2a; set fish; by species;
if first.species then count=0;
count+1; /*依序累加*/
run;
data fish3a; set fish2a;
if count<=2;
run;
proc print data=fish3a; run;


↑↑這是最直觀的方法,首先按物種與體長排序,再利用 SAS 的 FIRST.variable 給予每個物種的第一個觀測值 (同時也是體長的最大值) 號碼牌 1 號, 再以 Count +1 (SUM statement) 的方式依序累加排序(號碼牌 2 號、3號…)。
每個物種就會按標準體長 Length1由大至小排序且帶有編號,留下最大的前二個編號即可完成。
如果覺得要二個 data step 才可完成太麻煩的話,也可以簡要的寫成一個步驟:
proc sort data=fish out=fish2d nodupkey; by species descending length1; run;
data fish3d; set fish2d; by species descending Length1;
if first.species then count=0;
count+1;
if count <= 2;
drop count;
run;
proc print data=fish3d; run;
↑↑以上要注意的是,nodupkey option 的用意是去除標準體長相同的值,如果最大值剛好有二個或以上,這時候只留下一筆,可避免最大的二個值是同一個值。
方法二:PROC SQL
proc sql;
select *
from sashelp.fish as a
where (select count(distinct length1) from fish where species=a.species and length1 > a.length1) < 2
order by species, length1;
quit;
PROC SQL 提供了高效簡潔的語法,其概念是利用 Subqueries 的方式來擷取所需的資料。Subqueries 可以把資料集中的每個觀測值拿來與其他資料集的觀測值(或者計算過後的值)互比,甚至如這個例子中的情況,可以與自己本身的資料集互比。除此之外 Subqueries 也可以執行更複雜的 nested subqueries,並且不限定於 SELECT 與 WHERE 語句之中。
方法三:R
另一方面,若是改用R語言則可以直接用以下語法執行:
max.two<-function(x)
{
head(sort(x, decreasing=TRUE), 2)
}
tapply(fish$Length1, fish$Species, max.two)
首先創造出一個新函數稱作max.two,這是用來進行排序(sort)以及取前兩個值(head)的動作,其中的decreasing=TRUE表示為由大排到小(預設為由小到大)。接著再使用tapply來放進待處理之變項以執行我們所創造的函數,tapply的組成如下:
→tapply(待處理之變項, 分類變項, 預計使用之函數)
所得到的結果為一個list:

以上提供幾個方法讓大家可以擷取最大值前兩筆的資料,如有其他方法也會再不定時更新,也歡迎留言提供意見!
參考文獻
Puranen, J. (1917). “Fish Catch data set (1917).” Journal of Statistics Education Data Archive.
One Reply to “How to find out the max two values of one subject using SAS & R”