SAS:如何统计多个级别

时间:2014-12-11 11:16:56

标签: sql sas

在SAS中是否有一种优雅的方法可以在不使用一系列sql语句的情况下在多个级别计算“count distinct”?

假设我们正在统计独特的客户。 Joe已经购买了2个大型小部件和1个小部件,这很简单,可以将他视为大型小部件的1个客户和小型部件的1个客户。但我们还需要将他视为只有1个“小部件客户”,因此需要单独的SQL计算。事实证明Joe还买了一个鼠标垫,所以当我们计算杂项产品组的独特客户时,我们需要第三次SQL计算。 Joe从商店#1购买了所有这些东西,但是当我们计算小商店号码区域的杂项产品的独特客户时,我们还必须考虑Joe从商店#2购买咖啡杯。另一个SQL计算。

SAS有许多方法可以计算事物,但似乎没有一种简单的方法来计算“不同”的东西。有什么建议吗?

3 个答案:

答案 0 :(得分:4)

您在这里遇到了一些不同的问题,这取决于数据的结构。很高兴你没有提供,所以我决定!

我们将假设您拥有这样的数据集。您还可以将两个LargeWidget购买分为两行(对于其他产品也是如此),这对此没有任何影响。

data customers;
  length product $20;
  input name $ product $ store $ count;
datalines;
Joe SmallWidget Store1 1
Joe LargeWidget Store1 2
Joe Mousepad Store1 1
Joe TeaPitcher Store2 1
Jack SmallWidget Store2 1
Jack Mousepad Store1 1
Jane LargeWidget Store2 1
Jane Mousepad Store1 1
Jill LargeWidget Store3 1
;;;;
run;

然后我们需要再做一次设置:确定你打算如何分组。多标签格式允许您将一个值放入多个存储桶(即,大窗口小部件和所有窗口小部件)。这是一个例子,你可以在这里做很多不同的事情;您也可以从数据集中执行此操作(在proc格式中查找CNTLIN选项,或者询问其他问题)。

proc format;
  value $prodgroup (multilabel default=20)
    SmallWidget = "Small Widgets"
    SmallWidget = Widgets
    LargeWidget = "Large Widgets"
    LargeWidget = Widgets
    Mousepad = Mousepads
    Mousepad = "Misc Products"
    TeaPitcher = "Tea Pitchers"
    TeaPitcher = "Misc Products"
  ;
  value $storeRegions (multilabel)
    Store1=Store1
    Store1=West
    Store2=Store2
    Store2=East
    Store3=Store3
    Store3=East
  ;
quit;

然后我们面临一个大问题:SAS在计算' distinct'的东西。它并没有真正做到这一点。我不知道为什么;列表确实应该,但它没有。然而,它确实很好地把东西放在水桶里,而这在这里真的很难;实际的不同计数位可以在第二次传递中完成(无论您的初始数据大小如何,假设您的产品种类比数据点少很多,这将非常快。)

proc tabulate data=customers out=custdata;
  class product store /mlf preloadfmt order=data;  
  class name;
  format product $prodgroup20.;
  format store $storeRegions6.;
  tables (all store product store*product),name*n;
run;

proc tabulate data=custdata;
  class product store/missing;
  tables (product*store), n;
run;

MLF PRELOADFMT ORDER=DATA使多标记位正常工作并使其以有用的顺序出现(在我看来)。您可以将NOTSORTED添加到PROC FORMAT中的值语句中以获得更多控制权,但第二个PROC TABULATE只会在顺序方面搞乱,所以我不会打扰。

在这种情况下,我们所做的是首先生成一个由客户提供的表,然后该表输出到数据集;只要分组在两个过程中都被镜像(当然减去NAME类!),该数据集现在保证每个[每个分组]的每个客户包含一行。

这个过程的一个不足之处在于它不会为那些你需要没有第二个表格的产品生成行,而是在SQL或数据步骤中执行。例如:

proc tabulate data=customers out=custdata2;
  class product store /mlf preloadfmt order=data;  
  class name;
  format product $prodgroup20.;
  format store $storeRegions6.;
  tables (all store product store*product),name*n/printmiss;
run;

proc sql;
  select product, store, sum(case when n>0 then 1 else 0 end) as count
  from custdata2
  group by product,store;
quit;

注意对TABLE行的一个更改:printmiss,它指示Tabulate为每个可能的组合创建一行,无论如何。然后SQL完成剩下的工作。当然,如果您更喜欢SQL,这个SQL也可以先复制第二个Tabulate语句。

答案 1 :(得分:3)

如果我正确地解释它,听起来像带有类声明的proc手段可能会给你你想要的东西。

添加商店(1对2),产品(大小部件,小部件,咖啡和鼠标垫),产品汇总(小部件与非小部件)作为类,并查看发生的频率。

如果Joe的咖啡多次出现在同一次购买中,您可能需要使用单个SQL select distinct语句预处理数据。

我现在正在接触,因为我正在猜测数据的结构。如果您添加一个小数据示例,我相信我们可以得到正确答案。

答案 2 :(得分:2)

实际上很短暂,所以我会转储代码并运行......但我认为这应该可以解决问题。我正在使用sashelp.cars数据集来获取一些示例数据。我要说make + model的组合是我想要以尽可能多的方式测量的“独特”值。

创建样本数据。 Flag只是我们将在下一步中使用的数字字段。请注意,在此数据中,如果您有字段a,b,c,make + model,那么这些值的组合应该已经是唯一的,这一点非常重要。

data test;
  length key $50;
  set sashelp.cars;
  key = cats(make,model);
  flag = 1;
  keep make type origin key flag ;
run;

使用proc summary生成我们感兴趣的所有组合。注意where子句将我们保留的行限制为在考虑的字段中包含make + model的那些行。尝试评论where语句,看看我的意思。

proc summary data=test noprint missing;
  class make type origin key;
  var flag;
  output out=smry(where=(mod(_type_,2) eq 1)) sum=;
run;

简单总结一下结果表,瞧!

proc sql noprint;
  create table all_combos as
  select make,
         type,
         origin,
         sum(flag) as distinct_keys
  from smry
  group by 1,2,3,4
  order by _type_
  ;
quit;

我将回过头来详细解释它是如何运作的。如果其他人现在有时间,那么随时编辑这篇文章; - )

编辑:刚刚阅读JJFord的答案,这基本上就是它的实现。