5
$\begingroup$

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?

$\endgroup$
2
  • 1
    $\begingroup$ CoefficientRules[#, Variables@#] &@ToExpression[str] $\endgroup$ Commented Jan 30, 2014 at 13:53
  • 1
    $\begingroup$ Coefficient[#, Variables@#] &@ToExpression[str] ... etc $\endgroup$ Commented Jan 30, 2014 at 13:53

6 Answers 6

6
$\begingroup$

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

$\endgroup$
4
$\begingroup$

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"}}
$\endgroup$
4
  • $\begingroup$ 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? $\endgroup$
    – Mr.Wizard
    Commented Jan 30, 2014 at 15:56
  • $\begingroup$ @Mr.Wizard ok, done. Yes it is not general solution. Also free coefficient is skipped, I don't know if it is acceptable. $\endgroup$
    – Kuba
    Commented Jan 30, 2014 at 16:04
  • $\begingroup$ I skipped free coefficients as well because it seemed to me that the OP wanted to pair values. +1 with the correction. $\endgroup$
    – Mr.Wizard
    Commented Jan 30, 2014 at 16:14
  • $\begingroup$ @Mr.Wizard Ha, you were not careful enough, it skipped "+ x2" cases too! $\endgroup$
    – Kuba
    Commented Jan 30, 2014 at 16:51
3
$\begingroup$

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.

$\endgroup$
4
  • $\begingroup$ 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 $\endgroup$ Commented Jan 30, 2014 at 14:17
  • 2
    $\begingroup$ @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. $\endgroup$ Commented Jan 30, 2014 at 15:06
  • $\begingroup$ Identifying problems is quite different from having solutions :) $\endgroup$ Commented Jan 30, 2014 at 15:13
  • $\begingroup$ Thanks guys, I can work with that. It helps me a lot to see different approaches to a problem btw :) $\endgroup$
    – holistic
    Commented Jan 30, 2014 at 15:22
2
$\begingroup$

Using jVincent's ContextScope[]

m`str1 = "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`}}
*)
$\endgroup$
2
$\begingroup$

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}}
$\endgroup$
5
  • $\begingroup$ You missed what I've missed: "y = x2" $\endgroup$
    – Kuba
    Commented Jan 30, 2014 at 17:20
  • $\begingroup$ @Kuba, Ah, thanks! $\endgroup$
    – RunnyKine
    Commented Jan 30, 2014 at 17:23
  • $\begingroup$ @Kube see my edit please. $\endgroup$
    – RunnyKine
    Commented Jan 31, 2014 at 2:43
  • $\begingroup$ it seems ok now :) also more tidy than mine +1 $\endgroup$
    – Kuba
    Commented Jan 31, 2014 at 3:26
  • $\begingroup$ @Kuba, thanks for looking through it :) $\endgroup$
    – RunnyKine
    Commented Jan 31, 2014 at 3:45
0
$\begingroup$
str = 
 "y = 0.97*x1 + 0.521*x2 - 30.21 - 0.07431*x3 - 0.126*x4 - 0.1939*x5 - 0.361*x6";

Using SequenceCases and SequenceReplace

SequenceCases[
 ToExpression @ SequenceReplace[
   StringSplit[str, {" ", "*"}][[3 ;;]],
   {{"-", x_} :> "-" <> x, {"+"} :> Nothing}],
 x : {_?NumericQ, _Symbol} :> ToString /@ x]

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

$\endgroup$

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.