11
$\begingroup$

I have a list of strings like this

test1 = {
"psi-mi:dvl1_mouse(display_long)|uniprotkb:Dvl1(gene name)|psi-mi:Dvl(display_short)|uniprotkb:Dvl(gene name synonym)|uniprotkb:DSH homolog 1(gene name synonym)",
"psi-mi:nxn_mouse(display_long)|uniprotkb:Nxn(gene name)|psi-mi:Nxn(display_short)|uniprotkb:Gn25(gene name synonym)|uniprotkb:Protein Red-1(gene name synonym)",
"psi-mi:lrp8_mouse(display_long)|uniprotkb:Apolipoprotein Ereceptor 2(gene name synonym)|uniprotkb:Lrp8(gene name)|psi-mi:Lrp8(display_short)|uniprotkb:Apoer2(gene name synonym)",
"psi-mi:reln_mouse(display_long)|uniprotkb:Reln(gene name)|psi-mi:Reln(display_short)|uniprotkb:Rl(gene name synonym)|uniprotkb:Reeler protein(gene name synonym)"}  

and I want to get the "gene name" which place in between the "uniprotkb:" and "(gene name)|". So I use the function StringCases and Shortest like this, but I get the wrong output

Map[StringCases[#, Shortest["|uniprotkb:" ~~ aa__ ~~ "(gene name)"] -> aa] &, test1]

and the result is

{{"Dvl1"},
 {"Nxn"},
 {"Apolipoprotein E receptor 2(gene name synonym)|uniprotkb:Lrp8"},
 {"Reln"}}

the third element is not correct, becuase I just want to get "Lrp8". Can somebody tell me why this happened, and what should I do? Any suggestions will be appreciated. Thank you very much.

$\endgroup$
  • 1
    $\begingroup$ I don't know be here's different way: StringCases[test1, "|uniprotkb:" ~~ a : Except["|"] .. ~~ "(gene name)" :> a] $\endgroup$ – Kuba Jan 22 '15 at 13:59
  • $\begingroup$ Wonderful! It works, thank you very much $\endgroup$ – Zihu Guo Jan 22 '15 at 14:02
  • 3
    $\begingroup$ strongly related : mathematica.stackexchange.com/q/18193/5467 $\endgroup$ – andre314 Jan 22 '15 at 16:23
16
$\begingroup$

The behaviour we see here is due to the semantics of the regular expression pattern-matching process. Notionally, the string is scanned from left to right. At each character position, an attempt is made to match the string by looking ahead from that position. If there is no match, scanning moves onto the next character. If there is a match, then (by default) scanning resumes from the next character after the end of the match.

In the case at hand, consider what happens when the scanning position is the vertical bar at the beginning of the substring "|uniprotkb:Apolipoprotein". The scanner looks ahead from that position for a substring that ends "(gene name)". Since there are no constraints upon what the intervening characters (aa) will match, it happily scans right past a vertical bar and into what we consider to be the next entry. Having found match, it returns it and resumes scanning from the vertical bar introducing "|psi-mi:Lrp8".

There is some subtlety to meaning of the term Shortest. It does not mean find the shortest possible match relative to the lengths of other possible matches anywhere in the string. What it means is that the scanner, when looking ahead from any given position, is to stop as soon as it finds a match. The default (non-shortest) behaviour is to continue scanning, and to keep extending the match until there is no other possibility. The default behaviour is normally labelled by the jargon term "greedy". Mathematica's Shortest behaviour is usually labelled "lazy", or sometimes "reluctant".

A simple solution, as noted by @Kuba and @belisarius, is to constrain what can be matched by aa:

StringCases[test1, "|uniprotkb:" ~~ aa:Except["|"].. ~~ "(gene name)" :> aa]
(* {{"Dvl1"}, {"Nxn"}, {"Lrp8"}, {"Reln"}} *)

By excluding vertical bars from the intervening characters, we are essentially telling the scanner that it is not permitted to extend a match across entry boundaries.

$\endgroup$
  • $\begingroup$ No, I still do not understand how this can be concluded from docs ;) $\endgroup$ – Kuba Jan 22 '15 at 16:06
  • 2
    $\begingroup$ @Kuba Not the Mathematica docs, certainly. One needs to dig into the PCRE docs -- and it would probably help to put on the language-lawyer hat. :) $\endgroup$ – WReach Jan 22 '15 at 16:37
  • $\begingroup$ Thank you for your patience to answer! Now I am quite clear to this problem. $\endgroup$ – Zihu Guo Jan 23 '15 at 0:17
3
$\begingroup$
StringCases[test1,"|uniprotkb:" ~~ aa : (WordCharacter ..) ~~ "(gene name)" ->  aa] 

(* {{"Dvl1"}, {"Nxn"}, {"Lrp8"}, {"Reln"}} *)
$\endgroup$
  • $\begingroup$ Thank you very much! but I still want to know if it is possible get the right answer wiht 'Shortest' $\endgroup$ – Zihu Guo Jan 22 '15 at 14:24
2
$\begingroup$

If you are strictly looking for the shortest string between "|uniprotkb:" and "(gene name)" in each record, this is a way :

StringCases[test1, "|uniprotkb:" ~~ x__ ~~ "(gene name)" :> x, Overlaps -> True] // 
(First@SortBy[#, StringLength] &) /@ # & 

{Dvl1,Nxn,Lrp8,Reln}

$\endgroup$
1
$\begingroup$

The code provided in the Question seems to be selecting the first instance rather than the Shortest, as can be seen by moving |uniprotkb:Lrp8(gene name) earlier in test1[[3]].

However, the following seems to work well.

StringCases[test1, "|uniprotkb:" ~~ aa__ ~~ "(gene name)" /; (StringLength[aa] <= 8) :> aa]

(* {{"Dvl1"}, {"Nxn"}, {"Lrp8"}, {"Reln"}} *)
$\endgroup$
  • $\begingroup$ yes, it works in this problem. But I am afraid when the length of aa is more than 8, I would get some unpected result $\endgroup$ – Zihu Guo Jan 23 '15 at 0:28
  • $\begingroup$ @ZihuGuo, although I thought that genes all had short names. $\endgroup$ – bbgodfrey Jan 23 '15 at 0:31
1
$\begingroup$

MMA, V9:

StringCases[test1, __ ~~ "uniprotkb:" ~~ x__ ~~ "(gene name)|" :> x]
(*{{"Dvl1"}, {"Nxn"}, {"Lrp8"}, {"Reln"}}*)
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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