summaryrefslogtreecommitdiff
path: root/numpy/lib/shape_base.py
diff options
context:
space:
mode:
Diffstat (limited to 'numpy/lib/shape_base.py')
-rw-r--r--numpy/lib/shape_base.py539
1 files changed, 539 insertions, 0 deletions
diff --git a/numpy/lib/shape_base.py b/numpy/lib/shape_base.py
new file mode 100644
index 000000000..8d66b41d1
--- /dev/null
+++ b/numpy/lib/shape_base.py
@@ -0,0 +1,539 @@
+__all__ = ['atleast_1d','atleast_2d','atleast_3d','vstack','hstack',
+ 'column_stack','dstack','array_split','split','hsplit',
+ 'vsplit','dsplit','apply_over_axes','expand_dims',
+ 'apply_along_axis']
+
+import numeric as _nx
+from numeric import *
+from oldnumeric import product
+
+from type_check import isscalar
+
+def apply_along_axis(func1d,axis,arr,*args):
+ """ Execute func1d(arr[i],*args) where func1d takes 1-D arrays
+ and arr is an N-d array. i varies so as to apply the function
+ along the given axis for each 1-d subarray in arr.
+ """
+ arr = asarray(arr)
+ nd = arr.ndim
+ if axis < 0:
+ axis += nd
+ if (axis >= nd):
+ raise ValueError("axis must be less than arr.ndim; axis=%d, rank=%d."
+ % (axis,nd))
+ ind = [0]*(nd-1)
+ i = zeros(nd,'O')
+ indlist = range(nd)
+ indlist.remove(axis)
+ i[axis] = slice(None,None)
+ outshape = asarray(arr.shape).take(indlist)
+ i.put(ind, indlist)
+ res = func1d(arr[tuple(i.tolist())],*args)
+ # if res is a number, then we have a smaller output array
+ if isscalar(res):
+ outarr = zeros(outshape,asarray(res).dtypechar)
+ outarr[ind] = res
+ Ntot = product(outshape)
+ k = 1
+ while k < Ntot:
+ # increment the index
+ ind[-1] += 1
+ n = -1
+ while (ind[n] >= outshape[n]) and (n > (1-nd)):
+ ind[n-1] += 1
+ ind[n] = 0
+ n -= 1
+ i.put(ind,indlist)
+ res = func1d(arr[tuple(i.tolist())],*args)
+ outarr[ind] = res
+ k += 1
+ return outarr
+ else:
+ Ntot = product(outshape)
+ holdshape = outshape
+ outshape = list(arr.shape)
+ outshape[axis] = len(res)
+ outarr = zeros(outshape,asarray(res).dtypechar)
+ outarr[tuple(i.tolist())] = res
+ k = 1
+ while k < Ntot:
+ # increment the index
+ ind[-1] += 1
+ n = -1
+ while (ind[n] >= holdshape[n]) and (n > (1-nd)):
+ ind[n-1] += 1
+ ind[n] = 0
+ n -= 1
+ i.put(ind, indlist)
+ res = func1d(arr[tuple(i.tolist())],*args)
+ outarr[tuple(i.tolist())] = res
+ k += 1
+ return outarr
+
+
+def apply_over_axes(func, a, axes):
+ """Apply a function repeatedly over multiple axes, keeping the same shape
+ for the resulting array.
+
+ func is called as res = func(a, axis). The result is assumed
+ to be either the same shape as a or have one less dimension.
+ This call is repeated for each axis in the axes sequence.
+ """
+ val = asarray(a)
+ N = a.ndim
+ if array(axes).ndim == 0:
+ axes = (axes,)
+ for axis in axes:
+ if axis < 0: axis = N + axis
+ args = (val, axis)
+ res = func(*args)
+ if res.ndim == val.ndim:
+ val = res
+ else:
+ res = expand_dims(res,axis)
+ if res.ndim == val.ndim:
+ val = res
+ else:
+ raise ValueError, "function is not returning"\
+ " an array of correct shape"
+ return val
+
+def expand_dims(a, axis):
+ """Expand the shape of a by including newaxis before given axis.
+ """
+ a = asarray(a)
+ shape = a.shape
+ if axis < 0:
+ axis = axis + len(shape) + 1
+ return a.reshape(shape[:axis] + (1,) + shape[axis:])
+
+
+def atleast_1d(*arys):
+ """ Force a sequence of arrays to each be at least 1D.
+
+ Description:
+ Force an array to be at least 1D. If an array is 0D, the
+ array is converted to a single row of values. Otherwise,
+ the array is unaltered.
+ Arguments:
+ *arys -- arrays to be converted to 1 or more dimensional array.
+ Returns:
+ input array converted to at least 1D array.
+ """
+ res = []
+ for ary in arys:
+ ary = asarray(ary)
+ if len(ary.shape) == 0:
+ ary = ary.reshape(1)
+ res.append(ary)
+ if len(res) == 1:
+ return res[0]
+ else:
+ return res
+
+def atleast_2d(*arys):
+ """ Force a sequence of arrays to each be at least 2D.
+
+ Description:
+ Force an array to each be at least 2D. If the array
+ is 0D or 1D, the array is converted to a single
+ row of values. Otherwise, the array is unaltered.
+ Arguments:
+ arys -- arrays to be converted to 2 or more dimensional array.
+ Returns:
+ input array converted to at least 2D array.
+ """
+ res = []
+ for ary in arys:
+ ary = asarray(ary)
+ if len(ary.shape) == 0:
+ result = ary.reshape(1,1)
+ elif len(ary.shape) == 1:
+ result = ary[newaxis,:]
+ else:
+ result = ary
+ res.append(result)
+ if len(res) == 1:
+ return res[0]
+ else:
+ return res
+
+def atleast_3d(*arys):
+ """ Force a sequence of arrays to each be at least 3D.
+
+ Description:
+ Force an array each be at least 3D. If the array is 0D or 1D,
+ the array is converted to a single 1xNx1 array of values where
+ N is the orginal length of the array. If the array is 2D, the
+ array is converted to a single MxNx1 array of values where MxN
+ is the orginal shape of the array. Otherwise, the array is
+ unaltered.
+ Arguments:
+ arys -- arrays to be converted to 3 or more dimensional array.
+ Returns:
+ input array converted to at least 3D array.
+ """
+ res = []
+ for ary in arys:
+ ary = asarray(ary)
+ if len(ary.shape) == 0:
+ result = ary.reshape(1,1,1)
+ elif len(ary.shape) == 1:
+ result = ary[newaxis,:,newaxis]
+ elif len(ary.shape) == 2:
+ result = ary[:,:,newaxis]
+ else:
+ result = ary
+ res.append(result)
+ if len(res) == 1:
+ return res[0]
+ else:
+ return res
+
+
+def vstack(tup):
+ """ Stack arrays in sequence vertically (row wise)
+
+ Description:
+ Take a sequence of arrays and stack them veritcally
+ to make a single array. All arrays in the sequence
+ must have the same shape along all but the first axis.
+ vstack will rebuild arrays divided by vsplit.
+ Arguments:
+ tup -- sequence of arrays. All arrays must have the same
+ shape.
+ Examples:
+ >>> import scipy
+ >>> a = array((1,2,3))
+ >>> b = array((2,3,4))
+ >>> scipy.vstack((a,b))
+ array([[1, 2, 3],
+ [2, 3, 4]])
+ >>> a = array([[1],[2],[3]])
+ >>> b = array([[2],[3],[4]])
+ >>> scipy.vstack((a,b))
+ array([[1],
+ [2],
+ [3],
+ [2],
+ [3],
+ [4]])
+
+ """
+ return _nx.concatenate(map(atleast_2d,tup),0)
+
+def hstack(tup):
+ """ Stack arrays in sequence horizontally (column wise)
+
+ Description:
+ Take a sequence of arrays and stack them horizontally
+ to make a single array. All arrays in the sequence
+ must have the same shape along all but the second axis.
+ hstack will rebuild arrays divided by hsplit.
+ Arguments:
+ tup -- sequence of arrays. All arrays must have the same
+ shape.
+ Examples:
+ >>> import scipy
+ >>> a = array((1,2,3))
+ >>> b = array((2,3,4))
+ >>> scipy.hstack((a,b))
+ array([1, 2, 3, 2, 3, 4])
+ >>> a = array([[1],[2],[3]])
+ >>> b = array([[2],[3],[4]])
+ >>> scipy.hstack((a,b))
+ array([[1, 2],
+ [2, 3],
+ [3, 4]])
+
+ """
+ return _nx.concatenate(map(atleast_1d,tup),1)
+
+def column_stack(tup):
+ """ Stack 1D arrays as columns into a 2D array
+
+ Description:
+ Take a sequence of 1D arrays and stack them as columns
+ to make a single 2D array. All arrays in the sequence
+ must have the same length.
+ Arguments:
+ tup -- sequence of 1D arrays. All arrays must have the same
+ length.
+ Examples:
+ >>> import scipy
+ >>> a = array((1,2,3))
+ >>> b = array((2,3,4))
+ >>> scipy.column_stack((a,b))
+ array([[1, 2],
+ [2, 3],
+ [3, 4]])
+
+ """
+ arrays = map(_nx.transpose,map(atleast_2d,tup))
+ return _nx.concatenate(arrays,1)
+
+def dstack(tup):
+ """ Stack arrays in sequence depth wise (along third dimension)
+
+ Description:
+ Take a sequence of arrays and stack them along the third axis.
+ All arrays in the sequence must have the same shape along all
+ but the third axis. This is a simple way to stack 2D arrays
+ (images) into a single 3D array for processing.
+ dstack will rebuild arrays divided by dsplit.
+ Arguments:
+ tup -- sequence of arrays. All arrays must have the same
+ shape.
+ Examples:
+ >>> import scipy
+ >>> a = array((1,2,3))
+ >>> b = array((2,3,4))
+ >>> scipy.dstack((a,b))
+ array([ [[1, 2],
+ [2, 3],
+ [3, 4]]])
+ >>> a = array([[1],[2],[3]])
+ >>> b = array([[2],[3],[4]])
+ >>> scipy.dstack((a,b))
+ array([[ [1, 2]],
+ [ [2, 3]],
+ [ [3, 4]]])
+ """
+ return _nx.concatenate(map(atleast_3d,tup),2)
+
+def _replace_zero_by_x_arrays(sub_arys):
+ for i in range(len(sub_arys)):
+ if len(_nx.shape(sub_arys[i])) == 0:
+ sub_arys[i] = _nx.array([])
+ elif _nx.sometrue(_nx.equal(_nx.shape(sub_arys[i]),0)):
+ sub_arys[i] = _nx.array([])
+ return sub_arys
+
+def array_split(ary,indices_or_sections,axis = 0):
+ """ Divide an array into a list of sub-arrays.
+
+ Description:
+ Divide ary into a list of sub-arrays along the
+ specified axis. If indices_or_sections is an integer,
+ ary is divided into that many equally sized arrays.
+ If it is impossible to make an equal split, each of the
+ leading arrays in the list have one additional member. If
+ indices_or_sections is a list of sorted integers, its
+ entries define the indexes where ary is split.
+
+ Arguments:
+ ary -- N-D array.
+ Array to be divided into sub-arrays.
+ indices_or_sections -- integer or 1D array.
+ If integer, defines the number of (close to) equal sized
+ sub-arrays. If it is a 1D array of sorted indices, it
+ defines the indexes at which ary is divided. Any empty
+ list results in a single sub-array equal to the original
+ array.
+ axis -- integer. default=0.
+ Specifies the axis along which to split ary.
+ Caveats:
+ Currently, the default for axis is 0. This
+ means a 2D array is divided into multiple groups
+ of rows. This seems like the appropriate default, but
+ we've agreed most other functions should default to
+ axis=-1. Perhaps we should use axis=-1 for consistency.
+ However, we could also make the argument that SciPy
+ works on "rows" by default. sum() sums up rows of
+ values. split() will split data into rows. Opinions?
+ """
+ try:
+ Ntotal = ary.shape[axis]
+ except AttributeError:
+ Ntotal = len(ary)
+ try: # handle scalar case.
+ Nsections = len(indices_or_sections) + 1
+ div_points = [0] + list(indices_or_sections) + [Ntotal]
+ except TypeError: #indices_or_sections is a scalar, not an array.
+ Nsections = int(indices_or_sections)
+ if Nsections <= 0:
+ raise ValueError, 'number sections must be larger than 0.'
+ Neach_section,extras = divmod(Ntotal,Nsections)
+ section_sizes = [0] + \
+ extras * [Neach_section+1] + \
+ (Nsections-extras) * [Neach_section]
+ div_points = _nx.array(section_sizes).cumsum()
+
+ sub_arys = []
+ sary = _nx.swapaxes(ary,axis,0)
+ for i in range(Nsections):
+ st = div_points[i]; end = div_points[i+1]
+ sub_arys.append(_nx.swapaxes(sary[st:end],axis,0))
+
+ # there is a wierd issue with array slicing that allows
+ # 0x10 arrays and other such things. The following cluge is needed
+ # to get around this issue.
+ sub_arys = _replace_zero_by_x_arrays(sub_arys)
+ # end cluge.
+
+ return sub_arys
+
+def split(ary,indices_or_sections,axis=0):
+ """ Divide an array into a list of sub-arrays.
+
+ Description:
+ Divide ary into a list of sub-arrays along the
+ specified axis. If indices_or_sections is an integer,
+ ary is divided into that many equally sized arrays.
+ If it is impossible to make an equal split, an error is
+ raised. This is the only way this function differs from
+ the array_split() function. If indices_or_sections is a
+ list of sorted integers, its entries define the indexes
+ where ary is split.
+
+ Arguments:
+ ary -- N-D array.
+ Array to be divided into sub-arrays.
+ indices_or_sections -- integer or 1D array.
+ If integer, defines the number of (close to) equal sized
+ sub-arrays. If it is a 1D array of sorted indices, it
+ defines the indexes at which ary is divided. Any empty
+ list results in a single sub-array equal to the original
+ array.
+ axis -- integer. default=0.
+ Specifies the axis along which to split ary.
+ Caveats:
+ Currently, the default for axis is 0. This
+ means a 2D array is divided into multiple groups
+ of rows. This seems like the appropriate default, but
+ we've agreed most other functions should default to
+ axis=-1. Perhaps we should use axis=-1 for consistency.
+ However, we could also make the argument that SciPy
+ works on "rows" by default. sum() sums up rows of
+ values. split() will split data into rows. Opinions?
+ """
+ try: len(indices_or_sections)
+ except TypeError:
+ sections = indices_or_sections
+ N = ary.shape[axis]
+ if N % sections:
+ raise ValueError, 'array split does not result in an equal division'
+ res = array_split(ary,indices_or_sections,axis)
+ return res
+
+def hsplit(ary,indices_or_sections):
+ """ Split ary into multiple columns of sub-arrays
+
+ Description:
+ Split a single array into multiple sub arrays. The array is
+ divided into groups of columns. If indices_or_sections is
+ an integer, ary is divided into that many equally sized sub arrays.
+ If it is impossible to make the sub-arrays equally sized, the
+ operation throws a ValueError exception. See array_split and
+ split for other options on indices_or_sections.
+ Arguments:
+ ary -- N-D array.
+ Array to be divided into sub-arrays.
+ indices_or_sections -- integer or 1D array.
+ If integer, defines the number of (close to) equal sized
+ sub-arrays. If it is a 1D array of sorted indices, it
+ defines the indexes at which ary is divided. Any empty
+ list results in a single sub-array equal to the original
+ array.
+ Returns:
+ sequence of sub-arrays. The returned arrays have the same
+ number of dimensions as the input array.
+ Related:
+ hstack, split, array_split, vsplit, dsplit.
+ Examples:
+ >>> import scipy
+ >>> a= array((1,2,3,4))
+ >>> scipy.hsplit(a,2)
+ [array([1, 2]), array([3, 4])]
+ >>> a = array([[1,2,3,4],[1,2,3,4]])
+ [array([[1, 2],
+ [1, 2]]), array([[3, 4],
+ [3, 4]])]
+
+ """
+ if len(_nx.shape(ary)) == 0:
+ raise ValueError, 'hsplit only works on arrays of 1 or more dimensions'
+ if len(ary.shape) > 1:
+ return split(ary,indices_or_sections,1)
+ else:
+ return split(ary,indices_or_sections,0)
+
+def vsplit(ary,indices_or_sections):
+ """ Split ary into multiple rows of sub-arrays
+
+ Description:
+ Split a single array into multiple sub arrays. The array is
+ divided into groups of rows. If indices_or_sections is
+ an integer, ary is divided into that many equally sized sub arrays.
+ If it is impossible to make the sub-arrays equally sized, the
+ operation throws a ValueError exception. See array_split and
+ split for other options on indices_or_sections.
+ Arguments:
+ ary -- N-D array.
+ Array to be divided into sub-arrays.
+ indices_or_sections -- integer or 1D array.
+ If integer, defines the number of (close to) equal sized
+ sub-arrays. If it is a 1D array of sorted indices, it
+ defines the indexes at which ary is divided. Any empty
+ list results in a single sub-array equal to the original
+ array.
+ Returns:
+ sequence of sub-arrays. The returned arrays have the same
+ number of dimensions as the input array.
+ Caveats:
+ How should we handle 1D arrays here? I am currently raising
+ an error when I encounter them. Any better approach?
+
+ Should we reduce the returned array to their minium dimensions
+ by getting rid of any dimensions that are 1?
+ Related:
+ vstack, split, array_split, hsplit, dsplit.
+ Examples:
+ import scipy
+ >>> a = array([[1,2,3,4],
+ ... [1,2,3,4]])
+ >>> scipy.vsplit(a)
+ [array([ [1, 2, 3, 4]]), array([ [1, 2, 3, 4]])]
+
+ """
+ if len(_nx.shape(ary)) < 2:
+ raise ValueError, 'vsplit only works on arrays of 2 or more dimensions'
+ return split(ary,indices_or_sections,0)
+
+def dsplit(ary,indices_or_sections):
+ """ Split ary into multiple sub-arrays along the 3rd axis (depth)
+
+ Description:
+ Split a single array into multiple sub arrays. The array is
+ divided into groups along the 3rd axis. If indices_or_sections is
+ an integer, ary is divided into that many equally sized sub arrays.
+ If it is impossible to make the sub-arrays equally sized, the
+ operation throws a ValueError exception. See array_split and
+ split for other options on indices_or_sections.
+ Arguments:
+ ary -- N-D array.
+ Array to be divided into sub-arrays.
+ indices_or_sections -- integer or 1D array.
+ If integer, defines the number of (close to) equal sized
+ sub-arrays. If it is a 1D array of sorted indices, it
+ defines the indexes at which ary is divided. Any empty
+ list results in a single sub-array equal to the original
+ array.
+ Returns:
+ sequence of sub-arrays. The returned arrays have the same
+ number of dimensions as the input array.
+ Caveats:
+ See vsplit caveats.
+ Related:
+ dstack, split, array_split, hsplit, vsplit.
+ Examples:
+ >>> a = array([[[1,2,3,4],[1,2,3,4]]])
+ [array([ [[1, 2],
+ [1, 2]]]), array([ [[3, 4],
+ [3, 4]]])]
+
+ """
+ if len(_nx.shape(ary)) < 3:
+ raise ValueError, 'vsplit only works on arrays of 3 or more dimensions'
+ return split(ary,indices_or_sections,2)
+