2016-07-07 4 views
1

У меня есть набор данных с 400 наблюдений 4 цифровых кодов, которые я хотел бы подушечка с места на обеих сторонахКак найти и заменить определенный текст в наборе данных SAS?

ex. Dataset 
obs code 
1 1111 
2 1112 
3 3333 
. 
. 
. 
400 5999 

Как я могу пройти через еще большого объема данных и заменить каждое вхождение любого из заполненные 400 кодов с помощью "".

ex. Large Dataset 
obs text 
1 abcdef 1111 abcdef 
2 abcdef 1111 abcdef 1112 8888 
3 abcdef 1111 abcdef 11128888 
... 

данных установлено, что я хочу

ex. New Data set 
obs text 
1 abcdef abcdef 
2 abcdef abcdef 8888 
3 abcdef abcdef 11128888 
... 

Примечание: Я только ищу, чтобы заменить 4 значные коды, проложенный с обеих сторон пространства. Таким образом, в obs 3 1112 не будет заменен.

Я пробовал выполнить следующий запрос proc sql, но он находит и заменяет первое совпадение, а не все совпадения.

proc sql; 
    select 
    *, 
    tranwrd(large_dataset.text, trim(small_dataset.code), ' ') as new_text 
from large_dataset 
    left join small_dataset 
    on findw(large_dataset.text, trim(small_dataset.code)) 
; 
quit; 
+0

Что вы пробовали до сих пор? Что вы хотите сделать, если столкнулись с чем-то вроде «abc111112abc», который имеет два совпадающих 4-значных кода, но может соответствовать только одному из них? Что, если у вас есть что-то вроде 'abc00111100abc', где у вас есть совпадение в середине пробега цифр? – user667489

+0

@ user667489 Я отредактировал свой вопрос, чтобы я никогда не столкнулся с проблемой с двумя упомянутыми вами ситуациями. Я обновил свое оригинальное сообщение, чтобы включить то, что я пробовал делать. –

+0

Имея всего 400 строк по 4 байта, вы можете создать регулярное выражение, чтобы заменить их на один вызов функции. Строковые переменные SAS ограничены 32 Кбайтами. – Tom

ответ

3

Вы можете просто использовать цикл DO для сканирования через небольшой набор данных для каждой записи в большом наборе данных. Если вы хотите использовать функцию TRANWRD(), вам нужно будет добавить лишние символы.

data want ; 
    set have ; 
    length code $4 ; 
    do i=1 to nobs while (text ne ' '); 
    set codes(keep=code) nobs=nobs point=i ; 
    text = substr(tranwrd(' '||text,' '||code||' ',' '),2); 
    end; 
    drop code; 
run; 

Цикл DO будет считывать записи из списка CODES. Использование опции POINT = в инструкции SET позволяет вам читать файл несколько раз. Предложение WHILE будет остановлено, если строка TEXT пуста, так как нет необходимости продолжать поиск кодов для замены в этой точке.

Если ваш список кодов достаточно мал, и вы можете получить правильное регулярное выражение, тогда вы можете попробовать использовать функцию PRXCHANGE(). Вы можете использовать шаг SQL для генерации кодов в виде списка, который вы можете использовать в регулярном выражении.

proc sql noprint ; 
    select code into :codelist separated by '|' 
    from codes 
; 
quit; 

data want ; 
    set have ; 
    text=prxchange("s/\b(&codelist)\b/ /",-1,text); 
run; 
+0

Когда вы много раз читаете набор данных, оператор 'sasfile' может повысить производительность. – user667489

+0

SASFILE может помочь, но файл с 400 наблюдениями по 4 байта, вероятно, уже буферизуется дисковым буфером OS или SAS. – Tom

+0

Ничего себе, спасибо! Я думаю, это сработало. Я все еще новичок в SAS, и я не совсем понимаю, почему и как этот код устраняет ВСЕ вхождения четырехзначных кодов в каждом наблюдении. Может ли кто-нибудь объяснить? –

0

Там может быть более эффективные способы сделать это, но это, кажется, работает довольно хорошо:

/*Create test datasets*/ 
data codes; 
input code; 
cards; 
1111 
1112 
3333 
5999 
; 
run; 

data big_dataset; 
infile cards truncover; 
input text $100.; 
cards; 
abcdef 1111 abcdef 
abcdef 1111 abcdef 1112 8888 
abcdef 1111 abcdef 11128888 
; 
run; 

/*Get the number of codes to use for array definition*/ 
data _null_; 
    set codes(obs = 1) nobs = nobs; 
    call symput('ncodes',nobs); 
run; 

%put ncodes = &ncodes; 

data want; 
    set big_dataset; 
    /*Define and populate array with padded codes*/ 
    array codes{&ncodes} $6 _temporary_; 
    if _n_ = 1 then do i = 1 to &ncodes;  
     set codes; 
     codes[i] = cat(' ',put(code,4.),' '); 
    end; 
    do i = 1 to &ncodes; 
     text = tranwrd(text,codes[i],' '); 
    end; 
    drop i code; 
run; 

Я ожидаю, что решение с использованием prxchange также возможно, но я не знаю, сколько работа заключается в том, чтобы создать регулярное выражение, соответствующее всем вашим кодам, по сравнению с просто заменой их по одному.

+0

Нет необходимости в шаге 'data _null_' или массиве. Просто используйте «NOBS = NOBS» и «POINT = I» в инструкции 'set codes', чтобы вы могли просто использовать один цикл DO. – Tom

+0

Я предпочитаю читать набор данных кода только один раз и хранить его в памяти, поскольку дисковый ввод-вывод является тогда O (n + m), а не O (n * m). – user667489

0

Принимая решение Тома и помещая код в хэш-таблицу. Таким образом, набор данных будет считываться только один раз, и фактический поиск выполняется довольно быстро. Если Большой набор данных действительно большой, это будет иметь огромное значение.

data want ; 
    if _n_ = 1 then do; 
    length code $4 ; 
    declare hash h(dataset:"codes (keep=code)") ; 
    h.defineKey("code") ; 
    h.defineDone() ; 
    call missing (code); 
    declare hiter hiter('h') ; 
    end; 
    set big_dataset ; 

    rc = hiter.first() ; 
    do while (rc = 0 and text ne ' ') ; 
    text = substr(tranwrd(' '||text,' '||code||' ',' '),2) ; 
    rc = hiter.next() ; 
    end ; 
    drop code rc ; 
run; 
0

Использование массив и регулярный экспресс:

proc transpose data=codes out=temp; 
var code; 
run; 

data want; 
if _n_=1 then set temp; 
array var col:; 
set big_dataset; 
do over var; 
text = prxchange(cats('s/\b',var,'\b//'),-1,text); 
end; 
drop col:; 
run; 
Смежные вопросы