【问题标题】:SAS SCAN Function and Missing ValuesSAS SCAN 函数和缺失值
【发布时间】:2017-11-04 01:00:29
【问题描述】:

我正在尝试开发一个递归程序来使用平面概率来丢失字符串值(例如,如果一个变量具有三个可能的值并且缺少一个观察值,那么缺失的观察值将有 33% 的值被替换为任何值) .

注意:本文的目的不是讨论插补技术的优点。

DATA have;
    INPUT id gender $ b $ c $ x; 
    CARDS; 
    1 M Y . 5 
    2 F N . 4 
    3   N Tall 4 
    4 M   Short 2 
    5 F Y Tall 1
    ;

/* Counts number of categories i.e. 2 */
proc sql; 
    SELECT COUNT(Unique(gender)) into :rescats 
    FROM have 
    WHERE Gender ~= " " ;
    Quit;

%let rescats = &rescats; 
%put &rescats; /*internal check */

/* Collects response categories separated by commas i.e. F,M */
proc sql; 
    SELECT UNIQUE gender into :genders separated by ","
    FROM have
    WHERE Gender ~= " "
    GROUP BY Gender;
    QUIT;

%let genders = &genders;
%put &genders;  /*internal check */

/* Counts entries to be evaluated. In this case observations 1 - 5 */
/* Note CustomerKey is an ID variable */
proc sql; 
    SELECT COUNT (UNIQUE(customerKey)) into :ID
    FROM have
    WHERE customerkey < 6;
QUIT;

%let ID = &ID;
%put &ID; /*internal check */

data want; 
SET have;
DO i = 1 to &ID; /* Control works from 1 to 5 */
seed = 12345; 
/* Sets u to rand value between 0.00 and 1.00 */
u = RanUni(seed);  
/* Sets rand gender to either 1 and 2 */
RandGender = (ROUND(u*(&rescats - 1)) + 1)*1; 
/* PROBLEM Should if gender is missing set string value of M or F */
IF gender = ' ' THEN gender = SCAN(&genders, RandGender, ','); 
END;
RUN;

I SCAN 函数不会在性别范围内创建 F 或 M 观察值。它似乎还创建了一个新的 M 和 F 变量。此外,DO 循环在 CustomerKey 下创建附加条目。有没有办法摆脱这些?

我更喜欢使用循环和宏来解决这个问题。我还不精通数组。

【问题讨论】:

  • 这段代码有很多不同的问题。您遇到的主要问题是哪一个?您尝试解决什么问题?
  • “它似乎还创建了一个新的 M 和 F 变量。”你应该先解决这个问题。你能想到为什么它会创建一个名为 M 的变量吗?
  • 我知道您说插补方法超出了范围,但您为什么不使用其中一种 MI 方法。对于分类,我喜欢使用决策树来预测最佳值。您还应该查看 RNDTBL() 或 Bernoulli 随机变量以直接获取 1 和 0。正如其他人所提到的,到目前为止,您的代码有很多错误。
  • 标题有点偏离,因为这个算法没有递归。
  • 也发布您的示例数据的预期输出。

标签: sas missing-data


【解决方案1】:

这是我尝试整理一下:

/*Changed to delimited input so that values end up in the right columns*/
DATA have;
INPUT id gender $ b $ c $ x;
infile cards dlm=',';
CARDS; 
1,M,Y, ,5
2,F,N, ,4
3, ,N,Tall,4
4,M, ,Short,2
5,F,Y,Tall,1
;

/*Consolidated into 1 proc, addded noprint and removed unnecessary group by*/
proc sql noprint; 
    /* Counts number of categories i.e. 2 */
    SELECT COUNT(unique(gender)) into :rescats 
    FROM have 
    WHERE not(missing(Gender));
    /* Collects response categories separated by commas i.e. F,M */    
    SELECT unique gender into :genders separated by ","
    FROM have
    WHERE not(missing(Gender))
    ;   
Quit;
/*Removed redundant %let statements*/
%put rescats = &rescats; /*internal check */
%put genders = &genders;  /*internal check */

/*Removed ID list code as it wasn't making any difference to the imputation in this example*/

data want; 
SET have;
seed = 12345; 
/* Sets u to rand value between 0.00 and 1.00 */
u = RanUni(seed);  
/* Sets rand gender to either 1 or 2 */
RandGender = ROUND(u*(&rescats - 1)) + 1; 
IF missing(gender) THEN gender = SCAN("&genders", RandGender, ',');  /*Added quotes around &genders to prevent SAS interpreting M and F as variable names*/
RUN;

【讨论】:

  • 谢谢!我是 SAS 编程的新手。我的很多代码都试图消除先前的错误。我今天不会运行这个。但是,这看起来像是一套可行的解决方案,可以修复这个程序并扩展我的技能基础。
  • 不需要运行两次查询来记录它找到了多少条记录。拉取数据后只需保存SQLOBS自动宏变量即可。 %let rescats=&amp;sqlobs;
