【问题标题】:Transposing rows to columns in SAS or SQL在 SAS 或 SQL 中将行转置为列
【发布时间】:2016-05-18 21:57:32
【问题描述】:

我是一个新的 SAS/SQL 用户,我有一个数据集,我需要将一些行转换为列。我认为有一种更快或更简单的方法可以做到这一点,我想给大家一些建议。我的示例将更好地解释我的问题:

这是我拥有的数据集:

Month   ID     Car      Claim_Type   Cost_of_claim
  1    1243   Ferrari    Collision      12,000
  2    6437   Peugeot    Fire           50,000
  5    0184   Citroen    Stole           3,000
  9    1930   Fiat       Medical         1,000
  3    2934   GM         Liability      20,000

我需要创建一个这样的数据集:

Month   ID     Car    Collision   Fire    Stole   Medical Liability
1    1243   Ferrari    12,000       0       0       0         0 
2    6437   Peugeot       0      50,000     0       0         0         
5    0184   Citroen       0         0      3,000    0         0
9    1930   Fiat          0         0       0     1,000       0
3    2934   GM            0         0       0       0      20,000

我只是将一些行转换为列...

我正在考虑做类似的事情来创建我的新数据集:

proc sql;
select Month, ID, CAR
  case when Claim_Type = 'Collision' then Cost_of_claim end Collision,
  case when Claim_Type = 'Fire'      then Cost_of_claim end Fire,
  case when Claim_Type = 'Stole'     then Cost_of_claim end Stole,
  case when Claim_Type = 'Medical'   then Cost_of_claim end Medical,
  case when Claim_Type = 'Liability' then Cost_of_claim end Liability
from my_table;

问题是数据量很大,我认为这种方式可能效率不高。此外,在我的数据集中,我有更多的列和行,并且不想在case when 语句中输入所有可能性,因为维护代码似乎并不容易(或用户友好)。

有人可以帮我解决这个问题吗?

【问题讨论】:

    标签: sql sas transpose


    【解决方案1】:

    PROC TRANSPOSE 应该做你想做的事。

    data test;
      input Month   ID     Car $     Claim_Type : $12. Cost_of_claim;
      cards;
      1    1243   Ferrari    Collision      12000
      2    6437   Peugeot    Fire           50000
      5    0184   Citroen    Stole           3000
      9    1930   Fiat       Medical         1000
      3    2934   GM         Liability      20000
    run;
    
    proc transpose data=test out=transposed;
      by notsorted month notsorted id notsorted car;
      var cost_of_claim;
      id claim_type;
    run;
    

    输出数据集没有非对角线零,但如果您真的需要它们,您可以在数据步骤中添加它们。

    【讨论】:

    • NOTSORTED 适用于所有 BY 语句,它不像 DESCENDING 选项。我通常把它放在最后。
    【解决方案2】:

    您可以尝试动态 sql 和数据透视,但性能取决于您拥有多少不同的声明类型。

    create table #mytable (Month int, ID int, Car varchar(20), Claim_Type varchar(20),  Cost_of_claim int)
    
    insert into #mytable values 
    (1, 1243, 'Ferrari', 'Collision', 12000)
    , (2, 6437, 'Peugeot', 'Fire', 50000)
    , (5, 184, 'Citroen', 'Stole', 3000)
    , (9, 1930, 'Fiat', 'Medical', 1000)
    , (3, 2934, 'GM', 'Liability', 20000)
    , (12, 4455, 'Ford', 'Theft', 20)
    
    
    DECLARE @cols AS NVARCHAR(MAX),
        @query  AS NVARCHAR(MAX)
    
    select @cols = STUFF((SELECT ',' + QUOTENAME(Claim_Type) 
                        from #mytable
                        group by Claim_Type
                        order by Claim_Type
                FOR XML PATH(''), TYPE
                ).value('.', 'NVARCHAR(MAX)') 
            ,1,1,'')
    
    set @query = N'SELECT ' + 'month,id,car,' + @cols + N' from 
                 (
                    select month,id, car, Cost_of_claim, Claim_Type
                    from #mytable               
                ) x
                pivot 
                (
                    max(Cost_of_claim)
                    for Claim_Type in (' + @cols + N')
                ) p 
                '
    
    exec sp_executesql @query;
    
    drop table #mytable
    

    【讨论】:

      【解决方案3】:

      此方法使用所有可能的 claim_types 填充宏变量并循环遍历它们,以与示例代码相同的方式生成变量,因此您无需输入所有可能的情况。使用“backstop”变量是因为循环中的逗号(在 proc sql 步骤中的最后一个逗号之后没有一个变量的情况下,SAS 将出错)。

      data have;
         input Month ID Car $12. Claim_Type $12. Cost_of_claim;
         datalines;
        1    1243   Ferrari    Collision      12000
        2    6437   Peugeot    Fire           50000
        5    0184   Citroen    Stole           3000
        9    1930   Fiat       Medical         1000
        3    2934   GM         Liability      20000
          ;
      run;
      
      
      %macro your_macro;
      
          proc sql noprint;
              select distinct claim_type into: list_of_claims separated by " " from have;
      
              create table want (drop = backstop) as select
                  month, id, car,
                      %do i = 1 %to %sysfunc(countw(&list_of_claims.));
                      %let this_claim = %scan(&list_of_claims., &i.);
                          case when claim_type = "&this_claim." then cost_of_claim else 0 end as &this_claim.,
                      %end;
                  1 as backstop
              from have;
          quit;
      
      %mend your_macro;
      
      %your_macro;
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2019-08-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-05-06
        相关资源
        最近更新 更多