摆脱SAS中数据集的第k个最小值和最大值

Get rid of kth smallest and largest values of a dataset in SAS

我有一个类似这样的数据集

观察|富 |酒吧 |更多

1 | 111 | 11 | 9 2 | 9 | 2 | 2个 .........

我需要扔掉 foo 中最大的 4 个和最小的 4 个(稍后我会用 bar 做类似的事情)才能继续,但我不确定最有效的方法。我知道有最小和最大的函数,但我不明白如何使用它们从已制作的数据集中获取最小的 4 个或最大的 4 个。我想我也可以只删除最小值和最大值 4 次,但这听起来不必要 tedious/time 消耗。有没有更好的方法?

您将需要至少遍历数据集 2 次,但是您这样做 - 一次找出顶部和底部 4 个值是什么,另一个排除这些观察值。

您可以使用 proc univariate 获取前 5 个值和后 5 个值,然后使用其输出为后续数据步骤创建 where 过滤器。这是一个例子:

ods _all_ close;
ods output extremeobs = extremeobs;
proc univariate data = sashelp.cars;
    var MSRP INVOICE;
run;
ods listing;    

data _null_;
    do _N_ = 1 by 1 until (last.varname);
        set extremeobs;
        by varname notsorted;
        if _n_ = 2 then call symput(cats(varname,'_top4'),high);
        if _n_ = 4 then call symput(cats(varname,'_bottom4'),low);    
    end;
run;

data cars_filtered;
    set sashelp.cars(where = (      &MSRP_BOTTOM4 < MSRP < &MSRP_TOP4
                                and &INVOICE_BOTTOM4 < INVOICE < &INVOICE_TOP4
                             )
                    );
run;

如果有多个观察结果并列第 4 大/最小,这将过滤掉所有观察结果。

您可以使用 proc sql 将 foo 的不同值的数量放入宏 var(包括作为不同的空值)。

在您的数据步骤中,您可以使用 first.foo 和宏变量来有选择地仅输出那些不是最小或最大 4 个值的值。

proc sql noprint;
    select count(distinct foo) + count(distinct case when foo is null then 1 end)
    into :distinct_obs from have;
quit;

proc sort data = have; by foo; run;

data want;
    set have;
    by foo;
    if first.foo then count+1;
    if 4 < count < (&distinct_obs. - 3) then output;
    drop count;
run;

PROC RANK 会很容易地为你做到这一点。如果您知道观察的总数,那是微不足道的 - 如果您不知道,则稍微困难一些。

proc rank data=sashelp.class out=class_ranks(where=(height_r>4 and weight_r>4));  
    ranks height_r weight_r;
    var height weight;
run;

例如,这会删除 4 个最小身高或体重的任何观测值。最大的 4 个需要知道最大等级,或者执行第二个处理步骤。

data class_final;
  set class_ranks nobs=nobs;
  if height_r lt (nobs-3) and weight_r lt (nobs-3);
run;

当然,如果您只是删除值,则在数据步骤中完成所有操作,如果满足条件,call missing 变量而不是删除观察值。

我还找到了一种似乎适用于 IML 的方法(我正在通过尝试以不同的方式重做来练习)。我知道我的最大观察数,并且已经按照感兴趣的变量的顺序对其进行了排序。

 PROC IML;
 EDIT data_set; 
 DELETE point {{1, 2, 3, 4,51, 52, 53, 54}; 
 PURGE;
 close data_set; 
 run;

我没有经常使用 IML,但我在阅读文档时偶然发现了这一点。感谢所有回答我问题的人!