Skip to content

Add 'minlength' keyword to np.bincount (with docs+tests) #32

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions numpy/add_newdocs.py
Original file line number Diff line number Diff line change
Expand Up @@ -3796,12 +3796,15 @@

add_newdoc('numpy.lib._compiled_base', 'bincount',
"""
bincount(x, weights=None)
bincount(x, weights=None, minlength=None)

Count number of occurrences of each value in array of non-negative ints.

The number of bins (of size 1) is one larger than the largest value in
`x`. Each bin gives the number of occurrences of its index value in `x`.
`x`. If `minlength` is specified, there will be at least this number
of bins in the output array (though it will be longer if necessary,
depending on the contents of `x`).
Each bin gives the number of occurrences of its index value in `x`.
If `weights` is specified the input array is weighted by it, i.e. if a
value ``n`` is found at position ``i``, ``out[n] += weight[i]`` instead
of ``out[n] += 1``.
Expand All @@ -3812,6 +3815,8 @@
Input array.
weights : array_like, optional
Weights, array of the same shape as `x`.
minlength : integer, optional
A minimum number of bins for the output array.

Returns
-------
Expand All @@ -3823,7 +3828,7 @@
------
ValueError
If the input is not 1-dimensional, or contains elements with negative
values.
values, or if `minlength` is non-positive.
TypeError
If the type of the input is float or complex.

Expand Down
32 changes: 24 additions & 8 deletions numpy/lib/src/_compiled_base.c
Original file line number Diff line number Diff line change
Expand Up @@ -90,27 +90,29 @@ mnx (intp *i , intp len)
/*
* arr_bincount is registered as bincount.
*
* bincount accepts one or two arguments. The first is an array of
* non-negative integers and the second, if present, is an array of weights,
* which must be promotable to double. Call these arguments list and
* bincount accepts one, two or three arguments. The first is an array of
* non-negative integers The second, if present, is an array of weights,
* which must be promotable to double. Call these arguments list and
* weight. Both must be one-dimensional with len(weight) == len(list). If
* weight is not present then bincount(list)[i] is the number of occurrences
* of i in list. If weight is present then bincount(self,list, weight)[i]
* is the sum of all weight[j] where list [j] == i. Self is not used.
* The third argument, if present, is a minimum length desired for the
* output array.
*/
static PyObject *
arr_bincount(PyObject *NPY_UNUSED(self), PyObject *args, PyObject *kwds)
{
PyArray_Descr *type;
PyObject *list = NULL, *weight=Py_None;
PyObject *list = NULL, *weight=Py_None, *mlength=Py_None;
PyObject *lst=NULL, *ans=NULL, *wts=NULL;
intp *numbers, *ians, len , mxi, mni, ans_size;
intp *numbers, *ians, len , mxi, mni, ans_size, minlength;
int i;
double *weights , *dans;
static char *kwlist[] = {"list", "weights", NULL};
static char *kwlist[] = {"list", "weights", "minlength", NULL};

if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O",
kwlist, &list, &weight)) {
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|OO",
kwlist, &list, &weight, &mlength)) {
goto fail;
}
if (!(lst = PyArray_ContiguousFromAny(list, PyArray_INTP, 1, 1))) {
Expand All @@ -131,6 +133,20 @@ arr_bincount(PyObject *NPY_UNUSED(self), PyObject *args, PyObject *kwds)
goto fail;
}
ans_size = numbers [mxi] + 1;
if (mlength != Py_None) {
if (!(minlength = PyArray_PyIntAsIntp(mlength))) {
goto fail;
}
if (minlength <= 0) {
/* superfluous, but may catch incorrect usage */
PyErr_SetString(PyExc_ValueError,
"minlength, if specified, must be positive.");
goto fail;
}
if (ans_size < minlength) {
ans_size = minlength;
}
}
type = PyArray_DescrFromType(PyArray_INTP);
if (weight == Py_None) {
if (!(ans = PyArray_Zeros(1, &ans_size, type, 0))) {
Expand Down
15 changes: 15 additions & 0 deletions numpy/lib/tests/test_function_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -1026,6 +1026,21 @@ def test_simple_weight2(self):
y = np.bincount(x, w)
assert_array_equal(y, np.array([0, 0.2, 0.5, 0, 0.5, 0.1]))

def test_with_minlength(self):
x = np.array([0, 1, 0, 1, 1])
y = np.bincount(x, minlength=3)
assert_array_equal(y, np.array([2, 3, 0]))

def test_with_minlength_smaller_than_maxvalue(self):
x = np.array([0, 1, 1, 2, 2, 3, 3])
y = np.bincount(x, minlength=2)
assert_array_equal(y, np.array([1, 2, 2, 2]))

def test_with_minlength_and_weights(self):
x = np.array([1, 2, 4, 5, 2])
w = np.array([0.2, 0.3, 0.5, 0.1, 0.2])
y = np.bincount(x, w, 8)
assert_array_equal(y, np.array([0, 0.2, 0.5, 0, 0.5, 0.1, 0, 0]))

class TestInterp(TestCase):
def test_exceptions(self):
Expand Down