This is an archival dump of old wiki content --- see scipy.org for current material.

# accum, a function like MATLAB's accumarray

NumPy doesn't include a function that is equivalent to MATLAB's accumarray function. The following function, accum, is close.

Note that accum can handle n-dimensional arrays, and allows the data type of the result to be specified.

```   1 from itertools import product
2 import numpy as np
3
4
5 def accum(accmap, a, func=None, size=None, fill_value=0, dtype=None):
6     """
7     An accumulation function similar to Matlab's `accumarray` function.
8
9     Parameters
10     ----------
11     accmap : ndarray
12         This is the "accumulation map".  It maps input (i.e. indices into
13         `a`) to their destination in the output array.  The first `a.ndim`
14         dimensions of `accmap` must be the same as `a.shape`.  That is,
15         `accmap.shape[:a.ndim]` must equal `a.shape`.  For example, if `a`
16         has shape (15,4), then `accmap.shape[:2]` must equal (15,4).  In this
17         case `accmap[i,j]` gives the index into the output array where
18         element (i,j) of `a` is to be accumulated.  If the output is, say,
19         a 2D, then `accmap` must have shape (15,4,2).  The value in the
20         last dimension give indices into the output array. If the output is
21         1D, then the shape of `accmap` can be either (15,4) or (15,4,1)
22     a : ndarray
23         The input data to be accumulated.
24     func : callable or None
25         The accumulation function.  The function will be passed a list
26         of values from `a` to be accumulated.
27         If None, numpy.sum is assumed.
28     size : ndarray or None
29         The size of the output array.  If None, the size will be determined
30         from `accmap`.
31     fill_value : scalar
32         The default value for elements of the output array.
33     dtype : numpy data type, or None
34         The data type of the output array.  If None, the data type of
35         `a` is used.
36
37     Returns
38     -------
39     out : ndarray
40         The accumulated results.
41
42         The shape of `out` is `size` if `size` is given.  Otherwise the
43         shape is determined by the (lexicographically) largest indices of
44         the output found in `accmap`.
45
46
47     Examples
48     --------
49     >>> from numpy import array, prod
50     >>> a = array([[1,2,3],[4,-1,6],[-1,8,9]])
51     >>> a
52     array([[ 1,  2,  3],
53            [ 4, -1,  6],
54            [-1,  8,  9]])
55     >>> # Sum the diagonals.
56     >>> accmap = array([[0,1,2],[2,0,1],[1,2,0]])
57     >>> s = accum(accmap, a)
58     array([9, 7, 15])
59     >>> # A 2D output, from sub-arrays with shapes and positions like this:
60     >>> # [ (2,2) (2,1)]
61     >>> # [ (1,2) (1,1)]
62     >>> accmap = array([
63             [[0,0],[0,0],[0,1]],
64             [[0,0],[0,0],[0,1]],
65             [[1,0],[1,0],[1,1]],
66         ])
67     >>> # Accumulate using a product.
68     >>> accum(accmap, a, func=prod, dtype=float)
69     array([[ -8.,  18.],
70            [ -8.,   9.]])
71     >>> # Same accmap, but create an array of lists of values.
72     >>> accum(accmap, a, func=lambda x: x, dtype='O')
73     array([[[1, 2, 4, -1], [3, 6]],
74            [[-1, 8], [9]]], dtype=object)
75     """
76
77     # Check for bad arguments and handle the defaults.
78     if accmap.shape[:a.ndim] != a.shape:
79         raise ValueError("The initial dimensions of accmap must be the same as a.shape")
80     if func is None:
81         func = np.sum
82     if dtype is None:
83         dtype = a.dtype
84     if accmap.shape == a.shape:
85         accmap = np.expand_dims(accmap, -1)
87     if size is None:
88         size = 1 + np.squeeze(np.apply_over_axes(np.max, accmap, axes=adims))
89     size = np.atleast_1d(size)
90
91     # Create an array of python lists of values.
92     vals = np.empty(size, dtype='O')
93     for s in product(*[range(k) for k in size]):
94         vals[s] = []
95     for s in product(*[range(k) for k in a.shape]):
96         indx = tuple(accmap[s])
97         val = a[s]
98         vals[indx].append(val)
99
100     # Create the output array.
101     out = np.empty(size, dtype=dtype)
102     for s in product(*[range(k) for k in size]):
103         if vals[s] == []:
104             out[s] = fill_value
105         else:
106             out[s] = func(vals[s])
107
108     return out
```

# Examples

A basic example--sum the diagonals (with wrapping) of a 3 by 3 array:

```In [5]: from numpy import array, prod

In [6]: from accum import accum

In [7]: a = array([[1,2,3],[4,-1,6],[-1,8,9]])

In [8]: a
Out[8]:
array([[ 1,  2,  3],
[ 4, -1,  6],
[-1,  8,  9]])

In [9]: accmap = array([[0,1,2],[2,0,1],[1,2,0]])

In [10]: s = accum(accmap, a)

In [11]: s
Out[11]: array([ 9,  7, 15])```

Accumulate using multiplication, going from a 3 by 3 array to 2 by 2 array:

```In [12]: accmap = array([
....:             [[0,0],[0,0],[0,1]],
....:             [[0,0],[0,0],[0,1]],
....:             [[1,0],[1,0],[1,1]],
....:         ])

In [13]: accum(accmap, a, func=prod, dtype=float)
Out[13]:
array([[ -8.,  18.],
[ -8.,   9.]])```

Create an array of lists containing the values to be accumulated in each position in the output array:

```In [14]: accum(accmap, a, func=lambda x: x, dtype='O')
Out[14]:
array([[[1, 2, 4, -1], [3, 6]],
[[-1, 8], [9]]], dtype=object)```

Use accum to arrange some values from a 1D array in a 2D array (note that using accum for this is overkill; fancy indexing would suffice):

```In [15]: subs = np.array([[k,5-k] for k in range(6)])

In [16]: subs
Out[16]:
array([[0, 5],
[1, 4],
[2, 3],
[3, 2],
[4, 1],
[5, 0]])

In [17]: vals = array(range(10,16))

In [18]: accum(subs, vals)
Out[18]:
array([[ 0,  0,  0,  0,  0, 10],
[ 0,  0,  0,  0, 11,  0],
[ 0,  0,  0, 12,  0,  0],
[ 0,  0, 13,  0,  0,  0],
[ 0, 14,  0,  0,  0,  0],
[15,  0,  0,  0,  0,  0]])```

SciPy: Cookbook/AccumarrayLike (last edited 2015-10-24 17:48:25 by anonymous)