【问题标题】:Creating vertical detail tables创建垂直明细表
【发布时间】:2012-07-24 07:47:01
【问题描述】:

我正在寻找一种在 SAS 中创建垂直表的方法,其中每个变量都被视为行(而不是每行都是观察)。

例如,假设我有一些公司的数据,其中一些比其他更重要。很容易让 proc 报告吐出一个包含几个变量的汇总表,如下所示:

Name Price Shares MarketCap
co1    $5    100    $500
co2    $1    100    $100
co3    $2    200    $400

在此之后我想做的是为每个公司打印一页详细信息,该页面本质上是一个表格,其中有一列用于描述,一列用于价值(可能还有第三列用于计算)。

Company 1

   Location:   CA
        CEO:   Bob Johnson
   Industry:   Semiconductors

     Shares:   100
Share Price:   $5
 Market Cap:   $500

我能想到在 SAS 中执行此操作的唯一方法是基本上转置所有内容,创建一个具有标签(位置、股票价格等)的新字符变量和具有该值的第二个字符变量,然后生成由公司提供的两列报告,以获取每个页面。这很麻烦,因为一些值是数字,而另一些是字符,因此要让它们显示在一列上,需要创建一个新的字符变量并用数字变量的文本版本填充它。

我认为必须有一种更简单的方法来创建垂直表格,因为有很多简单的方法可以创建水平表格。

