2017-05-05 61 views
1

假设数据集have包含在outliers数据集中标识的各种异常值。这些异常值需要用缺失值替换,如下所示。自动替换缺失值的偏离值

Obs group replicate  height  weight   bp cholesterol 

    1  1   A   0.406  0.887  0.262  0.683 
    2  1   B   0.656  0.700  0.083  0.836 
    3  1   C   0.645  0.711  0.349  0.383 
    4  1   D   0.115  0.266 666.000  0.015 
    5  2   A   0.607  0.247  0.644  0.915 
    6  2   B   0.172 333.000 555.000  0.924 
    7  2   C   0.680  0.417  0.269  0.499 
    8  2   D   0.787  0.260  0.610  0.142 
    9  3   A   0.406  0.099  0.263  111.000 
    10  3   B   0.981 444.000  0.971  0.894 
    11  3   C   0.436  0.502  0.563  0.580 
    12  3   D   0.814  0.959  0.829  0.245 
    13  4   A   0.488  0.273  0.463  0.784 
    14  4   B   0.141  0.117  0.674  0.103 
    15  4   C   0.152  0.935  0.250  0.800 
    16  4   D  222.000  0.247  0.778  0.941 

 Obs group replicate height weight  bp  cholesterol 

     1  1   A  0.4056 0.8870 0.2615  0.6827 
     2  1   B  0.6556 0.6995 0.0829  0.8356 
     3  1   C  0.6445 0.7110 0.3492  0.3826 
     4  1   D  0.1146 0.2655  .   0.0152 
     5  2   A  0.6072 0.2474 0.6444  0.9154 
     6  2   B  0.1720  .   .   0.9241 
     7  2   C  0.6800 0.4166 0.2686  0.4992 
     8  2   D  0.7874 0.2595 0.6099  0.1418 
     9  3   A  0.4057 0.0988 0.2632  . 
     10  3   B  0.9805  .  0.9712  0.8937 
     11  3   C  0.4358 0.5023 0.5626  0.5799 
     12  3   D  0.8138 0.9588 0.8293  0.2448 
     13  4   A  0.4881 0.2731 0.4633  0.7839 
     14  4   B  0.1413 0.1166 0.6743  0.1032 
     15  4   C  0.1522 0.9351 0.2504  0.8003 
     16  4   D   .  0.2465 0.7782  0.9412 

的“把它做”的做法是在条件与失踪时真正取代手动输入每个变量/值组合。

data have; 
    input group replicate $ height weight bp cholesterol; 
    datalines; 
1 A 0.4056 0.8870 0.2615 0.6827 
1 B 0.6556 0.6995 0.0829 0.8356 
1 C 0.6445 0.7110 0.3492 0.3826 
1 D 0.1146 0.2655 666 0.0152 
2 A 0.6072 0.2474 0.6444 0.9154 
2 B 0.1720 333 555 0.9241 
2 C 0.6800 0.4166 0.2686 0.4992 
2 D 0.7874 0.2595 0.6099 0.1418 
3 A 0.4057 0.0988 0.2632 111 
3 B 0.9805 444 0.9712 0.8937 
3 C 0.4358 0.5023 0.5626 0.5799 
3 D 0.8138 0.9588 0.8293 0.2448 
4 A 0.4881 0.2731 0.4633 0.7839 
4 B 0.1413 0.1166 0.6743 0.1032 
4 C 0.1522 0.9351 0.2504 0.8003 
4 D 222 0.2465 0.7782 0.9412 
; 
run; 

data outliers; 
    input parameter $ 11. group replicate $ measurement; 
    datalines; 
cholesterol 3 A 111 
height  4 D 222 
weight  2 B 333 
weight  3 B 444 
bp   2 B 555 
bp   1 D 666 
    ; 
run; 

编辑:更新outliers使得parameter避免截断和改变measurement是数字类型,以便匹配相应heightweightbpcholesterol。这不应该改变回应。

data want; 
    set have; 

    if group = 3 and replicate = 'A' and cholesterol = 111 then cholesterol = .; 
    if group = 4 and replicate = 'D' and height  = 222 then height  = .; 
    if group = 2 and replicate = 'B' and weight  = 333 then weight  = .; 
    if group = 3 and replicate = 'B' and weight  = 444 then weight  = .; 
    if group = 2 and replicate = 'B' and bp   = 555 then bp   = .; 
    if group = 1 and replicate = 'D' and bp   = 666 then bp   = .; 
