summaryrefslogtreecommitdiff
path: root/numpy
diff options
context:
space:
mode:
Diffstat (limited to 'numpy')
-rw-r--r--numpy/core/records.py28
-rw-r--r--numpy/lib/_iotools.py59
-rw-r--r--numpy/lib/histograms.py2
-rw-r--r--numpy/ma/core.py46
-rw-r--r--numpy/random/_generator.pyx5
-rw-r--r--numpy/random/tests/test_generator_mt19937.py24
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))