diff options
Diffstat (limited to 'numpy')
-rw-r--r-- | numpy/core/records.py | 28 | ||||
-rw-r--r-- | numpy/lib/_iotools.py | 59 | ||||
-rw-r--r-- | numpy/lib/histograms.py | 2 | ||||
-rw-r--r-- | numpy/ma/core.py | 46 | ||||
-rw-r--r-- | numpy/random/_generator.pyx | 5 | ||||
-rw-r--r-- | numpy/random/tests/test_generator_mt19937.py | 24 |
6 files changed, 108 insertions, 56 deletions
diff --git a/numpy/core/records.py b/numpy/core/records.py index b1ee1aa11..9c3530787 100644 --- a/numpy/core/records.py +++ b/numpy/core/records.py @@ -157,8 +157,7 @@ class format_parser: def __init__(self, formats, names, titles, aligned=False, byteorder=None): self._parseFormats(formats, aligned) self._setfieldnames(names, titles) - self._createdescr(byteorder) - self.dtype = self._descr + self._createdtype(byteorder) def _parseFormats(self, formats, aligned=False): """ Parse the field formats """ @@ -217,8 +216,8 @@ class format_parser: if self._nfields > len(titles): self._titles += [None] * (self._nfields - len(titles)) - def _createdescr(self, byteorder): - descr = sb.dtype({ + def _createdtype(self, byteorder): + dtype = sb.dtype({ 'names': self._names, 'formats': self._f_formats, 'offsets': self._offsets, @@ -226,9 +225,10 @@ class format_parser: }) if byteorder is not None: byteorder = _byteorderconv[byteorder[0]] - descr = descr.newbyteorder(byteorder) + dtype = dtype.newbyteorder(byteorder) + + self.dtype = dtype - self._descr = descr class record(nt.void): """A data-type scalar that allows field access as attribute lookup. @@ -432,7 +432,7 @@ class recarray(ndarray): if dtype is not None: descr = sb.dtype(dtype) else: - descr = format_parser(formats, names, titles, aligned, byteorder)._descr + descr = format_parser(formats, names, titles, aligned, byteorder).dtype if buf is None: self = ndarray.__new__(subtype, shape, (record, descr), order=order) @@ -625,11 +625,9 @@ def fromarrays(arrayList, dtype=None, shape=None, formats=None, if dtype is not None: descr = sb.dtype(dtype) - _names = descr.names else: - parsed = format_parser(formats, names, titles, aligned, byteorder) - _names = parsed._names - descr = parsed._descr + descr = format_parser(formats, names, titles, aligned, byteorder).dtype + _names = descr.names # Determine shape from data-type. if len(descr) != len(arrayList): @@ -694,7 +692,7 @@ def fromrecords(recList, dtype=None, shape=None, formats=None, names=None, if dtype is not None: descr = sb.dtype((record, dtype)) else: - descr = format_parser(formats, names, titles, aligned, byteorder)._descr + descr = format_parser(formats, names, titles, aligned, byteorder).dtype try: retval = sb.array(recList, dtype=descr) @@ -737,7 +735,7 @@ def fromstring(datastring, dtype=None, shape=None, offset=0, formats=None, if dtype is not None: descr = sb.dtype(dtype) else: - descr = format_parser(formats, names, titles, aligned, byteorder)._descr + descr = format_parser(formats, names, titles, aligned, byteorder).dtype itemsize = descr.itemsize @@ -810,7 +808,7 @@ def fromfile(fd, dtype=None, shape=None, offset=0, formats=None, if dtype is not None: descr = sb.dtype(dtype) else: - descr = format_parser(formats, names, titles, aligned, byteorder)._descr + descr = format_parser(formats, names, titles, aligned, byteorder).dtype itemsize = descr.itemsize @@ -851,7 +849,7 @@ def array(obj, dtype=None, shape=None, offset=0, strides=None, formats=None, dtype = sb.dtype(dtype) elif formats is not None: dtype = format_parser(formats, names, titles, - aligned, byteorder)._descr + aligned, byteorder).dtype else: kwds = {'formats': formats, 'names': names, diff --git a/numpy/lib/_iotools.py b/numpy/lib/_iotools.py index 48d130bac..ff5b94342 100644 --- a/numpy/lib/_iotools.py +++ b/numpy/lib/_iotools.py @@ -711,6 +711,26 @@ class StringConverter: return self._callingfunction(value) # + def _do_upgrade(self): + # Raise an exception if we locked the converter... + if self._locked: + errmsg = "Converter is locked and cannot be upgraded" + raise ConverterLockError(errmsg) + _statusmax = len(self._mapper) + # Complains if we try to upgrade by the maximum + _status = self._status + if _status == _statusmax: + errmsg = "Could not find a valid conversion function" + raise ConverterError(errmsg) + elif _status < _statusmax - 1: + _status += 1 + self.type, self.func, default = self._mapper[_status] + self._status = _status + if self._initial_default is not None: + self.default = self._initial_default + else: + self.default = default + def upgrade(self, value): """ Find the best converter for a given string, and return the result. @@ -736,24 +756,7 @@ class StringConverter: try: return self._strict_call(value) except ValueError: - # Raise an exception if we locked the converter... - if self._locked: - errmsg = "Converter is locked and cannot be upgraded" - raise ConverterLockError(errmsg) - _statusmax = len(self._mapper) - # Complains if we try to upgrade by the maximum - _status = self._status - if _status == _statusmax: - errmsg = "Could not find a valid conversion function" - raise ConverterError(errmsg) - elif _status < _statusmax - 1: - _status += 1 - (self.type, self.func, default) = self._mapper[_status] - self._status = _status - if self._initial_default is not None: - self.default = self._initial_default - else: - self.default = default + self._do_upgrade() return self.upgrade(value) def iterupgrade(self, value): @@ -765,25 +768,7 @@ class StringConverter: for _m in value: _strict_call(_m) except ValueError: - # Raise an exception if we locked the converter... - if self._locked: - errmsg = "Converter is locked and cannot be upgraded" - raise ConverterLockError(errmsg) - _statusmax = len(self._mapper) - # Complains if we try to upgrade by the maximum - _status = self._status - if _status == _statusmax: - raise ConverterError( - "Could not find a valid conversion function" - ) - elif _status < _statusmax - 1: - _status += 1 - (self.type, self.func, default) = self._mapper[_status] - if self._initial_default is not None: - self.default = self._initial_default - else: - self.default = default - self._status = _status + self._do_upgrade() self.iterupgrade(value) def update(self, func, default=None, testing_value=None, diff --git a/numpy/lib/histograms.py b/numpy/lib/histograms.py index 32d7df117..ede8a26e4 100644 --- a/numpy/lib/histograms.py +++ b/numpy/lib/histograms.py @@ -207,7 +207,7 @@ def _hist_bin_fd(x, range): than the standard deviation, so it is less accurate, especially for long tailed distributions. - If the IQR is 0, this function returns 1 for the number of bins. + If the IQR is 0, this function returns 0 for the bin width. Binwidth is inversely proportional to the cube root of data size (asymptotically optimal). diff --git a/numpy/ma/core.py b/numpy/ma/core.py index 41af5cc70..fa888107f 100644 --- a/numpy/ma/core.py +++ b/numpy/ma/core.py @@ -2753,6 +2753,52 @@ class MaskedArray(ndarray): in any order (either C-, Fortran-contiguous, or even discontiguous), unless a copy is required, in which case it will be C-contiguous. + Examples + -------- + + The ``mask`` can be initialized with an array of boolean values + with the same shape as ``data``. + + >>> data = np.arange(6).reshape((2, 3)) + >>> np.ma.MaskedArray(data, mask=[[False, True, False], + ... [False, False, True]]) + masked_array( + data=[[0, --, 2], + [3, 4, --]], + mask=[[False, True, False], + [False, False, True]], + fill_value=999999) + + Alternatively, the ``mask`` can be initialized to homogeneous boolean + array with the same shape as ``data`` by passing in a scalar + boolean value: + + >>> np.ma.MaskedArray(data, mask=False) + masked_array( + data=[[0, 1, 2], + [3, 4, 5]], + mask=[[False, False, False], + [False, False, False]], + fill_value=999999) + + >>> np.ma.MaskedArray(data, mask=True) + masked_array( + data=[[--, --, --], + [--, --, --]], + mask=[[ True, True, True], + [ True, True, True]], + fill_value=999999, + dtype=int64) + + .. note:: + The recommended practice for initializing ``mask`` with a scalar + boolean value is to use ``True``/``False`` rather than + ``np.True_``/``np.False_``. The reason is :attr:`nomask` + is represented internally as ``np.False_``. + + >>> np.False_ is np.ma.nomask + True + """ __array_priority__ = 15 diff --git a/numpy/random/_generator.pyx b/numpy/random/_generator.pyx index 7766f8b8c..6b8a2f70b 100644 --- a/numpy/random/_generator.pyx +++ b/numpy/random/_generator.pyx @@ -3537,10 +3537,9 @@ cdef class Generator: # approximately zero or when the covariance is not positive-semidefinite _factor = u * np.sqrt(abs(s)) else: - _factor = np.sqrt(s)[:, None] * vh + _factor = u * np.sqrt(s) - x = np.dot(x, _factor) - x += mean + x = mean + x @ _factor.T x.shape = tuple(final_shape) return x diff --git a/numpy/random/tests/test_generator_mt19937.py b/numpy/random/tests/test_generator_mt19937.py index 6f4407373..b10c1310e 100644 --- a/numpy/random/tests/test_generator_mt19937.py +++ b/numpy/random/tests/test_generator_mt19937.py @@ -1242,6 +1242,17 @@ class TestRandomDist: assert_raises(ValueError, random.multivariate_normal, mean, cov, check_valid='raise', method='eigh') + # check degenerate samples from singular covariance matrix + cov = [[1, 1], [1, 1]] + if method in ('svd', 'eigh'): + samples = random.multivariate_normal(mean, cov, size=(3, 2), + method=method) + assert_array_almost_equal(samples[..., 0], samples[..., 1], + decimal=6) + else: + assert_raises(LinAlgError, random.multivariate_normal, mean, cov, + method='cholesky') + cov = np.array([[1, 0.1], [0.1, 1]], dtype=np.float32) with suppress_warnings() as sup: random.multivariate_normal(mean, cov, method=method) @@ -1259,6 +1270,19 @@ class TestRandomDist: assert_raises(ValueError, random.multivariate_normal, mu, np.eye(3)) + @pytest.mark.parametrize("method", ["svd", "eigh", "cholesky"]) + def test_multivariate_normal_basic_stats(self, method): + random = Generator(MT19937(self.seed)) + n_s = 1000 + mean = np.array([1, 2]) + cov = np.array([[2, 1], [1, 2]]) + s = random.multivariate_normal(mean, cov, size=(n_s,), method=method) + s_center = s - mean + cov_emp = (s_center.T @ s_center) / (n_s - 1) + # these are pretty loose and are only designed to detect major errors + assert np.all(np.abs(s_center.mean(-2)) < 0.1) + assert np.all(np.abs(cov_emp - cov) < 0.2) + def test_negative_binomial(self): random = Generator(MT19937(self.seed)) actual = random.negative_binomial(n=100, p=.12345, size=(3, 2)) |