5
$\begingroup$

I have the following list

l1 ={{{-1.342}, {-0.28}, {1.372}}, {{-1.34266}, {-0.278541}, {1.37156}}, \
  {{-1.34459}, {-0.274215}, {1.37026}}, {{-1.34769}, {-0.267169}, \
   {1.36807}}, {{-1.35177}, {-0.257626}, {1.36499}}, {{-1.35661}, \
   {-0.245864}, {1.36101}}, {{-1.36197}, {-0.232184}, {1.35611}}, \
   {{-1.3676}, {-0.216888}, {1.35026}}, {{-1.37326}, {-0.200263}, \
   {1.34346}}, {{-1.37875}, {-0.182564}, {1.33567}}}

I'd like to take it and transformit into the following form

newList = {{-1.34266, 0}, {-0.278541, 0}, {1.37156, 0}}

I have tried

{{#1, 0}, {#2, 0}, {#3, 0}}  & @@ {#1, #2, #3} & @@  l1

Which does not work. It gives the output: which is almost correct

{{{-1.34266}, 0}, {{-0.278541}, 0}, {{1.37156}, 0}}

I want to feed this data into the following function to animate the data:

Animate[ListPlot[{{l1[[n]][[1]], 0}, {l1[[n]][2]], 0}, {l1[[n]][3]], 0}}, 
  PlotStyle -> PointSize[0.1], PlotRange -> {{-2, 2}, {-1, 1}}], {n, 
  1, 750, 1}]

I thought It would be easiest to first construct the list we want and then pass it directly to ListPlot.

$\endgroup$
4
  • 2
    $\begingroup$ Try this: l1 /. {x_} -> {x, 0} $\endgroup$
    – Peltio
    Apr 13, 2014 at 4:20
  • $\begingroup$ Another way is this: Transpose[{Flatten[l1], Table[0, {Length[l1]}]}]. But it seems that your real problem is structurally different from the simplified version you gave. Please give an excerpt of your real data. $\endgroup$
    – Peltio
    Apr 13, 2014 at 4:23
  • $\begingroup$ @Pelito. this works for me. Now i'm trying to take every row of l1 to make a new table. In the example above I posted only the first row ie l1[[1]]. $\endgroup$
    – olliepower
    Apr 13, 2014 at 4:26
  • 1
    $\begingroup$ You might want to turn my first suggestion into a procedure like extendList[l1_] and then map that function on your big list. $\endgroup$
    – Peltio
    Apr 13, 2014 at 4:32

4 Answers 4

7
$\begingroup$

Acting on the simple list you gave

l1 = {{-1.34266}, {-0.278541}, {1.37156}}

is simple:

newlist= l1 /. {x_} :> {x, 0}

Now, let's work on the actual list. I'll call it bigList

bigList = {
{{-1.342}, {-0.28}, {1.372}}, {{-1.34266}, {-0.278541},{1.37156}},
{{-1.34459}, {-0.274215}, {1.37026}}, {{-1.34769}, {-0.267169}, {1.36807}},
{{-1.35177}, {-0.257626}, {1.36499}}, {{-1.35661}, {-0.245864}, {1.36101}},
{{-1.36197}, {-0.232184}, {1.35611}}, {{-1.3676}, {-0.216888}, {1.35026}},
{{-1.37326}, {-0.200263}, {1.34346}}, {{-1.37875}, {-0.182564}, {1.33567}}
}

You could recycle the previous method, encapsulate it into a procedure like this

extendList[l1_] := l1 /. {x_} :> {x, 0}

and then map it on every element of your bigList

extendList /@ bigList

Or you might like this approach better:

newList = bigList /. {{x_}, {y_}, {z_}} :> {{x, 0}, {y, 0}, {z, 0}}

In my original answer I had used an immediate replacement -> because I assumed that when one applies a replacement rule 'on the go', one can always make sure the variables used are unique and never had any values assigned to them before that moment. But if you fear that the variable names x, y and z might have values assigned to them when you invoke your procedures (and this might be the case when you encapsulate the code in a wrapper procedure without making those variable local, it is better to use a delayed replacement. Following Mr. Wizard's advice, I therefore switched to delayed replacement in the above definition.

Moreover, I have assumed you are not passing a list of three elements to the procedure.Some sort of control can be added by making sure that x, y and z are numeric, specifying a type for the pattern matching, as in x_Real or x_?NumericQ. But I am too lazy for that : -)

$\endgroup$
5
  • $\begingroup$ This is extremely helpful. I'm having a small issue with Animate now. It plots all of the positions of the points along the line at the same time instead of animating it. However since you answered my original question I will accept your answer after a few others have had a chance to chime in. I'm trying: Animate[ListPlot[ bigList /. {{x_}, {y_}, {z_}} -> {{x, 0}, {y, 0}, {z, 0}}, PlotStyle -> PointSize[0.1], PlotRange -> {{-2, 2}, {-1, 1}}], {n, 1, 10, 1}] $\endgroup$
    – olliepower
    Apr 13, 2014 at 4:49
  • 1
    $\begingroup$ I don't understand the role of n, in your Animate (it does not appear anywhere else). Perhaps you should define` bigDynList[n_]:=Take[bigList,n]` and then try again with bigDynList[n] in Animate's body $\endgroup$
    – Peltio
    Apr 13, 2014 at 6:34
  • $\begingroup$ I think I am implementing Animate wrong. The data corresponds to position data along the x axis. This is why the 'y' coordinate is zero. I want to animate the positions of particle 1,2, and 3, corresponding to {x,0}, {y,0}, {z,0}. Does this make sense? Thanks so much for the help. $\endgroup$
    – olliepower
    Apr 13, 2014 at 16:52
  • $\begingroup$ @Peltio In my opinion you should always localize named patterns with RuleDelayed unless you specifically know you need different behavior. I suggested editing this answer to use :> in each case. $\endgroup$
    – Mr.Wizard
    Apr 13, 2014 at 17:17
  • $\begingroup$ @Mr.Wizard, I updated the answer accordingly. But I believe that immediate replacement can still be safely used (the code look nicer with that little arrow :-) ) by making sure the variables have unique names. This is only valid when applying the rule 'on the go'. $\endgroup$
    – Peltio
    Apr 13, 2014 at 17:54