run; 

但是,这不利用outliers数据集。如何自动更换过程?


我马上想到IN=运算符,但那不行。这不是整个行需要匹配。也许一个SQL密钥匹配方法可行?但要匹配密钥,我不需要使用where声明吗?然后,我会再次手动写出所有内容。我大概可以创建包含各种ifwhere语句的宏变量,但似乎过多。

+0

检查sas更新声明 – DCR

+0

您是否有每行的唯一标识符? – Reeza

+0

在您的示例中,超过100的任何值都是异常值。这是你的规则吗?将这些级别加载到临时数组中然后使用数组来循环并将其分配为缺失可能更容易。 – Reeza

回答

1

我不认为在这种情况下生成语句过多。由于parameter值表示have数据集中的变量名,因此此处出现复杂性是因为您的异常数据集无法轻松合并。如果可以重新调整异常数据集的方向以便进行1对1合并,则此逻辑将更简单。

让我们假设你不能。有一些方法可以在数据集中使用与另一个变量相对应的变量。

  • 你可以使用一个数组像array params{*} height -- cholesterol;,然后使用vname功能为您遍历数组来比较的parameter变量的值,但是这个被你的情况复杂的,因为你有一个一对多的合并,所以你将不得不保留替换,并且只输出每个组的最后一条记录......所以它变得复杂。
  • 您可以使用proc transpose转置异常值数据,但这会变得冗长,因为您需要每个parameter都需要转置,然后您需要将所有转置数据集合并回have数据集。我用这种方法的主要问题是带有大量转置的代码变得笨拙。
  • 您所创建的宏变量逻辑可能过多。但相对于获得parameter变量的值与在have数据集中的变量名匹配的其他方式,我不认为这样的事情是过度:

    data _null_; 
        set outliers; 
        call symput("outlierstatement"||_n_,"if group = "||group||" and replicate = '"||replicate||"' and "||parameter||" = "||measurement||" then "|| parameter ||" = .;"); 
        call symput("outliercount",_n_); 
    run; 
    
    %macro makewant(); 
        data want; 
         set have; 
         %do i = 1 %to &outliercount; 
          &&outlierstatement&i; 
         %end; 
        run; 
    %mend; 
    
1

的Lorem:

换位是全自动程序化方法的关键。将发生的换位是过滤器数据,而不是原始数据。转置的过滤器数据将具有比原始更少的行。正如John所指出的那样,所需数据的换位可以创建一个非常高的表格,并且在应用过滤器之后必须转换回来。

至于过滤器数据,特定组,复制和参数的过滤器行的存在应足以标记要过滤的单元。这是假定您有一个自动异常值检测系统,并且过滤器值始终与原始值一致。

那么,如果没有代码生成测试墙和分配语句,自动化过滤器应用程序过程需要做些什么?

  1. 移调滤波数据到相同的形式要数据,把它过滤^
  2. 合并想要和由记录键过滤^(它是由组群和复制的)
  3. 阵列过程中的数据元素,寻找过滤条件。

为了您的考虑,请尝试以下SAS代码。混音中添加了错误的过滤器记录。

data have; 
    input group replicate $ height weight bp cholesterol; 
    datalines; 
1 A 0.4056 0.8870 0.2615 0.6827 
1 B 0.6556 0.6995 0.0829 0.8356 
1 C 0.6445 0.7110 0.3492 0.3826 
1 D 0.1146 0.2655 666 0.0152 
2 A 0.6072 0.2474 0.6444 0.9154 
2 B 0.1720 333 555 0.9241 
2 C 0.6800 0.4166 0.2686 0.4992 
2 D 0.7874 0.2595 0.6099 0.1418 
3 A 0.4057 0.0988 0.2632 111 
3 B 0.9805 444 0.9712 0.8937 
3 C 0.4358 0.5023 0.5626 0.5799 
3 D 0.8138 0.9588 0.8293 0.2448 
4 A 0.4881 0.2731 0.4633 0.7839 
4 B 0.1413 0.1166 0.6743 0.1032 
4 C 0.1522 0.9351 0.2504 0.8003 
4 D 222 0.2465 0.7782 0.9412 
5 E 222 0.2465 0.7782 0.9412 /* test record for filter value misalignment test */ 
; 
run; 

