SAS Proc SQL 中的列表聚合和组串联

问题描述 投票:0回答:5

我有一个数据集,必须根据粒度(FIELD1 和 FIELD2)进行汇总。必须对两个指标字段(METRIC1 和 METRIC2)求和。到目前为止,这似乎是一个简单的 GROUP BY 任务。但我有一个字符串字段(FLAG),也必须通过连接不同的值来汇总。

输入数据集:

enter image description here

预期结果:

enter image description here

可以在 Oracle 中使用 LISTAGG() 函数执行此操作。 请帮助我在 SAS Proc SQL 中实现同样的目标。

sas proc-sql
5个回答
2
投票

我不认为 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 排序,否则您需要先这样做。


1
投票

如上所述,没有

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 来跟踪每个组中的不同标志值。


0
投票

感谢大家的宝贵意见。显然 SAS 中没有针对这种情况的直接解决方案。考虑到需求的大局,我决定在数据层本身解决问题或添加另一个中间表示层。 我相信很多人都向 SAS 指出了这一需要,我也向 SAS 提出了这个问题。希望他们研究一下并提出与 LISTAGG 或 GROUP_CONCAT 类似的函数。


0
投票

乔的回答非常好,但缺少一个关键部分。 应该有一个

retain flag_out;

“by”行之后的行。


0
投票

除了Joe的回答和ibaranov的补充之外,我还必须添加以下内容:

if last.field2 then flag_out='';

之后

if last.field2 then output;

让它发挥作用。

© www.soinside.com 2019 - 2024. All rights reserved.