5
$\begingroup$

I have the feeling this is a very basic question, but I can't seem to find the way to solve this easily.

I have imported a set of data from a txt file into a table so the resulting list has the following structure:

{{x1,y1,f1},{x2,y2,f2},...}

I want it to reshape it into this form:

{{{x1,y1},f1},{{x2,y2},f2},...}

I apologize because I know it must be trivial but I haven't found the way yet. I've always had problems manipulating lists in Mathematica so, if anyone has any resource I can use to learn about this it'd be very much appreciated.

Regards

$\endgroup$
3
  • 2
    $\begingroup$ Look at the output for Apply[{{#1, #2}, #3} &, {a, b, c}] and it should help you. Check Apply for more details. $\endgroup$
    – Jason B.
    Feb 4, 2019 at 22:27
  • 1
    $\begingroup$ Also possible is the following: reshape = Curry[Replace, {3, 1, 2}][{s_, t_, u_} :> {{s, t}, u}, {1}]. Then {{x1, y1, f1}, {x2, y2, f2}} // reshape gives the desired result. $\endgroup$ Feb 4, 2019 at 23:53
  • $\begingroup$ @JasonB. this should be Apply[{{#1, #2}, #3} &, {{x1, y1, f1}, {x2, y2, f2}}, {1}] $\endgroup$ Feb 5, 2019 at 0:47

4 Answers 4

4
$\begingroup$

I think my vote is this so far, but I bet there's even cleaner/more clever ways to do this:

data = {{x1, y1, f1}, {x2, y2, f2}}
Replace[data, {x_, y_, f_} :> {{x, y}, f}, {1}]

I like the Apply solution just fine too:

Apply[{{#1, #2}, #3} &, {{x1, y1, f1}, {x2, y2, f2}}, {1}] 

For larger data they perform similarly, with Replace winning slightly:

In[53]:= data=(ToExpression/@{"x"<>#,"y"<>#,"f"<>#})&/@(ToString[#]&/@Range[1000000]);
Apply[{{#1,#2},#3}&,data,{1}]//RepeatedTiming//First
Replace[data,{x_,y_,f_}:>{{x,y},f},{1}]//RepeatedTiming//First
Out[54]= 0.825
Out[55]= 0.740

This is much better than the most naive approach:

In[57]:= ({{#[[1]], #[[2]]}, #[[3]]} & /@ data) // 
  RepeatedTiming // First

Out[57]= 1.859

The solution from @Oppenede is similar in timing to Apply

In[77]:= Transpose[{Drop[data, 0, -1], data[[All, -1]]}] // RepeatedTiming // First

Out[77]= 0.836
$\endgroup$
4
$\begingroup$
list = {{x1, y1, f1}, {x2, y2, f2}};

Transpose[{Drop[list, 0, -1], list[[All, -1]]}]
(* {{{x1, y1}, f1}, {{x2, y2}, f2}} *)
$\endgroup$
4
$\begingroup$

Use a rule and replacement:

{{x1, y1, f1}, {x2, y2, f2}} /. {x_, y_, f_} -> {{x, y}, f}

(* {{{x1,y1},f1},{{x2,y2},f2}} *)
$\endgroup$
3
  • $\begingroup$ Thank you. This works well and I feel it could be useful as an example for other cases. $\endgroup$ Feb 5, 2019 at 6:04
  • $\begingroup$ You’re welcome, Mikel. Using Replace with a rule based on a pattern, like this, is often less efficient than mapping an anonymous function. But I find it so clear and understandable that I often use it when execution time is not an issue. $\endgroup$ Feb 5, 2019 at 6:10
  • $\begingroup$ It is better to use Replace with a level spec as this tests each expression at each level $\endgroup$ Feb 5, 2019 at 15:37
4
$\begingroup$
list = {{x1, y1, f1}, {x2, y2, f2}};

{Most[#], Last[#]} & /@ list

(* {{{x1, y1}, f1}, {{x2, y2}, f2}} *)
$\endgroup$
3
  • $\begingroup$ This is a bit slower than the replace solutions and simliar to {{#[[1]], #[[2]]}, #[[3]]} & /@ data $\endgroup$ Feb 5, 2019 at 20:17
  • $\begingroup$ @GenericAccountName Yes, it is. My personal preference is to avoid using [[]] as I find it quite visually jarring. $\endgroup$ Feb 5, 2019 at 21:25
  • $\begingroup$ I agree, I prefer your syntax as well for this, the main point of my comment is this is a slower solution than others. $\endgroup$ Feb 5, 2019 at 22:08

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.