如何转置sas中多次出现的数据
how to transpose data with multiple occurrences in sas
我有一个包含 2 列的数据集 - 帐户和属性,其中有 6 种类型的属性。
我正在尝试使用 PROC TRANSPOSE 将 6 个不同的属性设置为 6 个新列,并在列具有该属性的位置设置 1,在没有该属性的位置设置 0
这个答案展示了两种方法:
Proc TRANSPOSE
,以及
- 通过散列使用索引查找的基于数组的转置。
对于所有帐户都缺少相同属性的情况,数据本身将无法展示所有属性——理想情况下,允许的或预期的属性应该在单独的 table 作为数据重塑的一部分。
过程转置
当仅使用 account
和 attribute
的 table 时,您将需要构建一个视图,添加一个数字变量,该变量可以被转置。在 TRANSPOSE
之后,必须进一步处理结果数据,用 0
.
替换缺失值 (.
)
示例:
data have;
call streaminit(123);
do account = 1 to 10;
do attribute = 'a','b','c','d','e','f';
if rand('uniform') < 0.75 then output;
end;
end;
run;
data stage / view=stage;
set have;
num = 1;
run;
proc transpose data=stage out=want;
by account;
id attribute;
var num;
run;
data want;
set want;
array attrs _numeric_;
do index = 1 to dim(attrs);
if missing(attrs(index)) then attrs(index) = 0;
end;
drop index;
run;
proc sql;
drop view stage;
来自
到
高级技巧——数组和哈希映射
在某些情况下,编码器或操作员认为 Proc TRANSPOSE
不可用,可能有很多组和很多属性。将属性值转置为类似命名标志变量的另一种方法是编码:
- 两次扫描
- 扫描 1 确定将遇到并用作列名的属性值
- 在宏变量中存储值列表
- 扫描2
- 将属性值排列为变量名
- 使用散列(或每个@Joe 的自定义信息)将值映射到数组索引
- 处理每组。将遇到的每个属性值对应的数组变量设置为
1
。通过哈希映射查找得到的数组索引。
示例:
* pass #1, determine attribute values present in data, the values will become column names;
proc sql noprint;
select distinct attribute into :attrs separated by ' ' from have;
* or make list of attributes from table of attributes (if such a table exists outside of 'have');
* select distinct attribute into :attrs separated by ' ' from attributes;
%put NOTE: &=attrs;
* pass #2, perform array based tranposformation;
data want2(drop=attribute);
* prep pdv, promulgate by group variable attributes;
if 0 then set have(keep=account);
array attrs &attrs.;
format &attrs. 4.;
if _n_=1 then do;
declare hash attrmap();
attrmap.defineKey('attribute');
attrmap.defineData('_n_');
attrmap.defineDone();
do _n_ = 1 to dim(attrs);
attrmap.add(key:vname(attrs(_n_)), data: _n_);
end;
end;
* preset all flags to zero;
do _n_ = 1 to dim(attrs);
attrs(_n_) = 0;
end;
* DOW loop over by group;
do until (last.account);
set have;
by account;
attrmap.find(); * lookup array index for attribute as column;
attrs(_n_) = 1; * set flag for attribute (as column);
end;
* implicit output one row per by group;
run;
另一种不使用 PROC TRANSPOSE 的方法是数据步长数组技术。
在这里,我有一个数据集,希望与您的数据集大致匹配。 ID可能是你的账号,Product是你的属性。
data have;
call streaminit(2007);
do id = 1 to 4;
do prodnum = 1 to 6;
if rand('Uniform') > 0.5 then do;
product = byte(96+prodnum);
output;
end;
end;
end;
run;
现在,我们在这里转置它。我们用 HAVE 中可能出现的六个变量创建一个数组。然后我们遍历数组以查看该变量是否存在。您可以在 if first.id
块中添加几行,将所有变量设置为 0 而不是最初缺失(我认为缺失更好,但 YMMV)。
data want;
set have;
by id;
array vars[6] a b c d e f;
retain a b c d e f;
if first.id then call missing(of vars[*]);
do _i = 1 to dim(vars);
if lowcase(vname(vars[_i])) = product then
vars[_i] = 1;
end;
if last.id then output;
run;
当然,如果我们知道数据集是如何构建的,我们可以做得更快。
data want;
set have;
by id;
array vars[6] a b c d e f;
if first.id then call missing(of vars[*]);
retain a b c d e f;
vars[rank(product)-96]=1;
if last.id then output;
run;
虽然您的数据实际上并不是那样工作的,但您可以制作一个信息,尽管这样做了。
*First we build an informat relating the product to its number in the array order;
proc format;
invalue arrayi
'a'=1
'b'=2
'c'=3
'd'=4
'e'=5
'f'=6
;
quit;
*Now we can use that!;
data want;
set have;
by id;
array vars[6] a b c d e f;
if first.id then call missing(of vars[*]);
retain a b c d e f;
vars[input(product,arrayi.)]=1;
if last.id then output;
run;
这最后一个可能是绝对最快的选项 - 很可能比 PROC TRANSPOSE 快得多,PROC TRANSPOSE 往往是我书中较慢的过程之一,但代价是必须提前知道你的变量是什么将包含在该数组中。
我有一个包含 2 列的数据集 - 帐户和属性,其中有 6 种类型的属性。 我正在尝试使用 PROC TRANSPOSE 将 6 个不同的属性设置为 6 个新列,并在列具有该属性的位置设置 1,在没有该属性的位置设置 0
这个答案展示了两种方法:
Proc TRANSPOSE
,以及- 通过散列使用索引查找的基于数组的转置。
对于所有帐户都缺少相同属性的情况,数据本身将无法展示所有属性——理想情况下,允许的或预期的属性应该在单独的 table 作为数据重塑的一部分。
过程转置
当仅使用 account
和 attribute
的 table 时,您将需要构建一个视图,添加一个数字变量,该变量可以被转置。在 TRANSPOSE
之后,必须进一步处理结果数据,用 0
.
.
)
示例:
data have;
call streaminit(123);
do account = 1 to 10;
do attribute = 'a','b','c','d','e','f';
if rand('uniform') < 0.75 then output;
end;
end;
run;
data stage / view=stage;
set have;
num = 1;
run;
proc transpose data=stage out=want;
by account;
id attribute;
var num;
run;
data want;
set want;
array attrs _numeric_;
do index = 1 to dim(attrs);
if missing(attrs(index)) then attrs(index) = 0;
end;
drop index;
run;
proc sql;
drop view stage;
来自
到
高级技巧——数组和哈希映射
在某些情况下,编码器或操作员认为 Proc TRANSPOSE
不可用,可能有很多组和很多属性。将属性值转置为类似命名标志变量的另一种方法是编码:
- 两次扫描
- 扫描 1 确定将遇到并用作列名的属性值
- 在宏变量中存储值列表
- 扫描2
- 将属性值排列为变量名
- 使用散列(或每个@Joe 的自定义信息)将值映射到数组索引
- 处理每组。将遇到的每个属性值对应的数组变量设置为
1
。通过哈希映射查找得到的数组索引。
- 扫描 1 确定将遇到并用作列名的属性值
示例:
* pass #1, determine attribute values present in data, the values will become column names;
proc sql noprint;
select distinct attribute into :attrs separated by ' ' from have;
* or make list of attributes from table of attributes (if such a table exists outside of 'have');
* select distinct attribute into :attrs separated by ' ' from attributes;
%put NOTE: &=attrs;
* pass #2, perform array based tranposformation;
data want2(drop=attribute);
* prep pdv, promulgate by group variable attributes;
if 0 then set have(keep=account);
array attrs &attrs.;
format &attrs. 4.;
if _n_=1 then do;
declare hash attrmap();
attrmap.defineKey('attribute');
attrmap.defineData('_n_');
attrmap.defineDone();
do _n_ = 1 to dim(attrs);
attrmap.add(key:vname(attrs(_n_)), data: _n_);
end;
end;
* preset all flags to zero;
do _n_ = 1 to dim(attrs);
attrs(_n_) = 0;
end;
* DOW loop over by group;
do until (last.account);
set have;
by account;
attrmap.find(); * lookup array index for attribute as column;
attrs(_n_) = 1; * set flag for attribute (as column);
end;
* implicit output one row per by group;
run;
另一种不使用 PROC TRANSPOSE 的方法是数据步长数组技术。
在这里,我有一个数据集,希望与您的数据集大致匹配。 ID可能是你的账号,Product是你的属性。
data have;
call streaminit(2007);
do id = 1 to 4;
do prodnum = 1 to 6;
if rand('Uniform') > 0.5 then do;
product = byte(96+prodnum);
output;
end;
end;
end;
run;
现在,我们在这里转置它。我们用 HAVE 中可能出现的六个变量创建一个数组。然后我们遍历数组以查看该变量是否存在。您可以在 if first.id
块中添加几行,将所有变量设置为 0 而不是最初缺失(我认为缺失更好,但 YMMV)。
data want;
set have;
by id;
array vars[6] a b c d e f;
retain a b c d e f;
if first.id then call missing(of vars[*]);
do _i = 1 to dim(vars);
if lowcase(vname(vars[_i])) = product then
vars[_i] = 1;
end;
if last.id then output;
run;
当然,如果我们知道数据集是如何构建的,我们可以做得更快。
data want;
set have;
by id;
array vars[6] a b c d e f;
if first.id then call missing(of vars[*]);
retain a b c d e f;
vars[rank(product)-96]=1;
if last.id then output;
run;
虽然您的数据实际上并不是那样工作的,但您可以制作一个信息,尽管这样做了。
*First we build an informat relating the product to its number in the array order;
proc format;
invalue arrayi
'a'=1
'b'=2
'c'=3
'd'=4
'e'=5
'f'=6
;
quit;
*Now we can use that!;
data want;
set have;
by id;
array vars[6] a b c d e f;
if first.id then call missing(of vars[*]);
retain a b c d e f;
vars[input(product,arrayi.)]=1;
if last.id then output;
run;
这最后一个可能是绝对最快的选项 - 很可能比 PROC TRANSPOSE 快得多,PROC TRANSPOSE 往往是我书中较慢的过程之一,但代价是必须提前知道你的变量是什么将包含在该数组中。