7
$\begingroup$

I am trying to import data from an XML file and then perform calculations on those numeric values. When I import the data the values seem to be stored as strings. I tried using a ToExpression function on the different fields in a Cases function but the values seem to stay as strings.

So first off here is a sample of my XML file:

<ArrayOfXMLHexNode>
  <XMLHexNode>
    <ID>1</ID>
    <X>-1.000000000000000e+003</X>
    <Y>0.000000000000000e+000</Y>
    <Radius>1.000000000000000e+003</Radius>
    <Angle>3.141592653589793e+000</Angle>
    <RingIndex>0.000000000000000e+000</RingIndex>
    <RingSuborder>1.000000000000000e+000</RingSuborder>
    <DisplayOrder>1</DisplayOrder>
  </XMLHexNode>
  <XMLHexNode>
    <ID>2</ID>
    <X>1.000000000000000e+003</X>
    <Y>0.000000000000000e+000</Y>
    <Radius>1.000000000000000e+003</Radius>
    <Angle>0.000000000000000e+000</Angle>
    <RingIndex>0.000000000000000e+000</RingIndex>
    <RingSuborder>1.000000000000000e+000</RingSuborder>
    <DisplayOrder>2</DisplayOrder>
  </XMLHexNode>
  <XMLHexNode>
    <ID>3</ID>
    <X>-5.000000000000000e+002</X>
    <Y>-8.660000000000000e+002</Y>
    <Radius>1.000000000000000e+003</Radius>
    <Angle>4.188790204786391e+000</Angle>
    <RingIndex>0.000000000000000e+000</RingIndex>
    <RingSuborder>1.000000000000000e+000</RingSuborder>
    <DisplayOrder>3</DisplayOrder>
  </XMLHexNode>
</ArrayOfXMLHexNode>

And an example of how I am importing the data follows:

hexNodeData =  Import["HexNodes.xml"]

myHexNodes := 
  Cases[hexNodeData, XMLElement["XMLHexNode", _, _], Infinity];

pointsWithDisplayOrder = 
 Cases[myHexNodes, 
  XMLElement[
    "XMLHexNode", _, {___, XMLElement["X", {}, {x_}], ___, 
     XMLElement["Y", {}, {y_}], ___, 
     XMLElement["RingSuborder", {}, {subOrder_}], ___, 
     XMLElement[
      "DisplayOrder", {}, {displayOrder_}], ___}] -> {ToExpression[x],
     ToExpression[y], ToExpression[subOrder], 
    ToExpression[displayOrder]}, Infinity]

And then a few lines to try out the data I imported:

In[53]:= tempDO1 = pointsWithDisplayOrder[[3]][[3]]

Out[53]= "1.000000000000000e+000"

In[54]:= tempDO1 \[Element] Reals

Out[54]= "1.000000000000000e+000" \[Element] Reals

In[55]:= StringMatchQ[tempDO1, "1.000000000000000e+000"]

Out[55]= True

In[56]:= ToExpression[tempDO1, InputForm]

Out[56]= 1. e

In[57]:= tempDO = ToExpression[pointsWithDisplayOrder[[3]][[4]]]

Out[57]= 3

In[58]:= tempDO \[Element] Reals

Out[58]= True

So a couple questions come out of this.

  1. Why does the ToExpression's used in the Cases call not convert my data to Reals or Integers as appropiate when I store the data in the pointsWithDisplayOrder List? And to be clear I mean in the first code block where I have a line like this:

    ...-> {ToExpression[x], ToExpression[y], ToExpression[subOrder], ToExpression[displayOrder]}...

  2. How can I convert values in sql scientific notation like 5.0000e+000 into a Real Number in Mathematica? Mathematica wants to treat e is an unknown variable.

Thanks in advance.

Brian

$\endgroup$
2
  • 1
    $\begingroup$ Brian, I updated my answer with a reference to a function that is the nuts and bolts core of ImportString for this kind of data, therefore does without the massive overhead of that function and is a great deal faster. It is an undocumented internal function however, so I leave you to choose between the compromises of all three methods (ImportString, ToExpression, ParseTable). $\endgroup$
    – Mr.Wizard
    Commented Aug 20, 2012 at 13:53
  • $\begingroup$ Thanks @Mr. Wizard I will check this out when I get home. I will have to consider what precision I need out of my numbers. I can probably cut them back in size quite a bit. The work I am doing is for a hobby, not for research or for work; so I appreciate the excellent support you have provided. $\endgroup$
    – BrianWaMc
    Commented Aug 20, 2012 at 19:37

1 Answer 1

7
$\begingroup$

Update

I have found a new method for converting numeric strings using an internal function that was previously unknown to me. It is quite flexible, but its use requires care as bad input will crash the kernel. I have detailed my present understanding of the function here: ParseTable syntax.


Does this work for you?

hexNodeData /. 
 XMLElement[a_, {}, {s_String}] :> 
  XMLElement[a, {}, ImportString[s, "List"]]

I am using ImportString[s, "List"] to convert numbers in the 5.0000e+000 form.


You commented that this works, but is slow; indeed it is, but I don't have a better method for you, only alternatives. This question's answers give some:

How do you convert a string containing a number in C scientific notation to a Mathematica number?

If your number were lower precision you could use Internal`StringToDouble but e.g. Internal`StringToDouble["3.141592653589793e+000"] gives $Failed["Bignum"]. Using ToExpression can be dangerous, because as Szabolcs states:

Whenever you use ToExpression on data read from a file, you make it possible to inject code into a program even inadvertently (one can never tell what sort of erroneous input the program might get by mistake).

Nevertheless it should be faster, so I present a method using it if you choose to risk it:

Block[{e},
 hexNodeData /. 
   XMLElement[a_, {}, {s_String}] :> 
    XMLElement[a, {}, {ToExpression @ s}]
      /. {e x_ + b_ :> x*10^b, e x_ :> x}
]
$\endgroup$
2
  • $\begingroup$ This takes care of it. It does take a while to execute. I guess I might eliminate some of the un-needed fields from my XML but this definitely gets me where I need to be. $\endgroup$
    – BrianWaMc
    Commented Aug 19, 2012 at 21:27
  • 1
    $\begingroup$ @Brian I added another, faster method, with a caveat. $\endgroup$
    – Mr.Wizard
    Commented Aug 20, 2012 at 5:16

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.