2010-05-27 2 views
1

У меня есть таблица с около 1000 записей и 2000 столбцов. Я хочу классифицировать каждую строку так, чтобы всем записям с одинаковыми значениями столбцов для всех столбцов, кроме «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) одинаковы для идентификаторов 1,3 так что они получают тот же идентификатор категории и так далее.

Я полагаю, что моя мысль заключалась в том, чтобы просто написать SQL-запрос, который выполняет группу, на каждом отдельном столбце, кроме «ID», и назначить число каждой группе, а затем присоединиться к моей исходной таблице. Мой текущий вход - текстовый файл, и у меня есть SAS, MS Access и Excel для работы. (Я мог бы использовать proc sql из SAS).

Прежде чем идти по этому маршруту и ​​построить весь запрос, мне просто интересно, есть ли лучший способ сделать это? Для написания запроса потребуется некоторая работа, и я даже не уверен, что было бы целесообразно присоединиться к 2000 столбцам (никогда не пробовал), поэтому я подумал, что попрошу идеи, прежде чем я перейду слишком далеко по неправильному пути ,

EDIT: Я только что понял, что мой титул на самом деле не имеет смысла. Первоначально я думал: «Есть ли способ, которым я могу группироваться и классифицировать в одно и то же время без фактической консолидации в группы?»

EDIT2: После импорта таблицы в Excel я легко мог определить, что только около 200 из 2000 столбцов действительно менялись, поэтому проблема со слишком большим количеством столбцов ушла. Для классификации, я только импортировали столбцы, которые различались, и я сделал что-то вроде следующего:

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 , так что это было не так уж плохо. Спасибо за все предложения.

ответ

3

Ну, я могу думать о простом способе, но я не узнайте, собираетесь ли вы поражать ограничения памяти/производительности SAS ... Я никогда не пробовал сортировать proc с 2000 переменными, но, возможно, кто-то еще и может комментировать.

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; 
+0

Привет, я готов попробовать ваш ответ, но я попал в ловушку импорта файла в SAS. proc import out = inputCSV datafile = file1 dbms = CSV REPLACE; getnames = YES; datarow = 2; guessingrows = 2000; RUN; Кажется, что SAS имеет ограничение длины строки для импорта proc, потому что он продолжает вырезать строки в определенной точке. Есть ли опция lineize или lrecl для импорта proc? Я знаю, что есть один для infile, но я не могу найти ничего об этом в документации по импорту proc. – oob

+0

Существует системный параметр - используйте «options lrecl = <ваше значение здесь>;» –

+0

gotcha. похоже, что предел равен 32767. Все мои линии длиннее. я должен выяснить способ разделить его. – oob

0

Вы можете сделать это с помощью SAS. Поскольку вы работаете только с 1000 строк, вы можете сделать что-то немного грязное, но это легко понять и работать.

Идея, которую я сейчас выполняю, состоит из массива со всеми переменными (два массива, если есть числовые и символьные переменные) Затем выполните цикл через массивы и поместите значения каждой отдельной переменной в новую контейнерную переменную с огромной длиной, которая будет совокупностью всех значений, в вашем примере. CONT = «103» для первого ID CONT = «213» для второго ID ....

Поскольку я не уверен, если вы работаете с номерами только я бы сказал, имея новый переменная как alfanumeric (таким образом, преобразование всех чисел при добавлении) является вашим лучшим способом

После того, как вы это сделаете, у вас будет новая переменная, которая является совокупностью всего, и вы можете легко ее использовать, чтобы проверить, не наблюдается ли какое-либо наблюдение должны быть в той же категории.

код будет следовать в ближайшее время для примера =)

Здесь, я добавил некоторые alfanumeric переменную для аромата (извините за форматирование, пытаясь выяснить, как форматировать правильно код вещь)

* ok я сдаюсь, после того, как трачу более 30 минут, я действительно не могу поместить этот код в удобочитаемый способ, который является shame.code или blockquote, не работает так, как я хотел.

