我有一个数据集,必须根据粒度(FIELD1 和 FIELD2)进行汇总。必须对两个指标字段(METRIC1 和 METRIC2)求和。到目前为止,这似乎是一个简单的 GROUP BY 任务。但我有一个字符串字段(FLAG),也必须通过连接不同的值来汇总。
可以在 Oracle 中使用 LISTAGG() 函数执行此操作。 请帮助我在 SAS Proc SQL 中实现同样的目标。
我不认为 SAS 中有直接的方法可以做到这一点。 CATS(和类似的串联函数)不是聚合函数。 几年前有人建议将它们添加回来,但据我所知,没有任何结果(请参阅此线程。)
如果我理解正确,您正在做的是 GROUP BY field1/field2、SUM metric1/metric2,并创建一个 FLAG 字段来连接所有看到的 FLAG 字段值(但不按它们分组)。
我处理这个问题的方法是首先进行聚合(field1/field2),然后将其加入到一个单独的表中,该表只是 field1/field2/flag。 您可以在数据步骤中最轻松地做到这一点,例如:
data want;
set have;
by field1 field2;
length flag_out $100; *or longer if you need longer;
flag_out = catx(',',flag_out,flag);
if last.field2 then output;
rename flag_out=flag;
drop flag;
run;
这假设它已经按 field1/field2 排序,否则您需要先这样做。
如上所述,没有
LISTAGG()
函数,也没有用于创建自定义聚合函数的内置功能。但是,有两种可能获得输出。
示例一
使用 DOW 处理和散列的数据步骤,用于在组内连接时跟踪不同的标志值。
data want;
if 0 then set have; *prep pdv;
length flags $200;
declare hash _flags();
_flags.defineKey('flag');
_flags.defineDone();
do until (last.f2);
set have;
by f1 f2;
m1_sum = sum(m1_sum,m1);
m2_sum = sum(m2_sum,m2);
if _flags.find() ne 0 then do;
_flags.add();
flags = catx(',',flags,flag);
end;
end;
drop m1 m2 flag;
_flags.delete();
run;
示例二
创建在 SQL 中使用的 FCMP 自定义函数。 由于 FCMP 无法创建聚合函数,因此结果将自动根据原始数据重新合并,然后必须对原始数据进行过滤。 FCMP 函数还使用哈希来跟踪组内
flag
的不同值。
proc fcmp outlib=sasuser.functionsx.package;
function listagg(f1 $, f2 $, n, item $) $;
length result $32000 index 8;
static flag;
static index;
declare hash items();
if flag = . then do;
flag = 1;
rc = items.defineKey('item');
rc = items.defineDone();
end;
static items;
index + 1;
rc = items.replace();
if index = n then do;
declare hiter hi('items');
result = '';
do while (hi.next() = 0);
result = catx(',',result,item);
end;
index = 0;
rc = items.clear();
return (result);
end;
else
return ("");
endsub;
run;
options cmplib=sasuser.functionsx;
proc sql;
create table wanted as
select * from
(
select /* subselect is a remerge due to 'listagg' mimic */
f1,
f2,
listagg(f1, f2, count(*), flag) as flags,
sum(m1) as m1,
sum(m2) as m2
from have
group by f1, f2
)
where flags is not null /* filter the subselect */
;
quit;
理想情况下会使用散列的散列,但 FCMP 仅提供在
declare
语句中创建散列实例,并且不能使用 _new_
实例化动态散列。 SAS Viya 用户将能够在 FCMP 函数中使用新的组件对象 Dictionary
,并且可能能够拥有一个 Dictionary of Dictionaries 来跟踪每个组中的不同标志值。
感谢大家的宝贵意见。显然 SAS 中没有针对这种情况的直接解决方案。考虑到需求的大局,我决定在数据层本身解决问题或添加另一个中间表示层。 我相信很多人都向 SAS 指出了这一需要,我也向 SAS 提出了这个问题。希望他们研究一下并提出与 LISTAGG 或 GROUP_CONCAT 类似的函数。
乔的回答非常好,但缺少一个关键部分。 应该有一个
retain flag_out;
“by”行之后的行。
除了Joe的回答和ibaranov的补充之外,我还必须添加以下内容:
if last.field2 then flag_out='';
之后
if last.field2 then output;
让它发挥作用。