【问题标题】:SAS Error: Null parameters for SUM are invalidSAS 错误:SUM 的空参数无效
【发布时间】:2019-06-27 16:00:39
【问题描述】:

所以我有一个名为 table1 的数据如下:

Obs  ID  M_201812  M_201901      M_201902    M_201903

1    X1     1         .             .           . 
2    X2     1         1             .           . 
3    X3     .         1             1           . 
4    X4     .         1             .           . 
5    X5     .         1             .           . 
6    X6     1         .             .           . 
7    X7     1         1             .           . 
8    X8     1         1             .           . 
9    X9     .         .             1           . 
10   X10    1         1             .           . 

这里的每一列都是一个月,是根据之前运行的某个宏动态生成的。月份将是动态的并且会有所不同。我需要做的是计算过去 3 个月、过去 6 个月和过去 12 个月的总和。我想到的方法如下: A) 将列名存储在宏变量中:

proc sql noprint;
    select distinct name
    into :cols2 separated by ','  
    from dictionary.columns
    where upcase(memname) = 'Table1' and name not in ('ID');
    ;
quit;
%put &cols2.

输出如下:

M_201812,M_201901,M_201902,M_201903

B) 此后根据变量中的项目数创建总和:

data table1;
set table1;

if count("&cols2",",") <=3 then do;
3m_total=sum(of &cols2);
6m_total=sum(of &cols2);
12m_total=sum(of &cols2);
end;
else if 3< count("&cols2",",") <=6 then do;
3m_total=sum(%scan(%superQ(cols2),-1,%str(,)),%scan(%superQ(cols2),-2,%str(,)),%scan(%superQ(cols2),-3,%str(,)));
6m_total=sum(of &cols2);
12m_total=sum(of &cols2);
end;
else if 6< count("&cols2",",") <=12 then do;
3m_total=sum(%scan(%superQ(cols2),-1,%str(,)),%scan(%superQ(cols2),-2,%str(,)),%scan(%superQ(cols2),-3,%str(,)));
6m_total=sum(%scan(%superQ(cols2),-1,%str(,)),%scan(%superQ(cols2),-2,%str(,)),%scan(%superQ(cols2),-3,%str(,)),%scan(%superQ(cols2),-4,%str(,)),%scan(%superQ(cols2),-5,%str(,)),%scan(%superQ(cols2),-6,%str(,)));
12m_total=sum(of &cols2);
else do;
    3m_total=sum(%scan(%superQ(cols2),-1,%str(,)),%scan(%superQ(cols2),-2,%str(,)),%scan(%superQ(cols2),-3,%str(,)));
    6m_total=sum(%scan(%superQ(cols2),-1,%str(,)),%scan(%superQ(cols2),-2,%str(,)),%scan(%superQ(cols2),-3,%str(,)),%scan(%superQ(cols2),-4,%str(,)),%scan(%superQ(cols2),-5,%str(,)),%scan(%superQ(cols2),-6,%str(,)));
    12m_total=sum(%scan(%superQ(cols2),-1,%str(,)),%scan(%superQ(cols2),-2,%str(,)),%scan(%superQ(cols2),-3,%str(,)),%scan(%superQ(cols2),-4,%str(,)),%scan(%superQ(cols2),-5,%str(,)),%scan(%superQ(cols2),-6,%str(,)),
    %scan(%superQ(cols2),-7,%str(,)),%scan(%superQ(cols2),-8,%str(,)),%scan(%superQ(cols2),-9,%str(,)),%scan(%superQ(cols2),-10,%str(,)),%scan(%superQ(cols2),-11,%str(,)),%scan(%superQ(cols2),-12,%str(,)));
    end;
    run;

基本上,只有当有 12 个每月列可用时,我们才会得到 12 个月的总和。如果只有 3 个月可用,则 3months sum=6months sum=12months sum。运行代码后出现如下错误:

ERROR 159-185: Null parameters for SUM are invalid.

这发生在最后一个 else do 语句中。我一生都无法弄清楚为什么 sas 不能阅读一个简单的 if-then-do-else 语句。 if 条件或调用宏变量是否有错误?这里的任何帮助将不胜感激。非常感谢。

【问题讨论】:

  • 你从那个结构开始有什么原因吗?你不能从垂直结构开始,其中日期是变量的值而不是变量的名称?
  • 呼应@Tom,这是一个报告结构,不是维护或计算数据的好结构。可以做到,但很痛苦。
  • 呼应@Reeza,具体来说,您应该使用 MM/YYY 列保持数据较长而不是宽,这对几乎任何分析都有很大帮助。见proc transpose

标签: if-statement sas sas-macro datastep


【解决方案1】:

如果不查看您的宏逻辑实际生成的代码,很难判断。 但很可能是因为您生成了两个逗号,它们之间没有任何内容,如下所示:

475   data test;
476     y=sum(1,,2,3);
                -
                159
ERROR 159-185: Null parameters for SUM are invalid.

477   run;

您需要要么不生成额外的逗号,要么在逗号之间放置一些东西。所以要么是缺失值的句点,要么是 0。

一般来说,如果您不使用逗号,SAS 会更容易。所以得到一个以空格分隔的变量名列表。

