diff options
author | Sebastian Berg <sebastian@sipsolutions.net> | 2013-02-15 22:11:48 +0100 |
---|---|---|
committer | Sebastian Berg <sebastian@sipsolutions.net> | 2013-03-04 14:12:34 +0100 |
commit | a530098e816e1164efdb356ac2280ba25bfb1c7f (patch) | |
tree | 95ec811ef8a50dd9d91a9567297451e2e4d2aa67 /numpy | |
parent | 4bf5a3feb00fe1d63e7d8fcf852cbf34e22fd60b (diff) | |
download | numpy-a530098e816e1164efdb356ac2280ba25bfb1c7f.tar.gz |
BUG: fix random.choice scalar object result and disallow 0-d arrays
Object arrays failed due to bad check for finding out if the result should
be a scalar type and not an array when size=None. Also in this case the
creation of the new array was wrong. This should be fixed with this.
The second fix is to forbid 0-d arrays. Allowing 0-d arrays does not
make much sense. But it is dangerous because for example floats will
be interpreted as 1-d arrays, while one may expect that they are interpreted
as integers. This also saves the trouble of reliably detecting all integers...
Diffstat (limited to 'numpy')
-rw-r--r-- | numpy/random/mtrand/mtrand.pyx | 50 | ||||
-rw-r--r-- | numpy/random/tests/test_random.py | 13 |
2 files changed, 45 insertions, 18 deletions
diff --git a/numpy/random/mtrand/mtrand.pyx b/numpy/random/mtrand/mtrand.pyx index f115dccdd..2d4d904bd 100644 --- a/numpy/random/mtrand/mtrand.pyx +++ b/numpy/random/mtrand/mtrand.pyx @@ -124,6 +124,7 @@ cdef extern from "initarray.h": import_array() import numpy as np +import operator cdef object cont0_array(rk_state *state, rk_cont0 func, object size): cdef double *array_data @@ -994,21 +995,24 @@ cdef class RandomState: """ # Format and Verify input - if isinstance(a, int): - if a > 0: - pop_size = a #population size - else: + a = np.array(a, copy=False) + if a.ndim == 0: + try: + # __index__ must return an integer by python rules. + pop_size = operator.index(a.item()) + except TypeError: + raise ValueError("a must be 1-dimensional or an integer") + if pop_size <= 0: raise ValueError("a must be greater than 0") + elif a.ndim != 1: + raise ValueError("a must be 1-dimensional") else: - a = np.array(a, ndmin=1, copy=0) - if a.ndim != 1: - raise ValueError("a must be 1-dimensional") - pop_size = a.size + pop_size = a.shape[0] if pop_size is 0: raise ValueError("a must be non-empty") if None != p: - p = np.array(p, dtype=np.double, ndmin=1, copy=0) + p = np.array(p, dtype=np.double, ndmin=1, copy=False) if p.ndim != 1: raise ValueError("p must be 1-dimensional") if p.size != pop_size: @@ -1019,7 +1023,10 @@ cdef class RandomState: raise ValueError("probabilities do not sum to 1") shape = size - size = 1 if shape is None else np.prod(shape, dtype=np.intp) + if shape is not None: + size = np.prod(shape, dtype=np.intp) + else: + size = 1 # Actual sampling if replace: @@ -1060,18 +1067,27 @@ cdef class RandomState: idx = self.permutation(pop_size)[:size] if shape is not None: idx.shape = shape + if shape is None and isinstance(idx, np.ndarray): # In most cases a scalar will have been made an array idx = idx.item(0) + #Use samples as indices for a if a is array-like - if isinstance(a, int): + if a.ndim == 0: return idx - res = a[idx] - # Note when introducing an axis argument a copy should be ensured. - if res.ndim == 0 and shape is not None: - # the result here is not a scalar but an array. - return np.array(res) - return res + + if shape is not None and idx.ndim == 0: + # If size == () then the user requested a 0-d array as opposed to + # a scalar object when size is None. However a[idx] is always a + # scalar and not an array. So this makes sure the result is an + # array, taking into account that np.array(item) may not work + # for object arrays. + res = np.empty((), dtype=a.dtype) + res[()] = a[idx] + return res + + return a[idx] + def uniform(self, low=0.0, high=1.0, size=None): """ diff --git a/numpy/random/tests/test_random.py b/numpy/random/tests/test_random.py index e8875e578..1621637d0 100644 --- a/numpy/random/tests/test_random.py +++ b/numpy/random/tests/test_random.py @@ -149,7 +149,8 @@ class TestRandomDist(TestCase): def test_choice_exceptions(self): sample = np.random.choice - assert_raises(ValueError, sample, -1,3) + assert_raises(ValueError, sample, -1, 3) + assert_raises(ValueError, sample, 3., 3) assert_raises(ValueError, sample, [[1,2],[3,4]], 3) assert_raises(ValueError, sample, [], 3) assert_raises(ValueError, sample, [1,2,3,4], 3, @@ -169,6 +170,11 @@ class TestRandomDist(TestCase): assert_(np.isscalar(np.random.choice(2, replace=True, p=p))) assert_(np.isscalar(np.random.choice(2, replace=False, p=p))) assert_(np.isscalar(np.random.choice([1,2], replace=True))) + assert_(np.random.choice([None], replace=True) is None) + a = np.array([1, 2]) + arr = np.empty(1, dtype=object) + arr[0] = a + assert_(np.random.choice(arr, replace=True) is a) # Check 0-d array s = tuple() @@ -177,6 +183,11 @@ class TestRandomDist(TestCase): assert_(not np.isscalar(np.random.choice(2, s, replace=True, p=p))) assert_(not np.isscalar(np.random.choice(2, s, replace=False, p=p))) assert_(not np.isscalar(np.random.choice([1,2], s, replace=True))) + assert_(np.random.choice([None], s, replace=True).ndim == 0) + a = np.array([1, 2]) + arr = np.empty(1, dtype=object) + arr[0] = a + assert_(np.random.choice(arr, s, replace=True).item() is a) # Check multi dimensional array s = (2,3) |