【问题标题】:Need to hide some fields for one variable, SAS/SQL需要为一个变量隐藏一些字段,SAS/SQL
【发布时间】:2015-10-06 20:08:51
【问题描述】:

我需要向一组人提供一份报告,总结每个人的信息,但只透露每个报告的目标人的姓名。 我所拥有的是:

爱丽丝 4 15% 8 20%
鲍勃 8 30% 6 15%
卡罗尔 4 15% 8 20%
戴夫 4 15% 8 20%
艾琳 4 15% 8 20%

我想要的是:

爱丽丝的总结
爱丽丝 4 15% 8 20%
人 2 8 30% 6 15%
人 3 4 15% 8 20%
人 4 4 15% 8 20%
人5 4 15% 8 20%

鲍勃的总结
人 1 4 15% 8 20%
鲍勃 8 30% 6 15%
人 3 4 15% 8 20%
人 4 4 15% 8 20%
人5 4 15% 8 20%

等等。

我已经尝试了几件事,放弃了一个接一个的数字,我最近的尝试如下:

proc sql;
create table Distinct_People
as select distinct(Name)
from have;
quit;

data People;
set Distinct_People end=no_more;
call symputx('Person'||left(_n_), Name);
if no_more then call symputx('NumPeople', _n_);
run;
quit;

%macro Loop;
%do j=1 %to &NumPeople;
%let Person=Person&j;
data want;
set have;
if Name="&&Person&j" then "&&Person&j";
else "Person";
run;
%end;
%mend Loop;
%Loop;

我知道我可能已经搞砸了上述内容,但确实需要弄清楚如何在 proc sql 或数据步骤中使用 SAS/SQL 来做到这一点。

谢谢!

