Here's a new version of 'reparray'.
I tested the previous version against 'repmat' for the 2d case and
found it to be as much as 3x slower in some instances. Ick. So I
redid it using the approach repmat uses -- reshape() and repeat()
rather than concatenate(). Now it's very nearly as fast as repmat for
the 2-d case.
def reparray(A, tup):
if numpy.isscalar(tup):
tup = (tup,)
d = len(tup)
c = numpy.array(A,copy=False,subok=True,ndmin=d)
shape = list(c.shape)
n = c.size
for i, nrep in enumerate(tup):
if nrep!=1:
c = c.reshape(-1,n).repeat(nrep,0)
dim_in = shape[i]
dim_out = dim_in*nrep
shape[i] = dim_out
n /= dim_in
return c.reshape(shape)
The full file with tests and timing code is attached.
One thing I noticed while doing this was that repmat doesn't preserve
subclasses. Which is funny considering 'mat' is part of the name and
it only works on 2-d arrays. Using asanyarray() instead of asarray()
in the first line should fix that.
--Bill
import numpy
def reparray(A, tup):
Repeat an array the number of times given in the integer tuple, tup.
Similar to repmat, but works for arrays of any dimension.
reparray(A,(m,n)) is equivalent to repmat(A,m,n)
If tup has length d, the result will have dimension of max(d, A.ndim).
If tup is scalar it is treated as a 1-tuple.
If A.ndim d, A is promoted to be d-dimensional by prepending new axes.
So a shape (3,) array is promoted to (1,3) for 2-D replication,
or shape (1,1,3) for 3-D replication.
If this is not the desired behavior, promote A to d-dimensions manually
before calling this function.
If d A.ndim, tup is promoted to A.ndim by appending 1's to it. Thus
for an A.shape of (2,3,4,5), a tup of (2,2) is treated as (2,2,1,1)
Examples:
a = array([0,1,2])
reparray(a,2)
array([0, 1, 2, 0, 1, 2])
reparray(a,(1,2))
array([[0, 1, 2, 0, 1, 2]])
reparray(a,(2,2))
array([[0, 1, 2, 0, 1, 2],
[0, 1, 2, 0, 1, 2]])
reparray(a,(2,1,2))
array([[[0, 1, 2, 0, 1, 2]],
[[0, 1, 2, 0, 1, 2]]])
See Also:
repmat, repeat
if numpy.isscalar(tup):
tup = (tup,)
d = len(tup)
c = numpy.array(A,copy=False,subok=True,ndmin=d)
shape = list(c.shape)
n = c.size
for i, nrep in enumerate(tup):
if nrep!=1:
c = c.reshape(-1,n).repeat(nrep,0)
dim_in = shape[i]
dim_out = dim_in*nrep
shape[i] = dim_out
n /= dim_in
return c.reshape(shape)
def _reparray_alt(A, tup):
Alternate implementation of reparray, that turns out to be slower.
if numpy.isscalar(tup):
tup = (tup,)
d = len(tup)
A = numpy.array(A,copy=False,subok=True,ndmin=d)
for i,k in enumerate(tup):
A = numpy.concatenate([A]*k, axis=i)
return A
def _test():
a = numpy.array([0,1,2])
b = numpy.array([[0,1,2],[3,4,5]])
c = numpy.array([[[0,1,2],[3,4,5]]])
d = numpy.array([[[0,1,2]],[[3,4,5]]])
shapes = [(1,1),(2,1),(1,2),(2,2),(2,3),(3,2)]
# double check results against repmat
for s in shapes:
r = reparray(b,s)
r0 = numpy.repmat(b,s[0],s[1])
assert( numpy.all(r == r0) )
shapes += [(1,),(2,),(1,1,1),(2,1,1),(1,2,1),(1,1,2),(1,2,2),(2,1,2),(2,2,1),(2,2,2)]
# cross check results between implementations
for arr in (a,b,c,d):
for s in shapes:
r = reparray(arr,s)
r0 = _reparray_alt(arr,s)
assert( numpy.all(r == r0) )
def speed_compare():
from timeit import Timer
print '10x10 replication of 2x2'
N = 1
t0 = Timer(c = repmat(a,10,10),import numpy; repmat=numpy.repmat; a=numpy.ones((2,2)))
print 'repmat: \t', t0.timeit(number=N)
t1 = Timer(c = _reparray_alt(a,(10,10)),import numpy; from __main__ import _reparray_alt; a=numpy.ones((2,2)))
print 'reparray v0:\t', t1.timeit(number=N)
t2 = Timer(c = reparray(a,(10,10)),import numpy; from __main__ import reparray; a=numpy.ones((2,2)))
print 'reparray v1:\t', t2.timeit(number=N)
print '2x2 replication of 100x100'
N = 1000
t0 = Timer(c = repmat(a,2,2),import numpy; repmat=numpy.repmat; a=numpy.ones((100,100)))
print 'repmat: \t', t0.timeit(number=N)
t1 = Timer(c = _reparray_alt(a,(2,2)),import numpy; from __main__ import _reparray_alt; a=numpy.ones((100,100)))
print 'reparray v0:\t', t1.timeit(number=N)
t2 = Timer(c = reparray(a,(2,2)),import numpy; from __main__ import reparray; a=numpy.ones((100,100)))
print 'reparray v1:\t', t2.timeit(number=N)
print '10x10 replication of 50x50'
N = 100
t0 = Timer(c = repmat(a,10,10),import numpy; repmat=numpy.repmat; a=numpy.ones((50,50)))
print 'repmat: \t', t0.timeit(number=N)
t1 = Timer(c = _reparray_alt(a,(10,10)),import numpy; from __main__ import _reparray_alt; a=numpy.ones((50,50)))