如何查找和替换 SAS 数据集中的特定文本?

How can I find and replace specific text in a SAS data set?

我有一个数据集,其中包含 400 个 4 位代码的观察值,我想在两边用 space 填充

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
...

注意:我只想替换两边用 space 填充的 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;

可能有更有效的方法来执行此操作,但这似乎工作得很好:

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

data big_dataset;
infile cards truncover;
input text 0.;
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}  _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 的解决方案也是可能的,但我不确定构建一个匹配所有代码的正则表达式与一个接一个地替换它们相比需要多少工作。

您可以只使用 DO 循环来扫描大型数据集中每条记录的小型代码数据集。如果您想使用 TRANWRD() 功能,则需要添加额外的 space 个字符。

data want ;
  set have ;
  length code  ;
  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 列表中读取记录。在 SET 语句上使用 POINT= 选项可以让您多次读取文件。如果 TEXT 字符串为空,则 WHILE 子句将停止,因为此时无需继续寻找要替换的代码。

如果您的代码列表足够小并且您可以获得正确的正则表达式,那么您可以尝试使用 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;

采用 Tom 的解决方案并将代码查找放入散列 table。因此,数据集只会被读取一次,实际查找速度非常快。如果大型数据集真的很大,这将产生巨大的影响。

data want ;
  if _n_ = 1 then do;
    length code  ;
    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;

使用数组和正则表达式:

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;