- Mark as New
- Bookmark
- Subscribe
- Mute
- RSS Feed
- Permalink
- Report Inappropriate Content
Are there alternatives to negative lookbehind regular expressions with variable lengths?
Do not match if "no" or "negative" are before certain key words (infarct)|(MI)|(myocardial infarction).
SAS returns the following error: Variable length lookbehind not implemented before HERE mark in regex
Thanks.
data text;
input string $60.;
cards;
Acute infarct.
Acute MI.
Acute myocardial infarction.
Extensive infarct found in the.
Massive infarct found in the.
No infarct.
Negative infarct.
Negative for infarct.
No CT evidence of infarct.
No apparent evidence of infarct.
No apparent CT evidence of infarct.
No evidence of infarct.
No evidence of infarct.
Evidence of infarct.
Evidence for infarct.
;
/* Only Match:
Acute infarct
Acute MI
Acute myocardial infarction
Extensive infarct found in the
Massive infarct found in the
evidence of infarct
evidence for infarct
*/
data text_prx;
set text;
if _n_=1 then do;
retain re;
re = prxparse('/(?<!\bno\b.{0,225}|\bnegative\b.{0,225})((infarct)|(MI)|(myocardial infarction))/i');
putlog 'ERROR: regex is malformed';
stop;
end;
end;
if prxmatch(re,string) then infarct=1;
else infarct=0;
run;
proc print data=text_prx(drop=re);
run;
Accepted Solutions
- Mark as New
- Bookmark
- Subscribe
- Mute
- RSS Feed
- Permalink
- Report Inappropriate Content
Hello @PharmlyDoc,
How about searching for the positive and negative words separately and then comparing character positions?
data text_prx(drop=re_: pos_:);
set text;
if _n_=1 then do;
re_i + prxparse('/(infarct)|(\bMI\b)|(myocardial infarction)/i');
re_n + prxparse('/\bno\b|\bnegative\b/i');
end;
pos_i = prxmatch(re_i,string);
pos_n = prxmatch(re_n,string);
infarct = pos_i & not (0<pos_n<pos_i);
run;
(Of course, "infarct" is a substring of "myocardial infarction", but I assume that you may want to evaluate the matches, e.g., with PRXPOSN.)
- Mark as New
- Bookmark
- Subscribe
- Mute
- RSS Feed
- Permalink
- Report Inappropriate Content
Hello @PharmlyDoc,
How about searching for the positive and negative words separately and then comparing character positions?
data text_prx(drop=re_: pos_:);
set text;
if _n_=1 then do;
re_i + prxparse('/(infarct)|(\bMI\b)|(myocardial infarction)/i');
re_n + prxparse('/\bno\b|\bnegative\b/i');
end;
pos_i = prxmatch(re_i,string);
pos_n = prxmatch(re_n,string);
infarct = pos_i & not (0<pos_n<pos_i);
run;
(Of course, "infarct" is a substring of "myocardial infarction", but I assume that you may want to evaluate the matches, e.g., with PRXPOSN.)
- Mark as New
- Bookmark
- Subscribe
- Mute
- RSS Feed
- Permalink
- Report Inappropriate Content
Note, if you need to know which of many possible substrings was matched, (using PRXPOSN) you should list the longer substrings first
if _n_=1 then do;
re_i + prxparse('/\b(myocardial infarction|infarct|MI)\b/i');
re_n + prxparse('/\bno\b|\bnegative\b/i');
end;
pos_i = prxmatch(re_i,string);
pos_n = prxmatch(re_n,string);
infarct = pos_i & not (0<pos_n<pos_i);
if infarct then word = prxposn(re_i, 1, string);
- Mark as New
- Bookmark
- Subscribe
- Mute
- RSS Feed
- Permalink
- Report Inappropriate Content
Thanks, @PGStats, for chiming in. I was under the impression that "myocardial infarction" is matched first in strings like "Acute myocardial infarction" even if listed after "infarct" in the regular expression because it starts earlier in the string. However, if the regex was /(infarction|infarct)/i, then the order of the two words would make a difference for PRXPOSN. (My guess that PRXPOSN might be involved somewhere else in the OP's code was just based on the way the regex was written.)
- Mark as New
- Bookmark
- Subscribe
- Mute
- RSS Feed
- Permalink
- Report Inappropriate Content
Thanks for helping with this.
Why use
re_i + prxparse('/ /');
instead of
re_i = prxparse('/ /');
?
I would still use your method even if SAS provided a flavor of regex that allows for variable length negative lookbehind. This is an excellent workaround!!
Yes, I've found the prxposn function helpful for seeing what is being captured/matched.
- Mark as New
- Bookmark
- Subscribe
- Mute
- RSS Feed
- Permalink
- Report Inappropriate Content
You're welcome.
@PharmlyDoc wrote:
Why use
re_i + prxparse('/ /');
instead of
re_i = prxparse('/ /');
?
Just to save the RETAIN statement. The sum statement implies RETAIN and the result in re_i and re_n, respectively, is the same as with an assignment statement. (I learned this application of the sum statement to regex definitions from PGStats, years ago.)