【解决方案2】:

光环8:

/*Changed to delimited input so that values end up in the right columns*/
DATA have;
INPUT id gender $ b $ c $ x;
infile cards dlm=',';
CARDS; 
1,M,Y, ,5
2,F,N, ,4
3, ,N,Tall,4
4,M, ,Short,2
5,F,Y,Tall,1
;
run;
  • 提示:您可以使用点 (.) 来表示 INPUT 期间字符变量的缺失值。
  • 提示:DATALINES 是 CARDS 的现代替代品。
  • 提示:数据值不必排列,但它对人类有帮助。

因此这也有效:

/*Changed to delimited input so that values end up in the right columns*/
DATA have;
INPUT id gender $ b $ c $ x;
DATALINES; 
1 M Y .     5
2 F N .     4
3 . N Tall  4
4 M . Short 2
5 F Y Tall  1
;
run;
  • 提示:您的技术需要两次遍历数据。
    • 用于确定不同的值。
    • 一秒钟即可应用您的估算。
    • 大多数方法要求每个处理的变量两次通过。哈希方法只能执行两次,但需要更多内存。

确定不同值的方法有很多:SORTING+FIRST.、Proc FREQ、DATA Step HASH、SQL 等等。

提示:有时需要将数据从代码移回数据的解决方案,但可能会很麻烦。通常最干净的方法是让数据保留数据。

例如:如果连接的不同值需要超过 64K,则 INTO 将是错误的方法

提示:数据到代码对于连续值和其他在成为代码时表示不完全相同的值尤其麻烦。

例如:高精度数值、带有控制字符的字符串、带有嵌入引号的字符串等...

这是使用 SQL 的一种方法。如前所述,Proc SURVEYSELECT 更适合实际应用。

Proc SQL;
  Create table REPLACEMENTS as select distinct gender from have where gender is NOT NULL;
  %let REPLACEMENT_COUNT = &SQLOBS;  %* Tip: Take advantage of automatic macro variable SQLOBS;

data REPLACEMENTS;
  set REPLACEMENTS;
  rownum+1; * rownum needed for RANUNI matching;
run;

Proc SQL;
  * Perform replacement of missing values;
  Update have
    set gender = 
      (
        select gender 
        from REPLACEMENTS
        where rownum =  ceil(&REPLACEMENT_COUNT * ranuni(1234))
      )
    where gender is NULL
  ;

%let SYSLAST = have;
DM 'viewtable have' viewtable;

您不必担心没有缺失值的列,因为这些列中不会发生替换。对于缺失的列,候选 REPLACEMENTS 列表排除了缺失,并且 REPLACEMENT_COUNT 对于计算统一替换概率 1/COUNT 是正确的,编码为 rownum = ceil (random)

【讨论】:

  • 为什么我当前的代码在没有 Richard 和 user667489 建议的更改的情况下创建变量“M”和“F”?此外,我当前的代码创建了附加变量,例如“i”、“seed”、“u”和“Rand Gender”。以后如何避免这种情况?
  • 我在这个线程中运行了你的所有代码,但都没有创建新的变量 M 和 F。我建议要么找到产生该输出的特定变体,要么将其作为“失败的努力领先”去学习”。推测是某些代码在非引用范围内使用了 &gender 宏变量,例如错误地尝试在语句中排列 sql generate 宏变量,例如 [array genders &gender; ] 本质上,任何可以在未引用的上下文中编译 M,F 的数据步骤构造都会将这些符号添加到 PDV 并成为输出数据集的一部分。
  • 只是为了确保我理解你,Richard:&gender 没有被引用 (" ")。因此 SAS 不知道如何处理字符串值。 SAS 将值更改为变量。您最好的猜测是 SAS 通过将 M&F 变成变量来解决这个问题。对吗?
  • 这非常接近。在这种特定情况下,“变量”一词的确切含义因句子而异。 SQL INTO 将数据从数据集中移动到宏变量中。核心的宏只是一个文本处理环境。宏变量在运行的 SAS 会话中作为范围符号存在。数据步变量作为程序数据向量中的项目存在,存在于正在运行的数据步(运行时)中。当在源代码中将宏变量(又名符号)解析指定为 & 时,符号的值将放在源代码流中。
  • 推荐阅读“A SAS® Programmer's View of the SAS Supervisor”Ian Whitlock, Westat Inc.”, SUGI 22 Proceedings, 1997 found at www2.sas.com/proceedings/sugi22/ADVTUTOR/PAPER34.PDF. 最后的参考资料也很棒适合认真的 SAS 程序员阅读。
猜你喜欢
  • 1970-01-01
  • 2016-12-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-22
  • 2018-06-18
相关资源
最近更新 更多