【问题讨论】:

    标签: sql loops sas data-hiding anonymity


    【解决方案1】:

    不确定您在宏中究竟尝试做什么,也不知道您的结果出了什么问题,我可以在您的代码中发现 3 个错误:

    1.

    data person;
    

    在您的循环中,每次执行循环时,您都会覆盖数据集 person。在下一个循环中,您再次访问被覆盖的数据集。所以最后你只有一个数据集,因为你每次都覆盖它,只剩下 personx 作为名字。所以你必须写data person&j; 来为每个人获取一个数据集。

    2.

     set person;
    

    什么是数据集人?您之前没有在示例中定义它。据我了解您要做什么,您必须在此处使用set have;

    3.

    您忘记覆盖姓名

     if Name="&&Person&j" then 
     name ="&&Person&j";
     else 
     name ="&Person";
    

    或更简单的版本:

    if Name ne "&&Person&j" then 
       name ="&Person";
    

    4.

    不是错误,但您应该使用data _null_; 而不是data people;,因为您使用此数据步骤只是为了生成一些宏变量而不再使用输出,因此无需在此处创建新数据集。而且在数据步过时后退出...


    编辑:

    我今天测试了,这段代码肯定对我有用,如果您仍然收到错误,我猜您有错字或您的环境有问题:

    data have;
    
    input name $ nr1 nr2 $ nr3 n4 $;
     datalines;
     Alice 4 15% 8 20%
    Bob 8 30% 6 15%
    Carol 4 15% 8 20%
    Dave 4 15% 8 20%
    Erin 4 15% 8 20% 
    ;
    run;
    
    proc sql;
    create table Distinct_People
    as select distinct(Name)
    from have;
    quit;
    
    data _null_;
    set Distinct_People end=no_more;
    call symputx('Person'||left(_n_), Name);
    if no_more then call symputx('NumPeople', _n_);
    run;
    quit;
    
    %macro Loop;
    %do j=1 %to &NumPeople;
    data want&j;
    set have;
    if Name ne "&&Person&j" then name = cat("Person",_n_);
    run;
    %end;
    %do j=1 %to &NumPeople;
    proc print data=want&j; 
    title1 " &&Person&j.'s Summary";
    run;
    %end;
    %mend Loop;
    %Loop;
    

    结果:

    【讨论】:

    • 所以代码很差。如何获得我需要的结果?我很困在这里。如前所述,我还将按人输出每组数据。
    • 当您执行我提到的更改时,您会得到什么结果?到目前为止,您的代码看起来还不错,只是一些逻辑错误
    • 我取回了原始表“Want”。看来无论我做什么,我都会把那张桌子拿回来。
    • @s.Cross :发现另一个错误,将其添加到我的原始答案中(第 3 点)。但是,如果您不使用第 1 点,您的代码仍然只会得到一个数据集,其中 max j 的结果
    • 如果我包含 name =,返回错误 180,说 name is out of order
    【解决方案2】:

    我有一个解决方案,在proc sql 中使用联合查询,其中包含用于实现匿名人员姓名和号码的子查询。

    但是正如您所注意到的,每个人都是手动输入到每个选择查询中的。您不愿意为数据集中的 200 人而不是示例中显示的 5 人执行此操作。一种可能性是运行插入查询以反映联合的选择查询:

    proc sql;
    
    create table AnonymousReport As
        SELECT CASE WHEN t1.name = 'Alice' THEN t1.name ELSE CATS('Person',
               (SELECT count(name) + 1 From have t2 
                WHERE t2.name <= t1.name AND t1.name ne t2.name)) END As RptName, 
               t1.Col1Number, t1.Col1Pct, t1.Col2Number, t1.Col2Pct, 'Alice' As ReportToWhom
        FROM have t1
    
        UNION ALL
    
        SELECT CASE WHEN t1.name = 'Bob' THEN t1.name ELSE CATS('Person',
               (SELECT count(name) + 1 From have t2 
                WHERE t2.name < t1.name AND t1.name ne t2.name)) END As RptName, 
                t1.Col1Number, t1.Col1Pct, t1.Col2Number, t1.Col2Pct, 'Bob' As ReportToWhom
        FROM have t1
    
        UNION ALL
    
        SELECT CASE WHEN t1.name = 'Carol' THEN t1.name ELSE CATS('Person',
               (SELECT count(name) + 1 From have t2 
                WHERE t2.name < t1.name AND t1.name ne t2.name)) END As RptName, 
                t1.Col1Number, t1.Col1Pct, t1.Col2Number, t1.Col2Pct, 'Carol' As ReportToWhom
        FROM have t1
    
        UNION ALL
    
        SELECT CASE WHEN t1.name = 'Dave' THEN t1.name ELSE CATS('Person',
               (SELECT count(name) + 1 From have t2 
                WHERE t2.name < t1.name AND t1.name ne t2.name)) END As RptName, 
        t1.Col1Number, t1.Col1Pct, t1.Col2Number, t1.Col2Pct, 'Dave' As ReportToWhom
        FROM have t1
    
        UNION ALL
    
        SELECT CASE WHEN t1.name = 'Erin' THEN t1.name ELSE CATS('Person', 
               (SELECT count(name) + 1 From have t2 
                WHERE t2.name < t1.name AND t1.name ne t2.name)) END As RptName, 
        t1.Col1Number, t1.Col1Pct, t1.Col2Number, t1.Col2Pct, 'Erin' As ReportToWhom
        FROM have t1;
    
    quit;
    

    输出数据集。从这里按最后一列 ReportToWhom 导出每个人的个人报告

    RptName     Col1Number  Col1Pct Col2Number  Col2Pct    ReportToWhom
    Alice       4           15%              8      20%    Alice
    Person2     8           30%              6      15%    Alice
    Person3     4           15%              8      20%    Alice
    Person4     4           15%              8      20%    Alice
    Person5     4           15%              8      20%    Alice
    Person1     4           15%              8      20%    Bob
    Bob         8           30%              6      15%    Bob
    Person3     4           15%              8      20%    Bob
    Person4     4           15%              8      20%    Bob
    Person5     4           15%              8      20%    Bob
    ...
    

    一种可能的解决方案是对数据集的所有行使用串联插入 SQL 查询:

    data concat;
        set have;
        length reptAll $3200;
        by name;
        retain reptAll;
    
        if first.name then reptAll = "";
        unionSQL = "INSERT INTO AnonymousReport (RptName, Col1Number, Col1Pct, Col2Number, Col2Pct, ReportToWhom)
            SELECT CASE WHEN t1.name = '" || name || "' THEN t1.name 
            ELSE CATS('Person', (SELECT count(name) + 1 From have t2 
            WHERE t2.name <= t1.name AND t1.name ne t2.name)) END As RptName, 
            t1.Col1Number, t1.Col1Pct, t1.Col2Number, t1.Col2Pct, '" || name || "' As ReportToWhom
            FROM have t1";
    
        reptAll =  catx('; ', reptAll, unionSQL) ;
    
        call symput('query', reptAll);
        if last.name then output;
    run;
    

    然后将字符串传入proc sql宏:

    %macro runsql;
        proc sql;   
            &query; 
        quit;
    %mend runsql;
    
    %runsql;
    

    在 R 中,我可以使用它的 paste/for loop/apply 函数在几秒钟内完成此操作,但 SAS 语法是另一个世界!

    【讨论】:

    • 虽然这看起来可行,但 kl78 的回复正是我想要的!谢谢你的帮助!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-08-10
    • 2015-06-26
    • 2014-04-08
    • 2019-01-13
    • 2011-05-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多