proc contents data=Table1 noprint
  out=_contents (where=(name ne 'ID'))
; 
run;

proc sql noprint;
select name
     , case when (varnum>9) then name else ' ' end 
     , case when (varnum>6) then name else ' ' end 
     , case when (varnum>3) then name else ' ' end
  into :cols_12 separated by ' '
     , :cols_3 separated by ' '
     , :cols_6 separated by ' '
     , :cols_9 separated by ' '
  from _contents
  order by name
;
quit;

然后使用 of 关键字使用该名称列表,这样它们之间就不需要逗号了。

data want;
  set table1;
  total_3m=sum(0,of &cols_3);
  total_6m=sum(0,of &cols_6);
  total_9m=sum(0,of &cols_9);
  total=sum(0,of &cols_12);
run;

还要记住为变量使用有效的名称。变量名不能以数字开头。

【讨论】:

  • 感谢汤姆的建议。我运行了你的代码。但是在某些情况下,我注意到一些 id 错误地填充了总共 3m、6m、12m。所以例如:对于给定表中的 ID X8,他的 3m 总数应该是 1(从 201901-201903),他的 6 个月总数应该是 2(从任何可用到 201903)。但碰巧我得到 1 作为 3m 和 6m 的总和。请让我知道是否有解决此问题的方法。问候。
  • 如果变量没有按时间顺序定义,如何选择哪些变量不起作用的逻辑。查看宏变量的值和 _CONTENT 数据集以了解发生了什么。也许将keep=M_20: datastep 选项添加到proc contents 的输入中,以仅列出与您的模式匹配的变量。但是对于更强大的解决方案,您确实应该将日期保留在数据中而不是变量名中,这样逻辑就容易多了。
  • 谢谢汤姆,我会尝试编辑它。 1 最后一个问题。在基本数据步骤中,如果我运行超出字符串长度范围的扫描函数,有没有办法让我杀死那里的进程?假设我有一个单词“coding”并且我运行 Scan("coding",10) - 它将是空白的。但我根本不希望它执行任何操作。可能吗?
  • 关于 SCAN() 和 %SCAN() 的问题在某些方面完全不同。在普通 SAS 代码中,您可以测试 SCAN() 是否仅返回空格并采取一些措施。但在您的示例中,您使用 %SCAN() 生成将被编译的 SAS 代码。如果您想根据 %SCAN() 的返回有条件地生成不同的代码,那么您需要宏逻辑,这通常意味着一个实际的宏。虽然在某些情况下可以在开放代码中使用 %sysfunc(ifc(....))。
【解决方案2】:

考虑使用proc transpose 将您的数据从宽调整为长,然后运行多个相关的聚合 SQL 子查询以获取 3/6/12 个月的运行总和。这将达到您提到的预期结果:

我需要做的是计算过去 3 个月、过去 6 个月和过去 12 个月的总和。

数据

data Month_Data;
    infile datalines delimiter=',' DSD; 
    length ID $ 3;
    input ID $ M_201812 M_201901 M_201902 M_201903;
    datalines;
X1, 1, ., ., . 
X2, 1, 1, ., . 
X3, ., 1, 1, . 
X4, ., 1, ., . 
X5, ., 1, ., . 
X6, 1, ., ., . 
X7, 1, 1, ., . 
X8, 1, 1, ., . 
X9, ., ., 1, . 
X10, 1, 1, ., . 
;

重塑

proc sort data=Month_Data;
    by ID;
run;

proc transpose data=Month_Data
    out=Month_Data_Long;
    by ID;
run;

data Month_Data_Long;
    set Month_Data_Long (rename=(_NAME_ = Month_Year col1=value));
    length MMYY $ 9;
    format Month_Date date9.;

    label Month_Year = "Month Year Original";
    MMYY =  tranwrd(Month_Year, "M_", "") || "01";
    Month_Date = input(MMYY,  yymmdd9.);
run;

聚合

proc sql;
    create table Run_Sums as
    select m.ID, 
           m.Month_Date,
           m.Value,
           (select sum(m.Value)
            from Month_Data_Long sub
            where sub.ID = m.ID
             and sub.Month_Date >= intnx('month', m.Month_Date, -3)
             and sub.Month_Date <= m.Month_Date
             and sub.Value ^= .
            ) AS ThreeMonthsSum,

           (select sum(m.Value)
            from Month_Data_Long sub
            where sub.ID = m.ID
             and sub.Month_Date >= intnx('month', m.Month_Date, -6)
             and sub.Month_Date <= m.Month_Date
             and sub.Value ^= .
            ) AS SixMonthsSum,

           (select sum(m.Value)
            from Month_Data_Long sub
            where sub.ID = m.ID
             and sub.Month_Date >= intnx('month', m.Month_Date, -12)
             and sub.Month_Date <= m.Month_Date
             and sub.Value ^= .
            ) AS TwelveMonthsSum

    from Month_Data_Long m;
quit;

输出 (由于 OP 发布的数据,总和没有差异)

【讨论】:

  • 谢谢芭菲!我试试看。
猜你喜欢
  • 2017-08-07
  • 1970-01-01
  • 2022-11-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-08-17
  • 2016-01-05
相关资源
最近更新 更多