SAS 中的计数
Countifs in SAS
我有一个包含 3 列的 SAS 数据集。一个 FirmIndex
、一个 ProducIndex
和一个名为 PrChange
的第三列。在每个 FirmIndex
& ProductIndex
组中,我想计算有多少 PrChange
与 .
和 0
不同,并将其添加到名为 Number
。然后我想将该列 Number
除以每个组中不是 .
.
的观察数
下面是数据集和所需输出的示例。
data prod;
input firmindex productindex PrChange Number Fract;
cards;
1 1 . 1 0.250
1 1 0.00 1 0.250
1 1 0.00 1 0.250
1 1 -0.40 1 0.250
1 1 0.00 1 0.250
1 2 . 2 1.000
1 2 1.00 2 1.000
1 2 0.30 2 1.000
1 3 . 4 0.800
1 3 0.70 4 0.800
1 3 1.00 4 0.800
1 3 0.70 4 0.800
1 3 0.00 4 0.800
1 3 -0.30 4 0.800
1 4 . 5 1.000
1 4 0.20 5 1.000
1 4 -1.00 5 1.000
1 4 -0.90 5 1.000
1 4 -0.50 5 1.000
1 4 1.00 5 1.000
2 1 . 2 1.000
2 1 0.30 2 1.000
2 1 -0.50 2 1.000
2 2 . 5 0.714
2 2 0.30 5 0.714
2 2 0.10 5 0.714
2 2 0.00 5 0.714
2 2 0.00 5 0.714
2 2 0.80 5 0.714
2 2 -0.20 5 0.714
2 2 0.40 5 0.714
2 3 . 1 1.000
2 3 0.60 1 1.000
2 4 . 5 0.714
2 4 -1.00 5 0.714
2 4 0.80 5 0.714
2 4 -0.20 5 0.714
2 4 0.00 5 0.714
2 4 0.00 5 0.714
2 4 -0.70 5 0.714
2 4 0.90 5 0.714
2 5 . 3 1.000
2 5 0.90 3 1.000
2 5 -0.70 3 1.000
2 5 -0.50 3 1.000
;
run;
这是我尝试生成列 number
的方法,但它不起作用:
data work.prod;
set work.prod;
by firmindex productindex;
if first.productindex or first.firmindex then sum = 0;
else if PrChange ne 0 and PrChange ne .;
sum = sum + 1;
run;
考虑 proc sql
使用条件 CASE WHEN
相关子查询:
proc sql;
create table ProdChangeCount as
SELECT p.firmindex, p.productindex,
(SELECT SUM(CASE WHEN sub.PrChange ^= . AND sub.PrChange ^= 0 THEN 1 ELSE 0 END)
FROM Prod sub
WHERE sub.firmindex = p.firmindex
AND sub.productindex = p.productindex) AS Number,
CALCULATED Number /
(SELECT Count(*)
FROM Prod sub
WHERE sub.PrChange ^= .
AND sub.firmindex = p.firmindex
AND sub.productindex = p.productindex) AS Frac
FROM Prod p;
quit;
这里的问题是您需要在 运行 数据行之前除以数字。这就是SAS不同于Excel的地方; SAS 是基于行的,这意味着它获取您的代码并一次对每一行数据(或多或少)运行它,而不是动态地查看每个其他单元格中的每个单元格(如 Excel)。对于这样的东西,速度更快、效率更高,但灵活性较差。
您的特定问题需要 DoW 循环。这接管了正常的数据步循环并执行自己的循环 - 两次。一次计算 number/fract 值,然后一次将这些值复制到 BY 组。注意我只检查 last.productIndex
; last/first 转换总是在第二个由变量设置,而第一个由变量设置为真。
在这里,我们对第一组值(前 5 条记录)执行第一次循环,然后我们重新循环相同的 5 条记录。然后是接下来的 3. 等等。每次这两个循环都采用相同的行数,所以它们总是保持同步。
data want;
do _n_ = 1 by 1 until (last.productIndex);
set have;
by firmindex productindex;
number_denom = sum(number_Denom,not missing(PrChange));
number = sum(number, not (PrChange in (.,0)));
end;
fract = number/number_denom;
do _n_ = 1 by 1 until (last.productIndex);
set have;
by firmindex productindex;
output;
end;
run;
SAS 解决方案中的 SQL - Parfait 可能是总体上更好的解决方案,但 SAS 重新合并的意愿使 SASsy 解决方案更简单一些。
proc sql;
create table want as
select firmindex, productindex, prchange,
sum (not (prchange in (0,.))) as number,
calculated number / (sum ( not missing(prchange))) as fract
from have
group by firmindex, productindex;
quit;
SAS 将执行 grouping/counting/etc。然后毫无问题地合并回原始数据集,跳过对相关子查询的需要。不是标准 SQL,但在 SAS 中很常见。
我将给出我能够给出的 IML 答案。 Rick 或其他更精通 IML 的人可能会做得更好。在 R 或其他矩阵语言中,我认为这会容易得多,但我没有 IML 印章来执行此操作而无需循环;也许有可能。
proc iml;
use have;
read all var _all_ into h;
u = h[uniqueby(h,1:2), 1:2]; *generate the "unique" categories for the first two columns;
v = j(nrow(h),5); *generate a matrix to save this into;
v[,1:3] = h; *start it out with the first three columns of the dataset;
do i = 1 to nrow(u); *iterate over the unique category matrix;
number = ncol(loc(h[loc((h[,1:2] = u[i,1:2])[,#]),3]));
*the inner LOC produces a two column 1/0 matrix with match 1 / nomatch 0 for each col
then reduce to 1 column via subscript reduction product, to get correct 1/0 match vector
the outer LOC takes the rows of h from that (so rows of h matching u), then returns nonzero/nonmissing
which then ncol summarizes into a count;
fract_denom = ncol(loc(h[loc((h[,1:2] = u[i,1:2])[,#]),3] ^= .));
*similar, but here we have to verify they are not missing explicitly, considering 0 valid;
v[loc((v[,1:2] = u[i,1:2])[,#]),4] = number; *assign to col4 of V;
v[loc((v[,1:2] = u[i,1:2])[,#]),5] = number/fract_denom; *assign to col5 of V;
end;
print v;
quit;
这或多或少地使用了 unique-loc 方法,并进行了一些修改;可能是获得比赛的更简单方法。
我有一个包含 3 列的 SAS 数据集。一个 FirmIndex
、一个 ProducIndex
和一个名为 PrChange
的第三列。在每个 FirmIndex
& ProductIndex
组中,我想计算有多少 PrChange
与 .
和 0
不同,并将其添加到名为 Number
。然后我想将该列 Number
除以每个组中不是 .
.
下面是数据集和所需输出的示例。
data prod;
input firmindex productindex PrChange Number Fract;
cards;
1 1 . 1 0.250
1 1 0.00 1 0.250
1 1 0.00 1 0.250
1 1 -0.40 1 0.250
1 1 0.00 1 0.250
1 2 . 2 1.000
1 2 1.00 2 1.000
1 2 0.30 2 1.000
1 3 . 4 0.800
1 3 0.70 4 0.800
1 3 1.00 4 0.800
1 3 0.70 4 0.800
1 3 0.00 4 0.800
1 3 -0.30 4 0.800
1 4 . 5 1.000
1 4 0.20 5 1.000
1 4 -1.00 5 1.000
1 4 -0.90 5 1.000
1 4 -0.50 5 1.000
1 4 1.00 5 1.000
2 1 . 2 1.000
2 1 0.30 2 1.000
2 1 -0.50 2 1.000
2 2 . 5 0.714
2 2 0.30 5 0.714
2 2 0.10 5 0.714
2 2 0.00 5 0.714
2 2 0.00 5 0.714
2 2 0.80 5 0.714
2 2 -0.20 5 0.714
2 2 0.40 5 0.714
2 3 . 1 1.000
2 3 0.60 1 1.000
2 4 . 5 0.714
2 4 -1.00 5 0.714
2 4 0.80 5 0.714
2 4 -0.20 5 0.714
2 4 0.00 5 0.714
2 4 0.00 5 0.714
2 4 -0.70 5 0.714
2 4 0.90 5 0.714
2 5 . 3 1.000
2 5 0.90 3 1.000
2 5 -0.70 3 1.000
2 5 -0.50 3 1.000
;
run;
这是我尝试生成列 number
的方法,但它不起作用:
data work.prod;
set work.prod;
by firmindex productindex;
if first.productindex or first.firmindex then sum = 0;
else if PrChange ne 0 and PrChange ne .;
sum = sum + 1;
run;
考虑 proc sql
使用条件 CASE WHEN
相关子查询:
proc sql;
create table ProdChangeCount as
SELECT p.firmindex, p.productindex,
(SELECT SUM(CASE WHEN sub.PrChange ^= . AND sub.PrChange ^= 0 THEN 1 ELSE 0 END)
FROM Prod sub
WHERE sub.firmindex = p.firmindex
AND sub.productindex = p.productindex) AS Number,
CALCULATED Number /
(SELECT Count(*)
FROM Prod sub
WHERE sub.PrChange ^= .
AND sub.firmindex = p.firmindex
AND sub.productindex = p.productindex) AS Frac
FROM Prod p;
quit;
这里的问题是您需要在 运行 数据行之前除以数字。这就是SAS不同于Excel的地方; SAS 是基于行的,这意味着它获取您的代码并一次对每一行数据(或多或少)运行它,而不是动态地查看每个其他单元格中的每个单元格(如 Excel)。对于这样的东西,速度更快、效率更高,但灵活性较差。
您的特定问题需要 DoW 循环。这接管了正常的数据步循环并执行自己的循环 - 两次。一次计算 number/fract 值,然后一次将这些值复制到 BY 组。注意我只检查 last.productIndex
; last/first 转换总是在第二个由变量设置,而第一个由变量设置为真。
在这里,我们对第一组值(前 5 条记录)执行第一次循环,然后我们重新循环相同的 5 条记录。然后是接下来的 3. 等等。每次这两个循环都采用相同的行数,所以它们总是保持同步。
data want;
do _n_ = 1 by 1 until (last.productIndex);
set have;
by firmindex productindex;
number_denom = sum(number_Denom,not missing(PrChange));
number = sum(number, not (PrChange in (.,0)));
end;
fract = number/number_denom;
do _n_ = 1 by 1 until (last.productIndex);
set have;
by firmindex productindex;
output;
end;
run;
SAS 解决方案中的 SQL - Parfait 可能是总体上更好的解决方案,但 SAS 重新合并的意愿使 SASsy 解决方案更简单一些。
proc sql;
create table want as
select firmindex, productindex, prchange,
sum (not (prchange in (0,.))) as number,
calculated number / (sum ( not missing(prchange))) as fract
from have
group by firmindex, productindex;
quit;
SAS 将执行 grouping/counting/etc。然后毫无问题地合并回原始数据集,跳过对相关子查询的需要。不是标准 SQL,但在 SAS 中很常见。
我将给出我能够给出的 IML 答案。 Rick 或其他更精通 IML 的人可能会做得更好。在 R 或其他矩阵语言中,我认为这会容易得多,但我没有 IML 印章来执行此操作而无需循环;也许有可能。
proc iml;
use have;
read all var _all_ into h;
u = h[uniqueby(h,1:2), 1:2]; *generate the "unique" categories for the first two columns;
v = j(nrow(h),5); *generate a matrix to save this into;
v[,1:3] = h; *start it out with the first three columns of the dataset;
do i = 1 to nrow(u); *iterate over the unique category matrix;
number = ncol(loc(h[loc((h[,1:2] = u[i,1:2])[,#]),3]));
*the inner LOC produces a two column 1/0 matrix with match 1 / nomatch 0 for each col
then reduce to 1 column via subscript reduction product, to get correct 1/0 match vector
the outer LOC takes the rows of h from that (so rows of h matching u), then returns nonzero/nonmissing
which then ncol summarizes into a count;
fract_denom = ncol(loc(h[loc((h[,1:2] = u[i,1:2])[,#]),3] ^= .));
*similar, but here we have to verify they are not missing explicitly, considering 0 valid;
v[loc((v[,1:2] = u[i,1:2])[,#]),4] = number; *assign to col4 of V;
v[loc((v[,1:2] = u[i,1:2])[,#]),5] = number/fract_denom; *assign to col5 of V;
end;
print v;
quit;
这或多或少地使用了 unique-loc 方法,并进行了一些修改;可能是获得比赛的更简单方法。