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$
  • 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 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$ – Shredderroy Feb 4 at 23:53
  • $\begingroup$ @JasonB. this should be Apply[{{#1, #2}, #3} &, {{x1, y1, f1}, {x2, y2, f2}}, {1}] $\endgroup$ – GenericAccountName Feb 5 at 0:47
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$
  • $\begingroup$ Thank you. This works well and I feel it could be useful as an example for other cases. $\endgroup$ – Mikel García Feb 5 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$ – David Keith Feb 5 at 6:10
  • $\begingroup$ It is better to use Replace with a level spec as this tests each expression at each level $\endgroup$ – GenericAccountName Feb 5 at 15:37
4
$\begingroup$
list = {{x1, y1, f1}, {x2, y2, f2}};

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

(* {{{x1, y1}, f1}, {{x2, y2}, f2}} *)
$\endgroup$
  • $\begingroup$ This is a bit slower than the replace solutions and simliar to {{#[[1]], #[[2]]}, #[[3]]} & /@ data $\endgroup$ – GenericAccountName Feb 5 at 20:17
  • $\begingroup$ @GenericAccountName Yes, it is. My personal preference is to avoid using [[]] as I find it quite visually jarring. $\endgroup$ – Rohit Namjoshi Feb 5 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$ – GenericAccountName Feb 5 at 22:08

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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