data outliers; 
    length parameter $32; %* <--- widened parameter so it can transposed into column via id; 
    input parameter $ group replicate $ measurement ; %* <--- changed measurement to numeric variable; 
    datalines; 
cholesterol 3 A 111 
height  4 D 222 
height  5 E 223 /* test record for filter value misalignment test */ 
weight  2 B 333 
weight  3 B 444 
bp   2 B 555 
bp   1 D 666 
    ; 
run; 

data want; 
    set have; 

    if group = 3 and replicate = 'A' and cholesterol = 111 then cholesterol = .; 
    if group = 4 and replicate = 'D' and height  = 222 then height  = .; 
    if group = 2 and replicate = 'B' and weight  = 333 then weight  = .; 
    if group = 3 and replicate = 'B' and weight  = 444 then weight  = .; 
    if group = 2 and replicate = 'B' and bp   = 555 then bp   = .; 
    if group = 1 and replicate = 'D' and bp   = 666 then bp   = .; 
run; 

/* Create a view with 1st row having all the filtered parameters 
* This is necessary so that the first transposed filter row 
* will have the parameters as columns in alphabetic order; 
*/ 

proc sql noprint; 
    create view outliers_transpose_ready as 
    select distinct parameter from outliers 
    union 
    select * from outliers 
    order by group, replicate, parameter 
; 

    /* Generate a alphabetic ordered list of parameters for use 
    * as a variable (aka column) list in the filter application step */ 
    select distinct parameter 
    into :parameters separated by ' ' 
    from outliers 
    order by parameter 
; 
quit; 

%put NOTE: &=parameters; 

/* tranpose the filter data 
* The ID statement pivots row data into column names. 
* The prefix=_filter_ ensure the new column names 
* will not collide with the original data, and can be 
* the shortcut listed with _filter_: in an array statement. 
*/ 

proc transpose data=outliers_transpose_ready out=outliers_apply_ready prefix=_filter_; 
    by group replicate notsorted; 
    id parameter; 
    var measurement; 
run; 

/* Robust production code should contain a bin for 
* data that does not conform to the filter application conditions 
*/ 

data 
    want2(label="Outlier filtering applied" drop=_i_ _filter_:) 
    want2_warnings(label="Outlier filtering: misaligned values") 
; 
    merge have outliers_apply_ready(keep=group replicate _filter_:); 
    by group replicate; 

    /* The arrays are for like named columns 
    * due to the alphabetic ordering enforced in data and codegen preparation 
    */ 
    array value_filter_check _filter_:; 
    array value &parameters; 

    if group ne .; 

    do _i_ = 1 to dim(value); 

    if value(_i_) EQ value_filter_check(_i_) then 
     value(_i_) = .; 
    else 
    if not missing(value_filter_check(_i_)) AND 
     value(_i_) NE value_filter_check(_i_) 
    then do; 
     put 'WARNING: Filtering expected but values do not match. ' group= replicate= value(_i_)= value_filter_check(_i_)=; 
     output want2_warnings; 
    end; 
    end; 

    output want2; 
run; 

确认您的需求并自动将want2同意。

proc compare noprint data=want compare=want2 outnoequal out=diffs; 
    by group replicate; 
run; 

享受您的SAS

1

你可以使用一个哈希表。使用outlier数据集加载散列表,并将parameter-group-replicate定义为键。然后读入数据,并在读取每条记录时,检查每个变量以查看散列表中是否可以找到参数组复制的组合。我想下面的作品(我不是哈希专家):

data want; 
    if 0 then set outliers (keep=parameter group replicate); 
    if _N_ = 1 then 
    do; 
     declare hash h(dataset:'outliers') ; 
     h.defineKey('parameter', 'group', 'replicate') ; 
     h.defineDone() ; 
    end; 
    set have ; 

    array vars {*} height weight bp cholesterol ; 

    do i=1 to dim(vars); 
    parameter=vname(vars{i}); 
    if h.check()=0 then call missing(vars{i}); 
    end; 

    drop i parameter; 
run; 
+0

我不认为'if 0 then'声明是必要的,因为你在散列声明语句中引用'dataset:outliers',但我可能会误解。使用散列很难确定,因为文档本身包含错误。 –

+0

在执行defineDone()之前,哈希表中的变量需要位于PDV中。如果你注释掉'if 0 then'就会出错。即使在指定数据集时,DECLARE语句也不能添加变量PDV。大多数SAS文档使用长度语句向PDV添加变量,但我认为'如果0则'更容易。 – Quentin

