13

I have a two dimensional list and a one dimensional list. I would like to insert the 1D list into the 2D list as an additional column. For example:

array = {{a,1,2},{b,2,3},{c,3,4}};
column = {x,y,z};

becomes

final = {{a,1,2,x},{b,2,3,y},{c,3,4,z}};

I have done this inelegantly:

Table[Insert[array[[i]], column[[i]], 4], {i, Length[array]}];

My question: what is the proper way to do this in Mathematica? I don't think it needs the loop I'm using. My solution feels ugly.

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Tim
  • 5,024
  • 2
  • 30
  • 58

7 Answers7

14

For example:

 Transpose@Append[Transpose@array, column]

You can also make is a function like so:

 subListAppend = Transpose@Append[Transpose@#1, #2] &;
 subListAppend[array, column]

which makes it easier if you have to use it frequently. And of course if you want to insert at any place other than just the end you can use Insert[].

subListInsert = Transpose@Insert[Transpose@#1, #2, #3] &;
subListInsert[array, column, 2]
--> {{a, x, 1, 2}, {b, y, 2, 3}, {c, z, 3, 4}}

EDIT: Since the obligatory speed optimization discussion has started, here are some results using this and a 10000x200 array:

ArrayFlatten@{{array, List /@ column}}:             0.020 s
Transpose@Append[Transpose@array, column]:          0.067 s
MapThread[Append, {array, column}]:                 0.083 s  
MapThread[Insert[#1, #2, 4] &, {array, column}]:    0.095 s
Map[Flatten, Flatten[{array, column}, {2}]]:        0.26 s
ConstantArray based solution:                       0.29 s
Partition[Flatten@Transpose[{array, column}], 4]:   0.48 s

And the winner is ArrayFlatten!

Community
  • 1
  • 1
Timo
  • 4,246
  • 6
  • 29
  • 42
  • 1
    Alright, that did the trick, thank you! Now I need to pick that apart to understand *why*, but that's for me to do. – Tim Nov 24 '10 at 19:48
  • Go one element at a time (e.g. see what Transpose@array does) and you'll figure it out :-). – Timo Nov 24 '10 at 19:50
  • Yep, that helped. It's like origami. I knew I was fighting Mathematica unnecessarily. Thanks again. – Tim Nov 24 '10 at 19:57
  • 2
    Using `ArrayFlatten` often looks nicer: http://stackoverflow.com/questions/1244782/how-to-prepend-a-column-to-a-matrix/2274679#2274679 – Janus Nov 25 '10 at 02:19
5

Another possibility is

result = ConstantArray[0, Dimensions[array] + {0, 1}];
result[[All, 1 ;; Last[Dimensions[array]]]] = array;
result[[All, -1]] = column;

which seems to be faster on my computer for large numeric matrices, although it requires an additional variable. If you're dealing with real-valued entries you'll want to use

result = ConstantArray[0.0, Dimensions[array] + {0, 1}];

to keep the speed gains.

There's also

MapThread[Append, {array, column}]

which is also fast (and elegant IMO) but will unpack the result. (But if you have symbolic entries as in the example, that's not a concern.)

Brett Champion
  • 8,497
  • 1
  • 27
  • 44
  • Thanks Brett. Your MapThread usage is indeed elegant. I'm glad I saw the Transpose answer first because I have a feeling that the concepts it uses are more widely applicable, however this is very, very concise. My matrices are not big enough to warrant the extra lines of code for the ConstantArray approach and they contain mainly symbolic data anyway. Still, four utterly different approaches to the same problem: that's Mathematica! – Tim Nov 24 '10 at 21:34
  • Interesting, I get roughly 35% better `Timing` using the OP's example and about a factor of ten better speed for large (1k -- 1M entries) matrices using my method. Most different list manipulations at the kernel level are performed by the same (very optimized) code, so trying to force MMA to do things a certain way (which may be correct / optimal in other languages) most times leads to overhead. – Timo Nov 24 '10 at 21:42
  • I was doing my tests with V8 on OS X. – Brett Champion Nov 24 '10 at 22:10
  • You mean the javascript engine? I'm confused. EDIT: oh you mean version 8? Well, I'm using v7.0.1 on OS X. – Timo Nov 25 '10 at 08:30
5

Here is my try using Join

In[11]:= Join[array,List/@column,2]
Out[11]= {{a,1,2,x},{b,2,3,y},{c,3,4,z}}

It might be comparable to the fastest one among previously mentioned programs.

Meng Lu
  • 13,726
  • 12
  • 39
  • 47
  • I am surprised a `Join` method was not already on this page, as it seems like one of the most natural (and efficient) ways to do this. +1 – Mr.Wizard Oct 14 '11 at 06:36
3

How about this?

pos = 4;
MapThread[Insert[#1, #2, pos] &, {array, column}]
Mark
  • 31
  • 1
3

I (sometimes) like to transpose with Flatten, as it works with a 'ragged' array.

Map[Flatten, Flatten[{array, column}, {2}]]

giving

{{a, 1, 2, x}, {b, 2, 3, y}, {c, 3, 4, z}}

But if, say, the column has only 2 elements

column2 = {x, y};
Map[Flatten, Flatten[{array, column2}, {2}]]

giving

{{a, 1, 2, x}, {b, 2, 3, y}, {c, 3, 4}}

(Transpose will not work here)

681234
  • 4,214
  • 2
  • 35
  • 42
2

Still:

k= Partition[Flatten@Transpose[{#, {x, y, z}}], 4]&

k@ {{a, 1, 2}, {b, 2, 3}, {c, 3, 4}}

(*
-> {{a, 1, 2, x}, {b, 2, 3, y}, {c, 3, 4, z}}
*)
Dr. belisarius
  • 60,527
  • 15
  • 115
  • 190
  • Make that five approaches. This one's like a sausage: you can eat it, but you don't want to see how it's made :) – Tim Nov 24 '10 at 21:40
1

Though not as practical or efficient as some of the extant methods, here are two more to add to the list:

ArrayPad[array, {0,{0,1}}, List /@ column]

PadRight[array, Dimensions[array] + {0, 1}, List /@ column]
Mr.Wizard
  • 24,179
  • 5
  • 44
  • 125