我有一张包含大约1000条记录和2000列的表格。我想要做的是对每一行进行分类,使得除“ID”之外的所有列具有相等列值的所有记录都被赋予类别ID。我的最终答案如下:
ID A B C ..... Category ID 1 1 0 3 1 2 2 1 3 2 3 1 0 3 1 4 2 1 3 2 5 4 5 6 3 6 4 5 6 3
其中所有列(除了ID)对于ID 1,3都是相同的,因此它们获得相同的类别ID,依此类推。
我想我的想法只是编写一个SQL查询,除了“ID”之外,每个列上都有一个组,并为每个组分配一个数字,然后再连接回原始表。我当前的输入是一个文本文件,我可以使用SAS,MS Access和Excel。 (我可以在SAS中使用proc sql)。
在我走这条路并构建整个查询之前,我只是想知道是否有更好的方法来做到这一点?编写查询需要一些工作,我甚至不确定加入2000列(从未尝试过)是否切实可行,所以我想在我走错路径之前我会问一些想法
编辑:我刚刚意识到我的头衔并没有多大意义。我最初的想法是“有没有一种方法可以在不实际合并成组的情况下同时进行分组和分类?”EDIT2:将表格导入Excel之后,我很容易确定2000列中只有大约200个实际变化,因此有太多列的问题消失了。为了分类,我只导入了不同的列,我做了类似以下的事情:
proc sql;
create table categories as
select distinct *
from inputTable;
quit;
data categories;
set categories;
categoryID = _N_;
run;
proc sql;
create table tableCategorized as
select a.ID, b.CategoryID
from inputTable as a, categories as b
where
(
a.A=b.A and
a.B=b.B and
a.C=b.C and
...
a.XYZ=b.XYZ);
;
quit;
生成所有“=”比较是一件痛苦的事情,但我只是在Excel中使用字符串操作技术,所以它一点也不差。感谢所有的建议。
答案 0 :(得分:3)
好吧,我可以想到一个简单的方法,但我不知道你是否会达到SAS的内存/性能限制......我从来没有尝试使用2000变量进行proc排序,但也许其他人已经和可以评价。
proc sort data= mydata;
by A B C D /* etc.... */ myLastColumn;
run;
data mydata;
set mydata;
by A B C D /* etc....*/ myLastColumn;
retain categoryID 0;
if first.myLastColumn then categoryID +1;
run;
答案 1 :(得分:1)
试试这个
select *, dense_RANK() over(order by a,b,c) from table
-- order by id
答案 2 :(得分:0)
你可以用SAS做到这一点。 由于你只使用了1000行,你可以做一些有点乱的事情,但这很容易理解和运作。
我目前正在关注的想法是拥有一个包含所有变量的数组(如果有数字和字符变量,则为两个数组) 然后循环遍历数组,并将每个单个变量的值放在一个新的容器变量中,该变量的长度将是所有值的汇总,在您的示例中。 CONT =“103”表示第一个ID CONT =“213”表示第二个ID ....
由于我不确定你是否只使用数字我会说新变量是一个alfanumeric(因此当你追加时转换所有数字)是你最好的方式
一旦你完成了这个,你就会得到一个新变量,它是所有东西的集合,你可以很容易地用它来检查一些obervation是否属于同一类别。
代码将很快跟随您的示例=)
在这里,我添加了一些风味的alfanumeric变量 (抱歉格式化,试图弄清楚如何正确格式化代码)
*好吧,我放弃了,浪费了超过30分钟后,我真的不能把这个代码放在一个可读的方式,这是一个羞耻.code或blockquote不能正常工作。
数据测试;
ID = 1; A = 1; B = 0; C = 3; d = 'HI'; E = 54; F = 'C';
输出;
ID = 2; A = 2; B = 7; C = 3; d = 'KI'; E = 3; F = 'C';
输出;
ID = 3; A = 1; B = 0; C = 3; d = 'HI'; E = 54; F = 'C';
输出;
ID = 4; A = 2; B = 7; C = 3; d = 'KI'; E = 3; F = 'C';
输出;
ID = 5; A = 1; B = 7; C = 3; d = 'ZI'; E = 3; F = 'C';
输出;
ID = 6; A = 1; B = 8; C = 3; d = 'ZI'; E = 3; F = 'd';
输出;
ID = 7; A = 1; B = 8; C = 3; d = 'SI'; E = 3; F = 'C';
输出;
ID = 8; A = 1; B = 8; C = 3; d = 'SI'; E = 3; F = 'C';
输出;
跑;
数据测试1;
设定测试;
array numeric(*) _NUMERIC_;
array chara(*) _CHARACTER_;
长度续2000美元;
续= '';
do i = 1 to dim(数字);
如果vname(数字{i})=“ID”则继续;
CONT =压缩(续)||压缩(putn(数值{I},) “BEST”);
端;
我= 1来暗淡(chara);
如果vname(chara {i})=“ID”则继续;
CONT =压缩(续)||压缩(CHARA {I});
端;
放弃我;
跑;
proc sort data = test1;
通过cont;
跑;
数据测试2;
设置test1;
通过cont;
保留catid 0;
如果first.cont然后catid + 1;
下降;
跑;
proc sort data = test2 out = test3;
由id;
跑;
答案 3 :(得分:0)
由于没有简单的方法(我知道),我写了一个小的VBScript来处理文件的预处理。这是我的testfile和我编写的脚本(将它们放在桌面上,运行proc.vbs生成output.txt):
[input.txt]
id a b c
1 1 0 3
2 2 1 3
3 1 0 3
4 2 1 3
5 4 5 6
6 4 5 6
[proc.vbs]
' init
set fso = createobject("scripting.filesystemobject")
set input = fso.opentextfile("input.txt")
set output = fso.createtextfile("output.txt")
set dict = createobject("scripting.dictionary")
' read columns
columns = split(input.readline,vbtab)
' write columns, and add categoryid
output.writeline join(columns,vbtab) & vbtab & "categoryid"
' read rows
do while not input.atendofstream
fields1 = split(input.readline,vbtab)
fields2 = fields1
' in fields1. clear columns that we don't need for category
for x = 0 to ubound(fields1)
if lcase(columns(x)) = "id" then
fields1(x) = ""
end if
next
' from fields1. create unique category string & add to dict if not exists
unique = join(fields1,"|")
if (not dict.exists(unique)) then
dict.add unique, dict.count + 1
end if
' write fields
output.writeline join(fields2,vbtab) & vbtab & cstr(dict(unique))
loop
output.close
input.close
msgbox "finished!"
答案 4 :(得分:0)
我面前没有SAS,所以我必须给你一些未经测试的代码,但你可以很容易地这样做:
1)将除ID以外的所有变量连接到单个分隔的文本字符串中。确保分隔符未出现在数据中。也许使用字节(10)之类的字符作为分隔符。使用catx()进行连接,以便修剪每个var(前导和尾随)以节省空间。由于你有一吨变量,你可能需要将它分成2个,3个或4个字符串......没什么大不了的 - 使用4个字符串比使用2000个变量更容易。
2)让我们假设你设法将它全部放入一个大字符串中。将值添加到具有1000个维度的数组中,因为您的表可能有1000个唯一标识符,即。如果每个obs都有一个独特的字符串。每个数组元素都将存储字符串的值。数组中元素的位置将用作唯一ID。在将每个字符串添加到数组之前,循环遍历整个数组以确保它不在那里。如果你确实在那里找到它,那么使用position作为当前行的unique_id。
Sooo有点罗嗦,但我会在一些未经测试的代码中做到这一点......
proc sql noprint;
select varname from sashelp.vcolumns into :vars separated by ',' where varname ne 'id' and memname eq 'xx';
quit;
data yy;
length str1-str1000 unique_string $32767;
set xx;
array arr_unique {1000} str1-str1000;
retain unique_counter 1;
unique_string = catx(byte(10),&vars);
exists_already = 0;
do i = 1 to (unique_counter -1);
if arr_unique[i] eq unique_string then do;
unique_id = i;
exists_already = 1;
leave;
end;
end;
if not exists_already then do;
arr_unique[unique_counter] = unique_string;
unique_counter = unique_counter + 1;
end;
drop str1-str1000;
run;
祝你好运!
干杯 罗布
答案 5 :(得分:0)
proc sort
这很容易。只需在by
之后列出要在分组中使用的所有变量,然后在最后一个变量在下一个数据步骤中更改值时增加组ID。下面在Windows上使用9.2(TS1M0)。 HTH。
/* test data */
%let seed = 1234567;
data one;
length id 8 v2 $1;
array v[3:2000] v3-v2000;
keep id v:;
do id = 1 to 1e3;
v2 = substr("ABCDEF", ceil(6*ranuni(&seed)), 1);
do j = 3 to 9, 11 to 1999;
v[j] = j;
end;
v[10] = ceil(2*ranuni(&seed));
v[2000] = ceil(3*ranuni(&seed));
output;
end;
run;
/* on log
NOTE: The data set WORK.ONE has 1000 observations and 2000 variables.
*/
/* group observations based on all the variables
values except id */
proc sort data=one;
by v:;
run;
data two;
set one;
by v:;
if first.v2000 then group + 1;
run;
/* check */
proc freq data=two;
tables group;
run;
proc print data=two;
var id group v2 v10 v2000;
run;