+0

我无法重现您提到的错误。我观察到的行为是相同的或不存在“if 0 then”。我的理解是'declare hash'使用'dataset:'参数来实例化。也就是说,'dataset:'参数中提供的数据集用于定义'defineKey'语句中列出的键。 https://support.sas.com/documentation/cdl/en/lecompobjref/69740/HTML/default/viewer.htm#p00ilfw5pzcjvtn1nfya9863fozd.htm#p04u7k0pr34yxjn13idjdtdpp8o4 –

1

我喜欢@约翰的建议:

You could use an array like array params{*} height -- cholesterol; and then use the vname function as you loop through the array to compare to the value in the parameter variable, but this gets complicated in your case because you have a one to many merge, so you would have to retain the replacements and only output the last record for each by group... so it gets complicated.

在一对多合并我会避免从在数据集重新编码变量

一般唯一的,因为变量保留在BY组中。但在这种情况下,它运行良好。

proc sort data=outliers; 
    by group replicate; 
run; 

data want (keep=group replicate height weight bp cholesterol); 
    merge have (in=a) 
     outliers (keep=group replicate parameter in=b) 
    ; 
    by group replicate; 

    array vars {*} height weight bp cholesterol ; 

    do i=1 to dim(vars); 
    if vname(vars{i})=parameter then call missing(vars{i}); 
    end; 

    if last.replicate; 
run; 
+0

我不清楚你为什么决定使用'in ='?你能澄清吗? –

+0

子女只是习惯。我会经常添加类似'if a = 0,然后放置'ERROR:'(group replicate)(=);'以防止异常数据集中的任何意外。 – Quentin

+0

良好的通话。就我而言,“异常值”数据集来自同事。我没有考虑异常值创建是自动的并且可能返回一个空白数据集的情况。 –

0

谢谢@John提供了一个概念验证。我的实现有点不同,我认为值得为后人单独写一篇文章。我采用宏观变量方法,因为我觉得它是最直观的,是一个简单的文本替换。但是,由于宏变量只能包含65534个字符,因此可以想象,可能有足够的异常值超出此限制。在这种情况下,任何其他解决方案都将成为很好的选择。请注意,put语句使用类似best32.的值很重要,如果太短,则会截断值。

如果你渴望拥有包含if语句(也许是验证)的数据集,只需取下into :声明并放置一个create table statements as线在PROC SQL步骤开始。

data have; 
    input group replicate $ height weight bp cholesterol; 
    datalines; 
1 A 0.4056 0.8870 0.2615 0.6827 
1 B 0.6556 0.6995 0.0829 0.8356 
1 C 0.6445 0.7110 0.3492 0.3826 
1 D 0.1146 0.2655 666 0.0152 
2 A 0.6072 0.2474 0.6444 0.9154 
2 B 0.1720 333 555 0.9241 
2 C 0.6800 0.4166 0.2686 0.4992 
2 D 0.7874 0.2595 0.6099 0.1418 
3 A 0.4057 0.0988 0.2632 111 
3 B 0.9805 444 0.9712 0.8937 
3 C 0.4358 0.5023 0.5626 0.5799 
3 D 0.8138 0.9588 0.8293 0.2448 
4 A 0.4881 0.2731 0.4633 0.7839 
4 B 0.1413 0.1166 0.6743 0.1032 
4 C 0.1522 0.9351 0.2504 0.8003 
4 D 222 0.2465 0.7782 0.9412 
; 
run; 

data outliers; 
    input parameter $ 11. group replicate $ measurement; 
    datalines; 
cholesterol 3 A 111 
height  4 D 222 
weight  2 B 333 
weight  3 B 444 
bp   2 B 555 
bp   1 D 666 
    ; 
run; 

proc sql noprint; 
    select 
    cat('if group = ' 
     , strip(put(group, best32.)) 
     , " and replicate = '" 
     , strip(replicate) 
     , "' and " 
     , strip(parameter) 
     , ' = ' 
     , strip(put(measurement, best32.)) 
     , ' then ' 
     , strip(parameter) 
     , ' = . ;') 
    into : listIfs separated by ' ' 
    from outliers 
    ; 
quit; 

%put %quote(&listIfs); 

data want; 
    set have; 
    &listIfs; 
run;