6
$\begingroup$

As with many things in Mathematica there are a great many ways to perform such a simple operation. Which one you choose can depend on what you are comfortable with and what performance level you require. I shall list several that come to mind. Some options have already been provided in other answer; I shall include them here for completeness.

A small data sample to keep things clean:

a = {{{1}, {2}, {3}}, {{4}, {5}, {6}}};

All lines return the same output; I shall include it only once:

a /. {x_} :> {x, 0}
Replace[a, {x_} :> {x, 0}, {2}]
Apply[{#, 0} &, a, {2}]
Join[a, 0*l1, 3]
ArrayPad[a, {0, 0, {0, 1}}]
a.{{1, 0}}
{{{1, 0}, {2, 0}, {3, 0}}, {{4, 0}, {5, 0}, {6, 0}}}

A special note regarding the last method (Dot): the "zero" will inherit the type of the data, meaning that if a is an array of Reals (as produced by N) so too will be the zeros:

N[a].{{1, 0}}
{{{1., 0.}, {2., 0.}, {3., 0.}}, {{4., 0.}, {5., 0.}, {6., 0.}}}

This is also true of the Join method, though by adding Chop that could be changed.

Now a large (packed) data sample for performance timings:

a = RandomReal[{-9, 9}, {500000, 3, 1}];

A timing function:

SetAttributes[timeAvg, HoldFirst]

timeAvg[func_] := Do[If[# > 0.3, Return[#/5^i]] & @@ Timing @ Do[func, {5^i}], {i, 0, 15}]

Timings:

a /. {x_} :> {x, 0}              // timeAvg
Replace[a, {x_} :> {x, 0}, {2}]  // timeAvg
Apply[{#, 0} &, a, {2}]          // timeAvg
Join[a, 0*a, 3]                  // timeAvg
ArrayPad[a, {0, 0, {0, 1}}]      // timeAvg
a.{{1, 0}}                       // timeAvg
0.453

0.405

0.515

0.453

0.259

0.03372

(Timings all performed in version 7.)

Keeping packed arrays packed

As you can see the Dot method is an order of magnitude faster than the rest. This largely due to the fact that the types are matched allowing the array to remain packed (because Dot is able to handle arrays without unpacking).

By modifying the ArrayPad method to also return a packed array (matching types, by specifying a machine precision zero for the padding element) we can get even greater speed:

ArrayPad[a, {0, 0, {0, 1}}, 0`] // timeAvg
0.01808

Likewise Alexey's Join method can be made faster (in version 7) by matching types:

Join[a, 0` a, 3] // timeAvg
0.02744

(Version 7 behaves differently in that 0 * 1.1 returns 0, whereas later versions return 0.. An explicit imprecise zero is needed to return an array of Reals.)

This improvement cannot be applied to the Replace, ReplaceAll and Apply methods because these functions are not (generally) capable of preserving packed arrays.

$\endgroup$
13
  • $\begingroup$ (+1) In version 8.0.4 (32 bit) the Join method with your test is the fastest: 0.25 sec as compared to 0.297 sec for Dot. Also Replace at negative level ({-2}) is substiantly more general way to do such things as compared to Replace at positive level. $\endgroup$ Apr 13, 2014 at 8:23
  • $\begingroup$ @Alexey That seems very slow for Dot; Do you get any messages with On["Packing"]? How is the timing for the modified ArrayPad method? I agree with your note regarding the levelspec. $\endgroup$
    – Mr.Wizard
    Apr 13, 2014 at 8:27
  • 1
    $\begingroup$ @Alexey I think I should have been aware of that version difference; I get {0, 0, 0} which explains the performance results. $\endgroup$
    – Mr.Wizard
    Apr 13, 2014 at 8:48
  • 1
    $\begingroup$ I like your Dot method: it works independently from the depth of the list. It is a shame that Join cannot handle negative levelspec. The workaround is to pass Depth@a - 1 as last argument. $\endgroup$ Apr 13, 2014 at 9:03
  • 3
    $\begingroup$ Dot is the winner here, with v9 on Windows. Closest I can get to it is the rather ugly Transpose[Join[Transpose[a, {2, 3, 1}], ConstantArray[0., RotateRight@Dimensions@a]], {3, 1, 2}] which takes about 20% longer. $\endgroup$ Apr 13, 2014 at 11:27
5
$\begingroup$

The first aim can be accomplished tersely:

{#,0}&@@@l1

The 'bigList':

{#, 0} & @@@ # & /@ bigList

As well as replacement rules. More complex nesting would require more complex approaches.

$\endgroup$
0
3
$\begingroup$

If it is known that all the elements are Real, the solution becomes pretty straightforward:

l1/.{x_Real}:>{x,0}
bigList/.{x_Real}:>{x,0}

More general solution is to Replace at level {-2}:

Replace[l1, {x_} :> {x, 0}, {-2}]

The same can be achieved by Joining the array with itself multiplied by zero:

Join[l1, 0*l1, 3]

Or more generally (making it independent from the depth of the list):

Join[l1, 0*l1, Depth@l1 - 1]
$\endgroup$
1
  • $\begingroup$ +1 for Join. Now I have to add it to my timings. $\endgroup$
    – Mr.Wizard
    Apr 13, 2014 at 8:09

Your Answer

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

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