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;
image-20200609224023479

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

image-20200609225400130

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

image-20200609231636692
image-20200609231414651

藉由前五筆資料大致了解資料的形態,以及七種魚各自的數量與百分比。其中,體長資料有三種,分別為標準體長 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; 
圖片1
按物種與體長排序過後的資料集
image-20200610001653881
篩選後的最大的二個體長值

↑↑這是最直觀的方法,首先按物種與體長排序,再利用 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.

https://support.sas.com/documentation/tools/sashelpug.pdf


One Reply to “How to find out the max two values of one subject using SAS & R”

如有任何問題和意見歡迎提出