21

I am looking for a function that behaves similarly to the rep function in R for Matlab. For example with rep I can do the following:

> rep(c(1,2,3),times=3)
[1] 1 2 3 1 2 3 1 2 3

> rep(c(1,2,3),each=3)
[1] 1 1 1 2 2 2 3 3 3
> 

In matlab there is the repmat function which accomplishes the first part

>> repmat([1,2,3],1,3)

ans =

     1     2     3     1     2     3     1     2     3

but not the second part (or at least I can't figure out how to do it).

Any suggestions?

Cleb
  • 25,102
  • 20
  • 116
  • 151
  • Can't believe I didn't know about the `each` option for `rep`; been using a hacky one-liner all this time... – bnaul Jan 31 '13 at 06:58
  • Note that since Matlab 2015a there is now a built-in function that does exactly this: [`repelem`](http://www.mathworks.com/help/matlab/ref/repelem.html) – Dan May 13 '15 at 13:51

4 Answers4

12

You can reproduce the syntax of the rep function in R fairly closely by first defining a function as follows:

function [result]=rep(array, count)
matrix = repmat(array, count,1);
result = matrix(:);

Then you can reproduce the desired behavior by calling with either a row or column vector:

>> rep([1 2 3],3)
ans =
 1     1     1     2     2     2     3     3     3

>> rep([1 2 3]',3)
ans =
 1     2     3     1     2     3     1     2     3

Note I have used the transpose (') operator in the second call to pass the input array as a column vector (a 3x1 matrix).

I benchmarked this on my laptop, and for a base array with 100,000 elements repeated 100 times, it was between 2 to 8 times faster than using the ceil option above, depending on whether you want the first or the second arrangement.

bcumming
  • 1,075
  • 2
  • 8
  • 17
  • Fast and simple! I'm surprised repmat is the winner, but it definitely seems to be the best solution in this case! – Dan Feb 01 '13 at 07:46
  • If implemented efficiently, repmat should be very fast, because it performs the repetition by copying memory from one location to another. Modern processors with lots of cache are quite good at this sort of thing. That is also the reason why there is such a factor of 4 speed difference between the two usage types: the fast one has stride one memory accesses, whereas the other has stride length(array) memory accesses, which are slower. – bcumming Feb 01 '13 at 10:21
8

Good question +1. A neat one-liner method to accomplish this is via the Kronecker tensor product, eg:

A = [1 2 3];
N = 3;
B = kron(A, ones(1, N));

Then:

B =

     1     1     1     2     2     2     3     3     3

UPDATE: @Dan has provided a very neat solution that looks to be more efficient than my kron method, so check that answer out before leaving the page :-)

UPDATE: @bcumming has also provided a nice solution that should scale very nicely when the input vector is large.

Colin T Bowers
  • 18,106
  • 8
  • 61
  • 89
  • I tried to come up with a solution I could understand as I don't know what a Kronecker tensor product is. You might be interested in the result, I think it is faster than kron() although I haven't really done proper benchmarking... – Dan Jan 31 '13 at 06:54
6

If like me you have no idea what a Kronecker tensor product is you might be interested in this more intuitive (and actually I think faster) solution:

c(ceil((1:length(c)*n)/n));

so here I used vector indexing to replicate the matrix. For example using the two case you have above we could do:

c = 1:3;
c([1 1 1 2 2 2 3 3 3]) %for each
c([1 2 3 1 2 3 1 2 3]) %for times

so the questions is how do we make a vector [1 2 3 1 2 3 1 2 3] without the very functionality you are requesting. So I made a vector with the number of elements we need i.e. 1:9 and then divide by three and round up (i.e. try ceil((1:9)/3) in the command line.

A bit of benchmarking (I know this stuff should be in loops so maybe this isn't so accurate):

c = 1:3; n = 3;
tic; k = kron(c, ones(1, n)); toc; % 0.000208 seconds.
tic; a = c(ceil((1:length(c)*n)/n)); toc;  % 0.000025 seconds.
clear;
c = 1:1000000; n = 3;
tic; k = kron(c, ones(1, n)); toc; % 0.143747 seconds.
tic; a = c(ceil((1:length(c)*n)/n)); toc;  % 0.090956 seconds.
clear;
c = 1:10000; n = 1000;
tic; k = kron(c, ones(1, n)); toc; % 0.583336 seconds.
tic; a = c(ceil((1:length(c)*n)/n)); toc;  % 0.237878 seconds.
Dan
  • 45,079
  • 17
  • 88
  • 157
  • 1
    Very neat +1! I verified your speed tests on my machine and added some loops, and the results you are getting are fairly representative. Typically, I was finding your method to be about 4 times faster than `kron`. More intuitive though? I think that's in the eye of the beholder :-) Kronecker products are conceptually very simple, once one has gone through the initial cost of working out how they work... – Colin T Bowers Jan 31 '13 at 07:18
  • Yeah fair enough - I guess it's just the name, it has the word tensor in it therefore it scares me :) – Dan Jan 31 '13 at 07:20
  • Well it definitely works in my smaller example but I want something that actually works for "irregular" vector. For example, a vector like c = [1.34,9.2,-8.2,99] –  Feb 01 '13 at 04:07
  • 1
    It works for any vector, even text. It definitely works on the vector in your comment. It will even work on this: n=3; c = 'wowee!'; c(ceil((1:length(c)*n)/n)) – Dan Feb 01 '13 at 06:13
1

Here's one idea:

a=[1,2,3];
reshape(repmat(a,1,length(a)),1,length(a)^2)

ans =

 1     2     3     1     2     3     1     2     3

reshape(repmat(a,length(a),1),1,length(a)^2)

ans =

 1     1     1     2     2     2     3     3     3

I can't yet find a simpler function that does this in one step, interested if there is one though.

RussH
  • 329
  • 2
  • 17
  • 1
    There is a simpler one-step function: `kron` (the Kronecker tensor product). I've added an answer with an example. Cheers. – Colin T Bowers Jan 30 '13 at 23:05