SAS - 通过每列中的唯一记录转置所有列与计数
SAS - Transpose all colums by unique records in each column with counts
我正试图找到高效的、类似转置的代码,可以将下面的测试 table 转换为最终 table。我有下面的代码适用于这个例子,但出于实际目的,拆分数据步骤可以产生非常深的 table(在我的例子中有 1.37 亿条记录,而聚合为 2k)。
我希望有一些我遗漏的 Proc 或 Data 步骤技巧可以跳过中间步骤并提高效率。
Data test;
Input f1 $ f2 $ f3 $ f4 $ f5 $ f6 $ f7 $ f8 $;
DataLines;
a c f h k l o q
a c f h k l o q
a c g h k m o q
b c g h k m o q
b d g i k m o r
b d g i k n o r
b e g j k n o s
b e g j k n p s
;
Run;
Data final;
Input field $ values $ records;
DataLines;
f1 a 3
f1 b 5
f2 c 4
f2 d 2
f2 e 2
f3 f 2
f3 g 6
f4 h 4
f4 i 2
f4 j 2
f5 k 8
f6 l 2
f6 m 3
f6 n 3
f7 o 7
f7 p 1
f8 q 4
f8 r 2
f8 s 2
;
Run;
/*Working solution - could it be done more efficiently?*/
Data split;
Set test;
Array f{8} f1-f8;
Do i=1 To 8;
field = 'f'||PUT(i,best2.);
values = f{i};
Output;
End;
Drop i f1-f8;
Run;
Proc SQL;
Create Table final As
Select
field
,values
,COUNT(*) As records Format=comma8.0
From split
Group By 1,2
Order By 1,2
;
Quit;
可能最省时的解决方案是使用摘要过程。 PROC TABULATE
似乎最适合这里。
proc tabulate data=test out=test_c;
class f1-f8;
tables (f1-f8),n;
run;
data want;
set test_c;
array f f1-f8;
col = cats(of f[*]);
which_f = whichc(col,of f[*]);
var = vname(f[which_f]);
keep col var n;
run;
最后的数据步骤非常'cheap',因为它只在汇总数据集上完成,所以 2k 大小的数据集。只要您没有大量的列,这应该非常简单。
如果您确实也有大量的列,那么上面提供的基于数据步骤的解决方案可能是最好的。您可以通过将中间(拆分)数据步进视图来显着加快速度。
使用 PROC SUMMARY
很容易做到。使用您的变量列表作为 CLASS
变量。您可以使用 WAYS
语句将其限制为单向分类组。您可以使用 CHARTYPE
选项来轻松转换结果。您可能还想添加 MISSING
选项以防止 proc summary 消除对任何变量都有缺失值的输入观察。
%let varlist=f1-f8 ;
proc summary data=test chartype missing ;
class &varlist ;
ways 1 ;
output out=result ;
run;
data want ;
set result ;
length field value count 8 ;
array fields &varlist ;
index=indexc(_type_,'1');
field=vname(fields(index));
value=fields(index);
count=_freq_;
keep field value count;
run;
您可能想要颠倒变量的顺序,以便它们以原来的顺序出现。所以使用 F8-F1
而不是 F1-F8
.
请注意,由于数据步骤中的 ARRAY 语句,所有变量都必须是同一类型。
马萨尔:
可以在使用 suminc:
和 keysum:
功能的 DATA Step 哈希对象中完成频率计数:
Data have;
Input f1 $ f2 $ f3 $ f4 $ f5 $ f6 $ f7 $ f8 $;
DataLines;
a c f h k l o q
a c f h k l o q
a c g h k m o q
b c g h k m o q
b d g i k m o r
b d g i k n o r
b e g j k n o s
b e g j k n p s
;
Run;
data want(keep=field value count);
length field value count 8;
one = 1;
call missing (field, value, count);
declare hash freq(suminc:'one', keysum:'count', ordered:'a', hashexp:20);
freq.defineKey('field', 'value');
freq.defineDone();
do while ( not end );
set have end=end;
array f f1-f8;
do over f; field = vname(f); value=f; freq.ref(); end;
end;
declare hiter iter("freq");
rc = iter.first();
do while(rc = 0);
rc = freq.sum(sum: count);
output;
rc = iter.next();
end;
stop;
run;
我正试图找到高效的、类似转置的代码,可以将下面的测试 table 转换为最终 table。我有下面的代码适用于这个例子,但出于实际目的,拆分数据步骤可以产生非常深的 table(在我的例子中有 1.37 亿条记录,而聚合为 2k)。
我希望有一些我遗漏的 Proc 或 Data 步骤技巧可以跳过中间步骤并提高效率。
Data test;
Input f1 $ f2 $ f3 $ f4 $ f5 $ f6 $ f7 $ f8 $;
DataLines;
a c f h k l o q
a c f h k l o q
a c g h k m o q
b c g h k m o q
b d g i k m o r
b d g i k n o r
b e g j k n o s
b e g j k n p s
;
Run;
Data final;
Input field $ values $ records;
DataLines;
f1 a 3
f1 b 5
f2 c 4
f2 d 2
f2 e 2
f3 f 2
f3 g 6
f4 h 4
f4 i 2
f4 j 2
f5 k 8
f6 l 2
f6 m 3
f6 n 3
f7 o 7
f7 p 1
f8 q 4
f8 r 2
f8 s 2
;
Run;
/*Working solution - could it be done more efficiently?*/
Data split;
Set test;
Array f{8} f1-f8;
Do i=1 To 8;
field = 'f'||PUT(i,best2.);
values = f{i};
Output;
End;
Drop i f1-f8;
Run;
Proc SQL;
Create Table final As
Select
field
,values
,COUNT(*) As records Format=comma8.0
From split
Group By 1,2
Order By 1,2
;
Quit;
可能最省时的解决方案是使用摘要过程。 PROC TABULATE
似乎最适合这里。
proc tabulate data=test out=test_c;
class f1-f8;
tables (f1-f8),n;
run;
data want;
set test_c;
array f f1-f8;
col = cats(of f[*]);
which_f = whichc(col,of f[*]);
var = vname(f[which_f]);
keep col var n;
run;
最后的数据步骤非常'cheap',因为它只在汇总数据集上完成,所以 2k 大小的数据集。只要您没有大量的列,这应该非常简单。
如果您确实也有大量的列,那么上面提供的基于数据步骤的解决方案可能是最好的。您可以通过将中间(拆分)数据步进视图来显着加快速度。
使用 PROC SUMMARY
很容易做到。使用您的变量列表作为 CLASS
变量。您可以使用 WAYS
语句将其限制为单向分类组。您可以使用 CHARTYPE
选项来轻松转换结果。您可能还想添加 MISSING
选项以防止 proc summary 消除对任何变量都有缺失值的输入观察。
%let varlist=f1-f8 ;
proc summary data=test chartype missing ;
class &varlist ;
ways 1 ;
output out=result ;
run;
data want ;
set result ;
length field value count 8 ;
array fields &varlist ;
index=indexc(_type_,'1');
field=vname(fields(index));
value=fields(index);
count=_freq_;
keep field value count;
run;
您可能想要颠倒变量的顺序,以便它们以原来的顺序出现。所以使用 F8-F1
而不是 F1-F8
.
请注意,由于数据步骤中的 ARRAY 语句,所有变量都必须是同一类型。
马萨尔:
可以在使用 suminc:
和 keysum:
功能的 DATA Step 哈希对象中完成频率计数:
Data have;
Input f1 $ f2 $ f3 $ f4 $ f5 $ f6 $ f7 $ f8 $;
DataLines;
a c f h k l o q
a c f h k l o q
a c g h k m o q
b c g h k m o q
b d g i k m o r
b d g i k n o r
b e g j k n o s
b e g j k n p s
;
Run;
data want(keep=field value count);
length field value count 8;
one = 1;
call missing (field, value, count);
declare hash freq(suminc:'one', keysum:'count', ordered:'a', hashexp:20);
freq.defineKey('field', 'value');
freq.defineDone();
do while ( not end );
set have end=end;
array f f1-f8;
do over f; field = vname(f); value=f; freq.ref(); end;
end;
declare hiter iter("freq");
rc = iter.first();
do while(rc = 0);
rc = freq.sum(sum: count);
output;
rc = iter.next();
end;
stop;
run;