данные испытаний;
ID = 1; А = 1; В = 0; С = 3; D = 'HI'; Е = 54; F = 'C';
мощность;
ID = 2; А = 2; В = 7; С = 3; D = 'КИ'; Е = 3; F = 'C';
мощность;
ID = 3; А = 1; В = 0; С = 3; D = 'HI'; Е = 54; F = 'C';
мощность;
ID = 4; А = 2; В = 7; С = 3; D = 'КИ'; Е = 3; F = 'C';
мощность;
ID = 5; А = 1; В = 7; С = 3; D = 'ЗИ'; Е = 3; F = 'C';
мощность;
ID = 6; А = 1; В = 8; С = 3; D = 'ЗИ'; Е = 3; F = 'D';
мощность;
ID = 7; А = 1; В = 8; С = 3; D = 'СИ'; Е = 3; F = 'C';
мощность;
ID = 8; А = 1; В = 8; С = 3; D = 'СИ'; Е = 3; F = 'C';
мощность;
run;

данные test1;
set test;
array numeric(*) _NUMERIC_;
array chara(*) _CHARACTER_;
длина cont $ 2000;
cont = '';
do i = 1 to dim (числовой);
IF vname (numeric {i}) = "ID", затем продолжить;
cont = compress (cont) || compress (putn (числовой {i}, "BEST."));
конец;
do i = 1 to dim (chara);
IF vname (chara {i}) = "ID", затем продолжить;
cont = compress (cont) || compress (chara {i});
конец;
drop i;
run;

proc sort данные = test1;
by cont;
run;

данные test2;
комплект test1;
by cont;
keep catid 0;
если first.cont then catid + 1;
drop cont;
run;

proc sort данные = test2 out = test3;
by id;
run;

+1

заявление суммы, catid + 1, подразумевает, что catid сохраняется. –

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!" 
+0

спасибо! Я думал о чем-то вроде этой вчера вечером на питоне, но, к сожалению, кто-то еще здесь должен понимать весь процесс, поэтому я не могу использовать скрипт. booooo! Ну что ж. – oob

0

У меня нет SAS передо мной, так что я должен буду дать вам некоторые непроверенный код, но вы можете сделать это довольно легко, так как:

1) Объединить все переменные, за исключением ID в единственная текстовая строка с разделителями. Убедитесь, что разделитель не отображается в данных. Возможно, используйте символ, подобный байту (10) для разделителя. Используйте catx(), чтобы выполнить конкатенацию, чтобы каждый var был обрезан (ведущий и конечный), чтобы сэкономить место. Поскольку у вас есть тонна варов, вам может понадобиться разбить ее на 2, 3 или 4 строки ... нет ничего сложного - проще работать с 4 строками, чем работать с 2000 vars.

2) Предположим, вам удалось поместить все это в одну большую строку. Добавьте значение в массив с 1000 размерами, потому что потенциально может быть 1000 уникальных идентификаторов для вашей таблицы, т.е. если бы каждый особь имел уникальную строку. Каждый элемент массива сохранит значение строки. Позиция элемента в массиве будет использоваться как уникальный идентификатор. Прежде чем добавлять каждую строку в массив, проведите цикл по всему массиву, чтобы убедиться, что он еще не существует. Если вы его уже там найдете, используйте эту позицию как 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; 

Успехов!

Приветствия Роб

1

Попробуйте

select *, dense_RANK() over(order by a,b,c) from table 
-- order by id 
+0

Я не уверен, что dense_RANK() работает в SAS или MS Access, но я не знал об этой функции SQL. Спасибо! Я обязательно буду использовать это в будущем. – oob

+0

Доступ не поддерживает rank/dense_rank. – msi77

0

Это легко с proc sort. Просто перечислите все переменные, которые вы хотите использовать при группировке после by, а затем увеличьте идентификатор группы, когда последняя переменная изменит значение на следующем шаге данных. Ниже работали с 9.2 (TS1M0) в Windows. НТН.

EDIT: не знал, что Луиза Грей уже предложила это. Думаю, мой ответ просто показывает, что это действительно работает ... :-)

/* 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; 
Смежные вопросы