6
$\begingroup$

For a Natural Language Processing analysis task it's desirable to highlight a text string programmatically according to XML tags that reference specific words or phrases.

The XML tags specify the character ranges in the input string as well as give other information such as identifiers and other codes.

Since character ranges are provided in the XML markup, originally I thought StringReplacePart would do the job, but it doesn't seem possible to transform the string into more general expressions, eg using Subscript or Style.

For example (please excuse I'm showing only a fragment due to Protected Health Information), given the (partial) input text:

In[566]:= StringTake[I2B2[1, text], -500]

Out[566]= " pain and was treated with Percocet . She went for a \
debridement of her left calf lesion on 10/2/93 and was started \
empirically on IV ceftriaxone which was changed to po doxycycline on \
the day of discharge . A follow-up CT scan was done which did not \
show any evidence for splenomegaly or hepatomegaly . The 1 cm cyst \
which was seen in 10/92 was still present . There was a question of a \
cyst in her kidney with a stone right below the cyst , although this \
did not seem to be clinically significant ."

And the XML tag:

In[581]:= Cases[I2B2[1, tags], 
 XMLElement[
  "EVENT", {id_, start_, end_, text_ -> "a stone", modality_, 
   polarity_, type_}, _]]

Out[581]= {XMLElement[
  "EVENT", {"id" -> "E75", "start" -> "2476", "end" -> "2483", 
   "text" -> "a stone", "modality" -> "POSSIBLE", "polarity" -> "POS",
    "type" -> "PROBLEM"}, {}]}

The following StringReplacePart works (to ALL CAPS) since the output is still a string:

In[577]:=  StringReplacePart[
 StringTake[I2B2[1, text], -500],
 "A STONE", {2476, 
   2483 - 1} - ((I2B2[1, text] // StringLength ) - 500)]

Out[577]= " pain and was treated with Percocet . She went for a \
debridement of her left calf lesion on 10/2/93 and was started \
empirically on IV ceftriaxone which was changed to po doxycycline on \
the day of discharge . A follow-up CT scan was done which did not \
show any evidence for splenomegaly or hepatomegaly . The 1 cm cyst \
which was seen in 10/92 was still present . There was a question of a \
cyst in her kidney with A STONE right below the cyst , although this \
did not seem to be clinically significant ."

But the following does not since I'm trying to highlight in Red using Style:

In[578]:=  StringReplacePart[
 StringTake[I2B2[1, text], -500],
 Style["a stone", 
  Red], {2476, 2483 - 1} - ((I2B2[1, text] // StringLength ) - 500)]

Out[578]= StringReplacePart[" pain and was treated with Percocet . \
She went for a debridement of her left calf lesion on 10/2/93 and was \
started empirically on IV ceftriaxone which was changed to po \
doxycycline on the day of discharge . A follow-up CT scan was done \
which did not show any evidence for splenomegaly or hepatomegaly . \
The 1 cm cyst which was seen in 10/92 was still present . There was a \
question of a cyst in her kidney with a stone right below the cyst , \
although this did not seem to be clinically significant .", \!\(\*
StyleBox["\<\"a stone\"\>",
StripOnInput->False,
LineColor->RGBColor[1, 0, 0],
FrontFaceColor->RGBColor[1, 0, 0],
BackFaceColor->RGBColor[1, 0, 0],
GraphicsColor->RGBColor[1, 0, 0],
FontColor->RGBColor[1, 0, 0]]\), {413, 419}]

Are there functions similar to StringReplacePart that can output more general expressions? Or in any case how to perform the desired highlighting with Subscript and Style?

$\endgroup$
2
  • $\begingroup$ This is a potentially very deep question. While styling a single section of a string (which becomes a Row) is trivial, styling multiple sections, potentially overlapping, becomes far from trivial. $\endgroup$
    – Mr.Wizard
    Jun 30, 2012 at 22:25
  • $\begingroup$ Related: (7008) $\endgroup$
    – Mr.Wizard
    Feb 6, 2017 at 22:22

3 Answers 3

5
$\begingroup$

As far as I know you can't put style information in strings themselves. They're just a bunch of characters from a character table. You can style the output of a string, but not put that in a string itself. So, StringReplacePart doesn't make sense here.

You could solve it as follows:

Row[{"There was a question of a cyst in her kidney with", 
      Style[" a stone", Red], 
      " right below the cyst , although this did not seem to be clinically significant ."}]

Mathematica graphics

or you could think of working in low-level StyleBox-es.

You could use StringSplit to split the string around "a stone", or use StringPosition and StringTake.

$\endgroup$
1
  • $\begingroup$ Thanks Sjoerd. StringSplit can't be used here because repeated substrings can have unique identifiers. StringTake followed by extracting the complementary ranges and sort them before applying Row works: complementaryRanges = Cases[ ReplacePart[(# - {0, 1}) & /@ Partition[ Drop[Flatten[ SortBy[Join[events[[All, 1]], timex[[All, 1]]], #[[1]] &]], 1], 2, 2, 1], {-1, 2} -> StringLength[I2B2[1, text]]], {s_, e_} /; e > s];Row[Last /@ SortBy[Join[events, timex, {#, StringTake[I2B2[file, text], #]} & /@ complementaryRanges], #[[1]] &]] $\endgroup$ Jul 1, 2012 at 20:00
4
$\begingroup$

I think you can use StyleBox in a String like this:

"\*StyleBox[\"a stone\", FontColor->RGBColor[1, 0, 0]]"

which will give a result the same as Style["a stone", Red].

Note you should specify color by RGBColor[...] but not Red, I think the reason is the same as this post.

Edit:

A more complicated example with SubscriptBox:

StringReplacePart["1234567890",
           "\*StyleBox[\!\(XXXX\_2\),FontColor->RGBColor[1,0,0]]",
                  {5, 7}]

Boxes have special form in String, you can use ToString[exp,form] to get them:

ToString[
         Style[
               HoldForm[
                        Sum[(Derivative[k][f][0]/k!)*x^k, {k, 0, Infinity}]
                       ],
               FontFamily -> "Times"],
         TraditionalForm]//FullForm
$\endgroup$
1
  • $\begingroup$ Thanks Silvia, that offers another way to go about it. I ultimately coded it using StringTake but that involved additional code to select complementary ranges and sorting. $\endgroup$ Jul 1, 2012 at 19:54
3
$\begingroup$

I needed some similar task that it was basically about, take a whatever text ( a string) and replace or highlighting some word or list of words with color, to notice them. I did this:

texto = "Este seńor JUANCHO, al ver a este seńor al cual yo estaba \
asdfasdf, viendo que asdfadsfasdf, entonces me llama por \
teléfono a la casa y me dice"

palabra = "en";

Row[Riffle[StringSplit[texto, palabra ], 
  Style[palabra, Red, FontWeight -> Bold]]]

enter image description here

$\endgroup$
2
  • $\begingroup$ Very nice and compact, thank you. What if you had multiple distinct substrings to highlight in different colors? $\endgroup$ Aug 26, 2013 at 15:53
  • $\begingroup$ Actually, I should do that with a list, so I use a iterative For over List, and with !StringFreeQ of regular expression from word in List, highlight it. $\endgroup$ Aug 27, 2013 at 18:06

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.