【问题讨论】:

    标签: sas


    【解决方案1】:

    也许我遗漏了一些东西,但您没有回答您自己的问题吗?它应该很简单:

    创建一些示例数据。确保每列都应用了格式和标签:

    data mydata;
      attrib name length=$10 format=$10.    label='FirstName'
             blah length=6   format=comma6. label='SomeValue';
    
      bygroup = 1; name = "Rob" ; blah = 1000; output;
      bygroup = 2; name = "Pete"; blah = 100 ; output;
    run;
    

    转置数据使其变高:

    proc transpose data=mydata out=trans;
      by bygroup;
      var _all_;
    run;
    

    打印出来:

    data _null_;
      set trans2;
      by bygroup;
    
      if first.bygroup then do;
        put bygroup;
        put "------";
      end;
      put _label_ ":" value;
    run;
    

    结果:

    1
    ------
    FirstName :Rob
    SomeValue :1,000
    2
    ------
    FirstName :P
    SomeValue :100
    

    【讨论】:

    • 这行得通(并且可以用 print 或 report 而不是 put 语句看起来不错),但我正在寻找一种更清洁的方法。基本上我可以用proc print; by bygroup; var var1 var2 var3 var4; 制作一个好看的(假设我已经设置了样式)水平版本,我希望有一些方法可以强制 proc print/report/tabulate 制作这种垂直表格。
    【解决方案2】:

    不如选择其中一种解决方案...在 Bases SAS 中打开一个表以查看它。转到视图-> 表单视图。这会将其置于您要求的布局中。它可能看起来不完全是您想要的方式,但它是一个快速的选择。

    另一种方法是自己编写。创建一个宏,该宏将数据集和您想要指定的任何其他参数作为参数,并使用 ODS、put 语句或您想要的任何其他技术显示它。

    我不知道 SAS 中有任何其他内置方法可以做到这一点。

    【讨论】:

    • 是的,谢谢。我真的在寻找生产质量的输出。您的其他答案是第一次通过(所以我会接受),但我无法获得超级有吸引力的输出。有了无限的时间,我可以推动 ODS 制作我想要的任何东西,但我真的希望 proc 报告中有一些漂亮的隐藏功能可以快速翻转输出......哦,好吧。现在我只是将数据发送到 csv 并使用一系列技巧来填充 50 个不同的 excel 表。
    【解决方案3】:

    还有这个解决方案可能更适合您的需求。

    首先创建一个将用作模板的 HTML 文件。无论您想在哪里放置值,都可以使用宏变量作为占位符,如下所示:

    <html>
    <h1> My title is &title </h1><br>
    Name: &name <br>
    Value of Blah: &blah
    </html>
    

    让它看起来像你喜欢的那样有吸引力。

    接下来创建一个将导入 HTML 模板的宏,将占位符替换为实际值并将结果保存到新文件中:

    /*****************************************************************************
    **  PROGRAM: MACRO.RESOLVE_FILE.SAS
    **
    **  READS IN A FILE AND REPLACES ANY MACRO REFERENCES IN THE FILE WITH THE 
    **  ACTUAL MACRO VALUES.  EG.  IF THE FILE WAS AN HTML FILE AND IT CONTAINED 
    **  THE FOLLOWING HTML:
    **
    **    <TITLE>&HTML_TITLE</TITLE>
    **
    **  THEN THE PROGRAM WOULD READ THE FILE IN AND RESOLVE IT SO THAT THE OUTPUT
    **  LOOKED LIKE THIS:
    **
    **    <TITLE>ROB</TITLE>
    **  
    **  ... WHEN THE MACRO VARIABLE "HTML_TITLE" EXISTED AND CONTAINED A VALUE OF 
    **  "ROB".  THIS IS USEFUL WHEN YOU NEED TO CREATE "DYNAMIC" HTML FILES FROM 
    **  SAS BUT DONT WANT TO DO IT FROM A DATASTEP USING PUT STATEMENTS.  DOING
    **  IT THIS WAY IS MUCH CLEANER.
    **
    **  PARAMETERS: NONE
    **
    ******************************************************************************
    **  HISTORY:
    **  1.0 MODIFIED: 22-JUL-2010  BY:RP
    **  - CREATED. 
    **  1.1 MODIFIED: 18-FEB-2011  BY:RP
    **  - ADDED LRECL OF 32K TO STOP TRUNCATION
    *****************************************************************************/
    %macro resolve_file(iFileIn=, iFileOut=);
      data _null_;
        length line $32767;
        infile "&iFileIn" truncover lrecl=32767;
        file   "&iFileOut" lrecl=32767;
        input; 
        line = resolve(_infile_);
        len = length(line);
        put line $varying. len;
      run;
    %mend;
    

    创建一些测试数据。还要创建一些命令来调用上述宏并传入数据集中的值:

    data mydata;
      attrib name length=$10 format=$10.    label='FirstName'
             blah length=6   format=comma6. label='SomeValue'
             cmd1  length=$1000
             cmd2  length=$1000
             ;
    
      title = 1; 
      name = "Rob" ; 
      blah = 1000; 
      cmd1 = cats('%let title=',title,';',
                  '%let name=',name,';',
                  '%let blah=',blah,';');
      cmd2 = cats('%resolve_file(iFileIn=c:\template.html, iFileOut=c:\result',title,'.html);');
      output;
    
      title = 2; 
      name = "Pete"; 
      blah = 100 ; 
      cmd1 = cats('%let title=',title,';',
                  '%let name=',name,';',
                  '%let blah=',blah,';');
      cmd2 = cats('%resolve_file(iFileIn=c:\template.html, iFileOut=c:\result',title,'.html);');
      output;
    run;
    

    使用call execute 运行我们在之前数据集中创建的 cmd1 和 cmd2。我们必须一次只在 1 行上执行调用执行,以便使用正确的宏变量,所以使用循环来执行。首先使用您喜欢的技术计算数据集中的行数:

    proc sql noprint;      
      select count(*) into :nobs from mydata;
    quit;
    

    然后遍历数据集,一次执行一个命令,并将每一行构建到一个新文件中:

    %macro publish;
      %local tmp;
      %do tmp = 1 %to &nobs;
        data _null_;
          set mydata(firstobs=&tmp obs=&tmp);
          call execute (cmd1);
          call execute (cmd2);
        run;
      %end;
    %mend;
    %publish;
    

    这应该可以解决问题。

    【讨论】:

    • 这真的很酷。我不知何故错过了这个答案(并通过导出数据然后使用一堆 Excel 工作簿生成表格来解决我的问题)但我很快将不得不再次做类似的事情,并且一定会尝试这种方法。
    • 谢谢 - 以防你最终使用它输出文件)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-02-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多