# Isolating characters in a string

I'm struggling to find the right function in mathematica to isolate specific characters in a string. I have got a formula as string (I imported it from an excelfile) like this:

y = 0.97*x1 + 0.521*x2 - 30.21 - 0.07431*x3 - 0.126*x4 - 0.1939*x5 - 0.361*x6

Now I want to isolate the coefficients, e.g. 0.97,0.521 from the x's and maybe store it seperately, so that I know which coefficients were for which x. I thought this would be easily done and I browsed through the help of mathematica, but I couln't find any example which helps me so far. Maybe some of you can help me out which functions is best for this task?

-
CoefficientRules[#, Variables@#] &@ToExpression[str] – Dr. belisarius Jan 30 '14 at 13:53
Coefficient[#, Variables@#] &@ToExpression[str] ... etc – Dr. belisarius Jan 30 '14 at 13:53

Using the internal expression parser:

string = "y = 0.97*x1 + 0.521*x2 - 30.21 - 0.07431*x3 - 0.126*x4 - 0.1939*x5 - 0.361*x6";

Cases[
ToHeldExpression[string],
s_Symbol * Except[_Symbol, n_?NumericQ] :> {HoldForm[s], n},
{-2}
]

{{x1,0.97}, {x2,0.521}, {x3,-0.07431}, {x4,-0.126}, {x5,-0.1939}, {x6,-0.361}}

• Note use of HoldForm to keep Symbols unevaluated in case they have values assigned.

• In this simple case the rule s_Symbol * n_ :> {HoldForm[s], n} could be used but I believe the code above is more robust.

• ToHeldExpression is a deprecated function but still entirely usable, and more concise than ToExpression[string, InputForm, Hold].

-

This answer is just for exercise purposes, I would use Coefficient way.

string = "y = 0.97*x1 + 0.521*x2 - 30.21 - 0.07431*x3 - 0.126*x4 - 0.1939*x5 - 0.361*x6";

StringCases[
StringReplace[string, "- " -> "-"],
c : NumberString ~~ _ ~~ x : ("x" ~~ DigitCharacter) :> {c, x}]

{{"0.97", "x1"}, {"0.521", "x2"}, {"-0.07431", "x3"}, {"-0.126", "x4"},
{"-0.1939", "x5"}, {"-0.361", "x6"}}


When there are cases like "+ x2" we need one more replacement:

string = "y = 0.97*x1 + x2 - 30.21 - x3 - 0.126*x4 - 0.1939*x5 - 0.361*x6";

StringCases[
Fold[StringReplace, string, {s : ("+ " | "- ") ~~ "x" :> s <> "1.*x", "- " -> "-"}]
,
c : NumberString ~~ _ ~~ x : ("x" ~~ DigitCharacter) :> {c, x}]

{{"0.97", "x1"}, {"1.", "x2"}, {"-1.", "x3"}, {"-0.126", "x4"},
{"-0.1939", "x5"}, {"-0.361", "x6"}}

-
I assumed that the x's were arbitrary symbols, but of that is not the case this is quite good. However you lost the signs of the coefficients which may not be acceptable; perhaps you could add an alternative that retains them? – Mr.Wizard Jan 30 '14 at 15:56
@Mr.Wizard ok, done. Yes it is not general solution. Also free coefficient is skipped, I don't know if it is acceptable. – Kuba Jan 30 '14 at 16:04
I skipped free coefficients as well because it seemed to me that the OP wanted to pair values. +1 with the correction. – Mr.Wizard Jan 30 '14 at 16:14
@Mr.Wizard Ha, you were not careful enough, it skipped "+ x2" cases too! – Kuba Jan 30 '14 at 16:51

There are several ways it might be done. This is one example. Here is something like your string:

str = "y=0.97*x1+0.52*x2-30.21-0.07*x3";


I do not take the full version, since you did not give it in M form, which normally you should have done. Now, let us make a list out of it:

 list = List @@ ToExpression[str]
(* {-30.21, 0.97 x1, 0.52 x2, -0.07 x3}   *)


This list is transformed into required one as follows:

 list /. a_ /; Characters[ToString@a][[1]] == "x" -> 1

(*   {-30.21, 0.97, 0.52, -0.07}  *)


Here is another simple solution based on the assumption that you have always the numeration of x-s monotonously increasing,i.e. the list of three elements will be x1, x2 x3 and not x1, x28, x5. In this case one can do as follows:

   rule = Table[
ToExpression[ToString[x] <> ToString[i]] -> 1, {i, 1,
Length[list] - 1}]

(*  {x1 -> 1, x2 -> 1, x3 -> 1}   *)


and then using the list from the previous solution:

    list /. rule

(*   {-30.21, 0.97, 0.52, -0.07}  *)


That' it. Have fun.

-
The problems with this kind of approach are that 1) y gets evaluated, which is an undesirable side effect and also, 2) if the xn already have assigned values in the .nb it fails. That's why I posted my "solutions" as comments in the question – Dr. belisarius Jan 30 '14 at 14:17
@belisarius You need no excuses to post a different solution: the more solutions - the better. I love to learn from others to see possibilities I failed to see myself. – Alexei Boulbitch Jan 30 '14 at 15:06
Identifying problems is quite different from having solutions :) – Dr. belisarius Jan 30 '14 at 15:13
Thanks guys, I can work with that. It helps me a lot to see different approaches to a problem btw :) – holistic Jan 30 '14 at 15:22
mstr1 = "y = 0.97*x1 + 0.521*x2 - 30.21 - 0.07431*x3 - 0.126*x4 - 0.1939*x5 - 0.361*x6";
ContextScope["m", {SymbolName /@ Variables@#,
Coefficient[#, Variables@#]} &@ToExpression[str1] // Transpose]
(*
{{"x1", 0.97}, {"x2", 0.521}, {"x3", -0.07431},
{"x4", -0.126}, {"x5", -0.1939}, {"x6", -0.361}}
*)

-

Since I'm still new to String Patterns here is another entry using StringCases.

string = "y = 0.97*x1 + 0.521*x2 - 30.21 - 0.07431*x3 - 0.126*x4 - 0.1939*x5 - 0.361*x6";

StringCases[string, s : ({" ", " + ", " - "} ~~ NumberString) ~~ "*" ~~
x : ("x" ~~ DigitCharacter ..) :> {x, ToExpression@s}]


Gives:

{{"x1", 0.97}, {"x2", 0.521}, {"x3", -0.07431}, {"x4", -0.126}, {"x5", -0.1939}, {"x6",-0.361}}


Note the use of DigitCharacter .. for situations with variables like x10, x11 etc.

Kuba pointed out that cases like "y = x2" cannot be handled by the above method, so we do some StringReplacement before proceeding:

string2 = "y = 0.97*x1 + x2 - 30.21 - x3 - 0.126*x4 - 0.1939*x5 - 0.361*x6";


Now

rp = StringReplace[string2, " " ~~ x : ("x" ~~ DigitCharacter ..) :> " 1" ~~ "*" ~~ x]


Then as before:

StringCases[rp, s : ({" ", " + ", " - "} ~~ NumberString) ~~ "*" ~~
x : ("x" ~~ DigitCharacter ..) :> {x, ToExpression@s}]

{{"x1", 0.97}, {"x2", 1}, {"x3", -1}, {"x4", -0.126}, {"x5", -0.1939}, {"x6", -0.361}}

-
You missed what I've missed: "y = x2" – Kuba Jan 30 '14 at 17:20
@Kuba, Ah, thanks! – RunnyKine Jan 30 '14 at 17:23
@Kube see my edit please. – RunnyKine Jan 31 '14 at 2:43
it seems ok now :) also more tidy than mine +1 – Kuba Jan 31 '14 at 3:26
@Kuba, thanks for looking through it :) – RunnyKine Jan 31 '14 at 3:45