diff options
| author | David Cournapeau <cournape@gmail.com> | 2008-12-22 13:23:03 +0000 |
|---|---|---|
| committer | David Cournapeau <cournape@gmail.com> | 2008-12-22 13:23:03 +0000 |
| commit | d10d5631368f97af5babd503371a862eaecce8d6 (patch) | |
| tree | 712d1c42f443e9ff82ebbbd55e349ac468391bf6 /numpy | |
| parent | 0715c87750409afd8785367c59c524d55ba13431 (diff) | |
| parent | fbe2af255824ad463da6936ab86738429008aee9 (diff) | |
| download | numpy-d10d5631368f97af5babd503371a862eaecce8d6.tar.gz | |
Merged revisions 6108,6110,6112-6127,6129-6134,6136-6138,6140-6149,6174-6175,6179-6182,6185-6187 via svnmerge from
http://svn.scipy.org/svn/numpy/trunk
........
r6108 | pierregm | 2008-11-26 11:13:57 +0900 (Wed, 26 Nov 2008) | 3 lines
* added ma.diag
* added copy, cumprod, cumsum, harden_mask, prod, round, soften_mask, squeeze to the namespace
* TEMPORARILY fixed a pb of compatibility with python 2.6 (involvingin(np.nan))
........
r6110 | pierregm | 2008-11-27 13:29:43 +0900 (Thu, 27 Nov 2008) | 5 lines
* Added get_object_signature to fix missing signatures
* Fixed .getdoc from _arraymethod, _frommethod, _convert2ma, _fromnxfunction
* Fixed the docstrings of .trace, .mean, .argsort, .sort
* Suppressed duplicated conjugate, ptp, round, expand_dims, apply_along_axis, compress_rowcols, mask_rowcols, vander, polyfit
........
r6112 | pierregm | 2008-11-27 15:56:12 +0900 (Thu, 27 Nov 2008) | 1 line
Doc update
........
r6113 | jarrod.millman | 2008-11-27 19:58:51 +0900 (Thu, 27 Nov 2008) | 2 lines
add release notes for 1.3
........
r6114 | ptvirtan | 2008-11-28 05:26:04 +0900 (Fri, 28 Nov 2008) | 1 line
doc: include release notes to Sphinx build
........
r6115 | charris | 2008-11-28 12:52:16 +0900 (Fri, 28 Nov 2008) | 2 lines
Make numpy version of atanh more robust.
Numpy log1p still needs a major overhaul.
........
r6116 | charris | 2008-11-28 14:34:33 +0900 (Fri, 28 Nov 2008) | 5 lines
Add preliminary docstrings for:
log2, exp2, logaddexp, logaddexp2, rad2deg, deg2rad.
The complete docstring for fmin and fmax are on the web but
haven't yet been merged.
........
r6117 | cdavid | 2008-11-29 01:47:34 +0900 (Sat, 29 Nov 2008) | 1 line
Fix typo in core scons script.
........
r6118 | cdavid | 2008-11-29 01:50:08 +0900 (Sat, 29 Nov 2008) | 1 line
Anoter typo on core scons script.
........
r6119 | stefan | 2008-11-29 21:07:07 +0900 (Sat, 29 Nov 2008) | 1 line
Add memory map support to `load` [patch by Gael Varoquaux]. Closes #954.
........
r6120 | stefan | 2008-11-29 21:07:54 +0900 (Sat, 29 Nov 2008) | 1 line
Add test for load's mmap_mode.
........
r6121 | stefan | 2008-11-29 21:08:29 +0900 (Sat, 29 Nov 2008) | 1 line
Opening a memmap requires a filename. Raise an error otherwise.
........
r6122 | stefan | 2008-11-29 21:09:07 +0900 (Sat, 29 Nov 2008) | 1 line
Reformat spacing in io tests.
........
r6123 | stefan | 2008-11-29 23:53:44 +0900 (Sat, 29 Nov 2008) | 1 line
Identify file object using 'readline', rather than 'seek'.
........
r6124 | stefan | 2008-11-29 23:54:29 +0900 (Sat, 29 Nov 2008) | 1 line
Add bz2 support to loadtxt [patch by Ryan May].
........
r6125 | ptvirtan | 2008-11-30 23:44:38 +0900 (Sun, 30 Nov 2008) | 1 line
Move Sphinx extensions under Numpy's SVN trunk
........
r6126 | ptvirtan | 2008-12-01 00:08:38 +0900 (Mon, 01 Dec 2008) | 1 line
Rename core/.../docstrings.py to ufunc_docstrings.py
........
r6127 | pierregm | 2008-12-01 18:45:51 +0900 (Mon, 01 Dec 2008) | 1 line
Fixed make_mask_descr for nested dtypes
........
r6129 | pierregm | 2008-12-02 02:56:58 +0900 (Tue, 02 Dec 2008) | 2 lines
* added flatten_mask to collapse masks w/ (nested) flexible types.
* fixed __getitem__ on arrays w/ nested dtype
........
r6130 | pierregm | 2008-12-02 11:40:22 +0900 (Tue, 02 Dec 2008) | 3 lines
* Fixed MaskedArray for nested dtype w/ input mask
* Fixed masked_all for nested dtype
* Fixed masked_all_like for nested dtype
........
r6131 | pierregm | 2008-12-02 17:50:11 +0900 (Tue, 02 Dec 2008) | 1 line
* Fixed make_mask_descr for dtype w/ composite names, like [(('A','B'), float)]
........
r6132 | pierregm | 2008-12-03 03:42:12 +0900 (Wed, 03 Dec 2008) | 1 line
* Cleaned up make_mask_descr
........
r6133 | ptvirtan | 2008-12-04 06:52:36 +0900 (Thu, 04 Dec 2008) | 1 line
Refactor plot:: directive somewhat
........
r6134 | ptvirtan | 2008-12-04 07:15:51 +0900 (Thu, 04 Dec 2008) | 1 line
sphinxext: fix a small bug in plot directive
........
r6136 | cdavid | 2008-12-04 12:21:51 +0900 (Thu, 04 Dec 2008) | 1 line
Add /arch option to superpack installer to override detected arch.
........
r6137 | ptvirtan | 2008-12-05 08:06:29 +0900 (Fri, 05 Dec 2008) | 1 line
sphinxext: support autosummary:: directives in automodule docstrings
........
r6138 | pierregm | 2008-12-06 05:40:44 +0900 (Sat, 06 Dec 2008) | 2 lines
* Added MaskError
* If a bool or int ndarray is given as the explicit output of var/min/max, an exception is raised if the function should have output np.nan
........
r6140 | ptvirtan | 2008-12-14 01:18:04 +0900 (Sun, 14 Dec 2008) | 1 line
Get lstsq and eigvals from numpy.linalg, not from numpy.dual. Addresses Scipy ticket #800
........
r6141 | ptvirtan | 2008-12-14 06:02:05 +0900 (Sun, 14 Dec 2008) | 12 lines
docs: fix minor issues, support htmlhelp.
- Don't use :members: in automodule; it generates too much
and not very useful output
- Fix edit links and summarize.py
- Add better htmlhelp build target
- Add upload target
- Fix permissions on make dist
........
r6142 | jarrod.millman | 2008-12-14 19:32:51 +0900 (Sun, 14 Dec 2008) | 2 lines
wordsmithing
........
r6143 | jarrod.millman | 2008-12-16 20:21:52 +0900 (Tue, 16 Dec 2008) | 2 lines
added missing THANKS for Alan's testing work this summer
........
r6144 | cdavid | 2008-12-17 03:04:24 +0900 (Wed, 17 Dec 2008) | 1 line
BUG: Do not harcode fortran runtime when copying it on windows. Should fix #969.
........
r6145 | cdavid | 2008-12-17 03:26:13 +0900 (Wed, 17 Dec 2008) | 1 line
Add a function to get configured target for gfortran.
........
r6146 | cdavid | 2008-12-17 03:32:41 +0900 (Wed, 17 Dec 2008) | 1 line
Fix get_target.
........
r6147 | cdavid | 2008-12-17 03:41:32 +0900 (Wed, 17 Dec 2008) | 1 line
Add target specific lib dir for gfortran on windows when msvc is the C compiler.
........
r6148 | cdavid | 2008-12-17 03:48:37 +0900 (Wed, 17 Dec 2008) | 1 line
Fix overriding of library_dirs.
........
r6149 | cdavid | 2008-12-17 03:53:25 +0900 (Wed, 17 Dec 2008) | 1 line
Add mingw32 and mingwex libraries as runtime libraries for extensions which use fortran and are built with gfortran+MS compiler.
........
r6174 | ptvirtan | 2008-12-20 02:58:57 +0900 (Sat, 20 Dec 2008) | 1 line
docs: put CHM files in a zip
........
r6175 | ptvirtan | 2008-12-20 22:40:30 +0900 (Sat, 20 Dec 2008) | 1 line
test_umath: don't check against cmath on branch cuts, since the behavior of our functions varies across platforms on them
........
r6179 | cdavid | 2008-12-21 15:02:29 +0900 (Sun, 21 Dec 2008) | 1 line
Do not declare missing functions to avoid mismatch with potentially conflicting, undetected ones
........
r6180 | cdavid | 2008-12-21 15:02:44 +0900 (Sun, 21 Dec 2008) | 1 line
Update comments in umath.
........
r6181 | cdavid | 2008-12-21 15:03:05 +0900 (Sun, 21 Dec 2008) | 1 line
Do not set function to macro in umath anymore.
........
r6182 | cdavid | 2008-12-21 15:03:19 +0900 (Sun, 21 Dec 2008) | 1 line
Do not define math func as static: better to have a link error when we have a config problem than having two functions with the same name.
........
r6185 | cdavid | 2008-12-22 01:19:14 +0900 (Mon, 22 Dec 2008) | 1 line
Add doc sources so that sdist tarball contains them.
........
r6186 | pierregm | 2008-12-22 19:01:51 +0900 (Mon, 22 Dec 2008) | 4 lines
testutils:
assert_array_compare : make sure that the comparison is performed on ndarrays, and make sure we use the np version of the comparison function.
core:
* Try not to touch the data in unary/binary ufuncs, (including inplace)
........
r6187 | pearu | 2008-12-22 19:05:00 +0900 (Mon, 22 Dec 2008) | 1 line
Fix a bug.
........
Diffstat (limited to 'numpy')
| -rw-r--r-- | numpy/core/SConscript | 22 | ||||
| -rw-r--r-- | numpy/core/code_generators/generate_umath.py | 16 | ||||
| -rw-r--r-- | numpy/core/code_generators/ufunc_docstrings.py (renamed from numpy/core/code_generators/docstrings.py) | 186 | ||||
| -rw-r--r-- | numpy/core/src/umath_funcs_c99.inc.src | 137 | ||||
| -rw-r--r-- | numpy/core/tests/test_umath.py | 2 | ||||
| -rw-r--r-- | numpy/distutils/fcompiler/gnu.py | 37 | ||||
| -rwxr-xr-x | numpy/f2py/crackfortran.py | 4 | ||||
| -rw-r--r-- | numpy/lib/format.py | 8 | ||||
| -rw-r--r-- | numpy/lib/io.py | 52 | ||||
| -rw-r--r-- | numpy/lib/polynomial.py | 31 | ||||
| -rw-r--r-- | numpy/lib/tests/test_io.py | 153 | ||||
| -rw-r--r-- | numpy/ma/core.py | 604 | ||||
| -rw-r--r-- | numpy/ma/extras.py | 195 | ||||
| -rw-r--r-- | numpy/ma/tests/test_core.py | 191 | ||||
| -rw-r--r-- | numpy/ma/tests/test_extras.py | 56 | ||||
| -rw-r--r-- | numpy/ma/testutils.py | 16 |
16 files changed, 1122 insertions, 588 deletions
diff --git a/numpy/core/SConscript b/numpy/core/SConscript index 0ffbb444a..70d56c902 100644 --- a/numpy/core/SConscript +++ b/numpy/core/SConscript @@ -1,4 +1,4 @@ -# Last Change: Fri Oct 03 04:00 PM 2008 J +# Last Change: Sat Nov 29 01:00 AM 2008 J # vim:syntax=python import os import sys @@ -137,9 +137,9 @@ mfuncs = ('expl', 'expf', 'log1p', 'expm1', 'asinh', 'atanhf', 'atanhl', mfuncs_defined = dict([(f, 0) for f in mfuncs]) # Check for mandatory funcs: we barf if a single one of those is not there - mandatory_funcs = ["sin", "cos", "tan", "sinh", "cosh", "tanh", "fabs", - "floor", "ceil", "sqrt", "log10", "log", "exp", "asin", - "acos", "atan", "fmod", 'modf', 'frexp', 'ldexp'] +mandatory_funcs = ["sin", "cos", "tan", "sinh", "cosh", "tanh", "fabs", + "floor", "ceil", "sqrt", "log10", "log", "exp", "asin", + "acos", "atan", "fmod", 'modf', 'frexp', 'ldexp'] if not config.CheckFuncsAtOnce(mandatory_funcs): raise SystemError("One of the required function to build numpy is not" @@ -159,17 +159,17 @@ def check_funcs(funcs): # XXX: we do not test for hypot because python checks for it (HAVE_HYPOT in # python.h... I wish they would clean their public headers someday) - optional_stdfuncs = ["expm1", "log1p", "acosh", "asinh", "atanh", - "rint", "trunc", "exp2", "log2"] +optional_stdfuncs = ["expm1", "log1p", "acosh", "asinh", "atanh", + "rint", "trunc", "exp2", "log2"] check_funcs(optional_stdfuncs) # C99 functions: float and long double versions - c99_funcs = ["sin", "cos", "tan", "sinh", "cosh", "tanh", "fabs", "floor", - "ceil", "rint", "trunc", "sqrt", "log10", "log", "log1p", "exp", - "expm1", "asin", "acos", "atan", "asinh", "acosh", "atanh", - "hypot", "atan2", "pow", "fmod", "modf", 'frexp', 'ldexp', - "exp2", "log2"] +c99_funcs = ["sin", "cos", "tan", "sinh", "cosh", "tanh", "fabs", "floor", + "ceil", "rint", "trunc", "sqrt", "log10", "log", "log1p", "exp", + "expm1", "asin", "acos", "atan", "asinh", "acosh", "atanh", + "hypot", "atan2", "pow", "fmod", "modf", 'frexp', 'ldexp', + "exp2", "log2"] for prec in ['l', 'f']: fns = [f + prec for f in c99_funcs] diff --git a/numpy/core/code_generators/generate_umath.py b/numpy/core/code_generators/generate_umath.py index 00fde1360..a34dcc540 100644 --- a/numpy/core/code_generators/generate_umath.py +++ b/numpy/core/code_generators/generate_umath.py @@ -1,7 +1,7 @@ import re, textwrap import sys, os sys.path.insert(0, os.path.dirname(__file__)) -import docstrings +import ufunc_docstrings as docstrings sys.path.pop(0) Zero = "PyUFunc_Zero" @@ -330,12 +330,12 @@ defdict = { ), 'logaddexp' : Ufunc(2, 1, None, - "", + docstrings.get('numpy.core.umath.logaddexp'), TD(flts, f="logaddexp") ), 'logaddexp2' : Ufunc(2, 1, None, - "", + docstrings.get('numpy.core.umath.logaddexp2'), TD(flts, f="logaddexp2") ), 'bitwise_and' : @@ -381,7 +381,7 @@ defdict = { ), 'rad2deg' : Ufunc(1, 1, None, - '', + docstrings.get('numpy.core.umath.rad2deg'), TD(fltsM, f='rad2deg'), ), 'radians' : @@ -391,7 +391,7 @@ defdict = { ), 'deg2rad' : Ufunc(1, 1, None, - '', + docstrings.get('numpy.core.umath.deg2rad'), TD(fltsM, f='deg2rad'), ), 'arccos' : @@ -474,7 +474,7 @@ defdict = { ), 'exp2' : Ufunc(1, 1, None, - '', + docstrings.get('numpy.core.umath.exp2'), TD(flts, f='exp2'), TD(M, f='exp2'), ), @@ -492,7 +492,7 @@ defdict = { ), 'log2' : Ufunc(1, 1, None, - '', + docstrings.get('numpy.core.umath.log2'), TD(flts, f='log2'), TD(M, f='log2'), ), @@ -522,7 +522,7 @@ defdict = { ), 'trunc' : Ufunc(1, 1, None, - '', + docstrings.get('numpy.core.umath.trunc'), TD(flts, f='trunc'), TD(M, f='trunc'), ), diff --git a/numpy/core/code_generators/docstrings.py b/numpy/core/code_generators/ufunc_docstrings.py index 882a781a8..08d544bc5 100644 --- a/numpy/core/code_generators/docstrings.py +++ b/numpy/core/code_generators/ufunc_docstrings.py @@ -691,8 +691,9 @@ add_newdoc('numpy.core.umath', 'trunc', """ Return the truncated value of the input, element-wise. - The truncated value of the scalar `x` is the nearest integer `i`, such - that i is not larger than x amplitude + The truncated value of the scalar `x` is the nearest integer `i` which + is closer to zero than `x` is. In short, the fractional part of the + signed number `x` is discarded. Parameters ---------- @@ -789,7 +790,9 @@ add_newdoc('numpy.core.umath', 'cosh', add_newdoc('numpy.core.umath', 'degrees', """ - Convert angles from radians to degrees. + Convert angles from radians to degrees. This is the same + function as rad2deg but the latter is preferred because of + the more descriptive name. Parameters ---------- @@ -804,6 +807,8 @@ add_newdoc('numpy.core.umath', 'degrees', See Also -------- + rad2deg : Convert angles from radians to degrees. + deg2rad : Convert angles from degrees to radians. radians : Convert angles from degrees to radians. unwrap : Remove large jumps in angle by wrapping. @@ -818,6 +823,41 @@ add_newdoc('numpy.core.umath', 'degrees', """) +add_newdoc('numpy.core.umath', 'rad2deg', + """ + Convert angles from radians to degrees. This is the same + function as degrees but is preferred because its more + descriptive name. + + Parameters + ---------- + x : array_like + Angle in radians. + + Returns + ------- + y : ndarray + The corresponding angle in degrees. + + + See Also + -------- + degrees : Convert angles from radians to degrees. + deg2rad : Convert angles from degrees to radians. + radians : Convert angles from degrees to radians. + unwrap : Remove large jumps in angle by wrapping. + + Notes + ----- + rad2deg(x) is ``180 * x / pi``. + + Examples + -------- + >>> np.rad2deg(np.pi/2) + 90.0 + + """) + add_newdoc('numpy.core.umath', 'divide', """ Divide arguments element-wise. @@ -962,6 +1002,22 @@ add_newdoc('numpy.core.umath', 'exp', """) +add_newdoc('numpy.core.umath', 'exp2', + """ + Calculate `2**p` for all `p` in the input array. + + Parameters + ---------- + x : array_like + Input values. + + Returns + ------- + out : ndarray + Element-wise 2 to the power `x`. + + """) + add_newdoc('numpy.core.umath', 'expm1', """ Return the exponential of the elements in the array minus one. @@ -1661,6 +1717,82 @@ add_newdoc('numpy.core.umath', 'log10', """) +add_newdoc('numpy.core.umath', 'log2', + """ + Base-2 logarithm of `x`. + + Parameters + ---------- + x : array_like + Input values. + + Returns + ------- + y : ndarray + Base-2 logarithm of `x`. + + See Also + -------- + log, log10, log1p + + """) + +add_newdoc('numpy.core.umath', 'logaddexp', + """ + Logarithm of `exp(x) + exp(y)`. + + This function is useful in statistics where the calculated probabilities of + events may be so small as to excede the range of normal floating point + numbers. In such cases the logarithm of the calculated probability is + stored. This function allows adding probabilities stored in such a fashion. + + Parameters + ---------- + x : array_like + Input values. + y : array_like + Input values. + + + Returns + ------- + result : ndarray + Logarithm of `exp(x) + exp(y)`. + + See Also + -------- + logaddexp2 + + """) + +add_newdoc('numpy.core.umath', 'logaddexp2', + """ + Base-2 Logarithm of `2**x + 2**y`. + + This function is useful in machine learning when the calculated probabilities of + events may be so small as to excede the range of normal floating point + numbers. In such cases the base-2 logarithm of the calculated probability + can be used instead. This function allows adding probabilities stored in such a fashion. + + Parameters + ---------- + x : array_like + Input values. + y : array_like + Input values. + + + Returns + ------- + result : ndarray + Base-2 logarithm of `2**x + 2**y`. + + See Also + -------- + logaddexp + + """) + add_newdoc('numpy.core.umath', 'log1p', """ `log(1 + x)` in base `e`, elementwise. @@ -1921,6 +2053,16 @@ add_newdoc('numpy.core.umath', 'minimum', """) +add_newdoc('numpy.core.umath', 'fmax', + """ + + """) + +add_newdoc('numpy.core.umath', 'fmin', + """ + + """) + add_newdoc('numpy.core.umath', 'modf', """ Return the fractional and integral part of a number. @@ -2102,7 +2244,8 @@ add_newdoc('numpy.core.umath', 'power', add_newdoc('numpy.core.umath', 'radians', """ - Convert angles from degrees to radians. + Convert angles from degrees to radians. This function is + the same as deg2rad, which is more descriptive.. Parameters ---------- @@ -2116,6 +2259,8 @@ add_newdoc('numpy.core.umath', 'radians', See Also -------- + deg2rad : Convert angles from degrees to radians. + rad2deg : Convert angles from radians to degrees. degrees : Convert angles from radians to degrees. unwrap : Remove large jumps in angle by wrapping. @@ -2130,6 +2275,39 @@ add_newdoc('numpy.core.umath', 'radians', """) +add_newdoc('numpy.core.umath', 'deg2rad', + """ + Convert angles from degrees to radians. This is the same + function as radians, but deg2rad is a more descriptive name. + + Parameters + ---------- + x : array_like + Angles in degrees. + + Returns + ------- + y : ndarray + The corresponding angle in radians. + + See Also + -------- + radians : Convert angles from degrees to radians. + rad2deg : Convert angles from radians to degrees. + degrees : Convert angles from radians to degrees. + unwrap : Remove large jumps in angle by wrapping. + + Notes + ----- + ``deg2rad(x)`` is ``x * pi / 180``. + + Examples + -------- + >>> np.deg2rad(180) + 3.1415926535897931 + + """) + add_newdoc('numpy.core.umath', 'reciprocal', """ Return element-wise reciprocal. diff --git a/numpy/core/src/umath_funcs_c99.inc.src b/numpy/core/src/umath_funcs_c99.inc.src index 828331875..e0bcdebee 100644 --- a/numpy/core/src/umath_funcs_c99.inc.src +++ b/numpy/core/src/umath_funcs_c99.inc.src @@ -17,9 +17,7 @@ * can be linked from the math library. The result can depend on the * optimization flags as well as the compiler, so can't be known ahead of * time. If the function can't be linked, then either it is absent, defined - * as a macro, or is an intrinsic (hardware) function. If it is linkable it - * may still be the case that no prototype is available. So to cover all the - * cases requires the following construction. + * as a macro, or is an intrinsic (hardware) function. * * i) Undefine any possible macros: * @@ -27,44 +25,20 @@ * #undef foo * #endif * - * ii) Check if the function was in the library, If not, define the - * function with npy_ prepended to its name to avoid conflict with any - * intrinsic versions, then use a define so that the preprocessor will - * replace foo with npy_foo before the compilation pass. Make the - * function static to avoid poluting the module library. + * ii) Avoid as much as possible to declare any function here. Declaring + * functions is not portable: some platforms define some function inline + * with a non standard identifier, for example, or may put another + * idendifier which changes the calling convention of the function. If you + * really have to, ALWAYS declare it for the one platform you are dealing + * with: * - * #ifdef foo - * #undef foo - * #endif - * #ifndef HAVE_FOO - * static double - * npy_foo(double x) - * { - * return x; - * } - * #define foo npy_foo - * - * iii) Finally, even if foo is in the library, add a prototype. Just being - * in the library doesn't guarantee a prototype in math.h, and in any case - * you want to make sure the prototype is what you think it is. Count on it, - * whatever can go wrong will go wrong. Think defensively! The result: - * - * #ifdef foo - * #undef foo - * #endif - * #ifndef HAVE_FOO - * static double - * npy_foo(double x) - * { - * return x; - * } - * #define foo npy_foo - * #else - * double foo(double x); - * #end - * - * And there you have it. + * Not ok: + * double exp(double a); * + * Ok: + * #ifdef SYMBOL_DEFINED_WEIRD_PLATFORM + * double exp(double); + * #endif */ /* @@ -82,8 +56,7 @@ /* Original code by Konrad Hinsen. */ #ifndef HAVE_EXPM1 -static double -npy_expm1(double x) +double expm1(double x) { double u = exp(x); if (u == 1.0) { @@ -94,14 +67,10 @@ npy_expm1(double x) return (u-1.0) * x/log(u); } } -#define expm1 npy_expm1 -#else -double expm1(double x); #endif #ifndef HAVE_LOG1P -static double -npy_log1p(double x) +double log1p(double x) { double u = 1. + x; if (u == 1.0) { @@ -110,14 +79,10 @@ npy_log1p(double x) return log(u) * x / (u - 1); } } -#define log1p npy_log1p -#else -double log1p(double x); #endif #ifndef HAVE_HYPOT -static double -npy_hypot(double x, double y) +double hypot(double x, double y) { double yx; @@ -135,25 +100,17 @@ npy_hypot(double x, double y) return x*sqrt(1.+yx*yx); } } -#define hypot npy_hypot -#else -double hypot(double x, double y); #endif #ifndef HAVE_ACOSH -static double -npy_acosh(double x) +double acosh(double x) { return 2*log(sqrt((x+1.0)/2)+sqrt((x-1.0)/2)); } -#define acosh npy_acosh -#else -double acosh(double x); #endif #ifndef HAVE_ASINH -static double -npy_asinh(double xx) +double asinh(double xx) { double x, d; int sign; @@ -172,25 +129,22 @@ npy_asinh(double xx) } return sign*log1p(x*(1.0 + x/(d+1))); } -#define asinh npy_asinh -#else -double asinh(double xx); #endif #ifndef HAVE_ATANH -static double -npy_atanh(double x) +double atanh(double x) { - return 0.5*log1p(2.0*x/(1.0-x)); + if (x > 0) { + return -0.5*log1p(-2.0*x/(1.0 + x)); + } + else { + return 0.5*log1p(2.0*x/(1.0 - x)); + } } -#define atanh npy_atanh -#else -double atanh(double x); #endif #ifndef HAVE_RINT -static double -npy_rint(double x) +double rint(double x) { double y, r; @@ -209,46 +163,31 @@ npy_rint(double x) } return y; } -#define rint npy_rint -#else -double rint(double x); #endif #ifndef HAVE_TRUNC -static double -npy_trunc(double x) +double trunc(double x) { return x < 0 ? ceil(x) : floor(x); } -#define trunc npy_trunc -#else -double trunc(double x); #endif #ifndef HAVE_EXP2 #define LOG2 0.69314718055994530943 -static double -npy_exp2(double x) +double exp2(double x) { return exp(LOG2*x); } -#define exp2 npy_exp2 #undef LOG2 -#else -double exp2(double x); #endif #ifndef HAVE_LOG2 #define INVLOG2 1.4426950408889634074 -static double -npy_log2(double x) +double log2(double x) { return INVLOG2*log(x); } -#define log2 npy_log2 #undef INVLOG2 -#else -double log2(double x); #endif /* @@ -326,14 +265,10 @@ static int signbit_ld (long double x) #undef @kind@@c@ #endif #ifndef HAVE_@KIND@@C@ -static @type@ -npy_@kind@@c@(@type@ x) +@type@ @kind@@c@(@type@ x) { return (@type@) @kind@((double)x); } -#define @kind@@c@ npy_@kind@@c@ -#else -@type@ @kind@@c@(@type@ x); #endif /**end repeat1**/ @@ -346,14 +281,10 @@ npy_@kind@@c@(@type@ x) #undef @kind@@c@ #endif #ifndef HAVE_@KIND@@C@ -static @type@ -npy_@kind@@c@(@type@ x, @type@ y) +@type@ @kind@@c@(@type@ x, @type@ y) { return (@type@) @kind@((double)x, (double) y); } -#define @kind@@c@ npy_@kind@@c@ -#else -@type@ @kind@@c@(@type@ x, @type@ y); #endif /**end repeat1**/ @@ -361,17 +292,13 @@ npy_@kind@@c@(@type@ x, @type@ y) #undef modf@c@ #endif #ifndef HAVE_MODF@C@ -static @type@ -npy_modf@c@(@type@ x, @type@ *iptr) +@type@ modf@c@(@type@ x, @type@ *iptr) { double niptr; double y = modf((double)x, &niptr); *iptr = (@type@) niptr; return (@type@) y; } -#define modf@c@ npy_modf@c@ -#else -@type@ modf@c@(@type@ x, @type@ *iptr); #endif /**end repeat**/ diff --git a/numpy/core/tests/test_umath.py b/numpy/core/tests/test_umath.py index 0dab9ac3a..1b4ba97ca 100644 --- a/numpy/core/tests/test_umath.py +++ b/numpy/core/tests/test_umath.py @@ -404,7 +404,7 @@ class TestComplexFunctions(object): if sys.version_info < (2,5,3): broken_cmath_asinh = True - points = [-2, 2j, 2, -2j, -1-1j, -1+1j, +1-1j, +1+1j] + points = [-1-1j, -1+1j, +1-1j, +1+1j] name_map = {'arcsin': 'asin', 'arccos': 'acos', 'arctan': 'atan', 'arcsinh': 'asinh', 'arccosh': 'acosh', 'arctanh': 'atanh'} atol = 4*np.finfo(np.complex).eps diff --git a/numpy/distutils/fcompiler/gnu.py b/numpy/distutils/fcompiler/gnu.py index bd9284ba7..7b642aff3 100644 --- a/numpy/distutils/fcompiler/gnu.py +++ b/numpy/distutils/fcompiler/gnu.py @@ -10,6 +10,7 @@ from numpy.distutils.misc_util import msvc_runtime_library compilers = ['GnuFCompiler', 'Gnu95FCompiler'] +TARGET_R = re.compile("Target: ([a-zA-Z0-9_\-]*)") class GnuFCompiler(FCompiler): compiler_type = 'gnu' compiler_aliases = ('g77',) @@ -130,10 +131,10 @@ class GnuFCompiler(FCompiler): # if windows and not cygwin, libg2c lies in a different folder if sys.platform == 'win32' and not d.startswith('/usr/lib'): d = os.path.normpath(d) - if not os.path.exists(os.path.join(d, 'libg2c.a')): + if not os.path.exists(os.path.join(d, "lib%s.a" % self.g2c)): d2 = os.path.abspath(os.path.join(d, '../../../../lib')) - if os.path.exists(os.path.join(d2, 'libg2c.a')): + if os.path.exists(os.path.join(d2, "lib%s.a" % self.g2c)): opt.append(d2) opt.append(d) return opt @@ -269,12 +270,44 @@ class Gnu95FCompiler(GnuFCompiler): flags = GnuFCompiler.get_flags_linker_so(self) return self._add_arches_for_universal_build(flags) + def get_library_dirs(self): + opt = GnuFCompiler.get_library_dirs(self) + if sys.platform == 'win32': + c_compiler = self.c_compiler + if c_compiler and c_compiler.compiler_type == "msvc": + target = self.get_target() + if target: + d = os.path.normpath(self.get_libgcc_dir()) + root = os.path.join(d, os.pardir, os.pardir, os.pardir, os.pardir) + mingwdir = os.path.normpath(os.path.join(root, target, "lib")) + full = os.path.join(mingwdir, "libmingwex.a") + if os.path.exists(full): + opt.append(mingwdir) + return opt + def get_libraries(self): opt = GnuFCompiler.get_libraries(self) if sys.platform == 'darwin': opt.remove('cc_dynamic') + if sys.platform == 'win32': + c_compiler = self.c_compiler + if c_compiler and c_compiler.compiler_type == "msvc": + if "gcc" in opt: + i = opt.index("gcc") + opt.insert(i+1, "mingwex") + opt.insert(i+1, "mingw32") return opt + def get_target(self): + status, output = exec_command(self.compiler_f77 + + ['-v'], + use_tee=0) + if not status: + m = TARGET_R.search(output) + if m: + return m.group(1) + return "" + if __name__ == '__main__': from distutils import log log.set_verbosity(2) diff --git a/numpy/f2py/crackfortran.py b/numpy/f2py/crackfortran.py index 8e5f2882f..449db33a3 100755 --- a/numpy/f2py/crackfortran.py +++ b/numpy/f2py/crackfortran.py @@ -2446,9 +2446,9 @@ def crack2fortrangen(block,tab='\n'): global skipfuncs, onlyfuncs setmesstext(block) ret='' - if type(block) is type([]): + if isinstance(block, list): for g in block: - if g['block'] in ['function','subroutine']: + if g and g['block'] in ['function','subroutine']: if g['name'] in skipfuncs: continue if onlyfuncs and g['name'] not in onlyfuncs: diff --git a/numpy/lib/format.py b/numpy/lib/format.py index 32a23ae18..28444613c 100644 --- a/numpy/lib/format.py +++ b/numpy/lib/format.py @@ -381,7 +381,7 @@ def read_array(fp): def open_memmap(filename, mode='r+', dtype=None, shape=None, - fortran_order=False, version=(1,0)): + fortran_order=False, version=(1,0)): """ Open a .npy file as a memory-mapped array. @@ -390,7 +390,7 @@ def open_memmap(filename, mode='r+', dtype=None, shape=None, Parameters ---------- filename : str - The name of the file on disk. This may not be a filelike object. + The name of the file on disk. This may not be a file-like object. mode : str, optional The mode to open the file with. In addition to the standard file modes, 'c' is also accepted to mean "copy on write". See `numpy.memmap` for @@ -425,6 +425,10 @@ def open_memmap(filename, mode='r+', dtype=None, shape=None, numpy.memmap """ + if not isinstance(filename, basestring): + raise ValueError("Filename must be a string. Memmap cannot use" \ + " existing file handles.") + if 'w' in mode: # We are creating the file, not reading it. # Check if we ought to create the file. diff --git a/numpy/lib/io.py b/numpy/lib/io.py index deb86fb61..e9a012db1 100644 --- a/numpy/lib/io.py +++ b/numpy/lib/io.py @@ -79,7 +79,7 @@ class NpzFile(object): else: raise KeyError, "%s is not a file in the archive" % key -def load(file, memmap=False): +def load(file, mmap_mode=None): """ Load a pickled, ``.npy``, or ``.npz`` binary file. @@ -87,10 +87,15 @@ def load(file, memmap=False): ---------- file : file-like object or string The file to read. It must support ``seek()`` and ``read()`` methods. - memmap : bool - If True, then memory-map the ``.npy`` file (or unzip the ``.npz`` file - into a temporary directory and memory-map each component). This has - no effect for a pickled file. + mmap_mode: {None, 'r+', 'r', 'w+', 'c'}, optional + If not None, then memory-map the file, using the given mode + (see `numpy.memmap`). The mode has no effect for pickled or + zipped files. + A memory-mapped array is stored on disk, and not directly loaded + into memory. However, it can be accessed and sliced like any + ndarray. Memory mapping is especially useful for accessing + small fragments of large files without reading the entire file + into memory. Returns ------- @@ -104,18 +109,28 @@ def load(file, memmap=False): Notes ----- - - If file contains pickle data, then whatever is stored in the + - If the file contains pickle data, then whatever is stored in the pickle is returned. - If the file is a ``.npy`` file, then an array is returned. - If the file is a ``.npz`` file, then a dictionary-like object is - returned, containing {filename: array} key-value pairs, one for - every file in the archive. + returned, containing ``{filename: array}`` key-value pairs, one for + each file in the archive. Examples -------- - >>> np.save('/tmp/123', np.array([1, 2, 3]) + Store data to disk, and load it again: + + >>> np.save('/tmp/123', np.array([[1, 2, 3], [4, 5, 6]])) >>> np.load('/tmp/123.npy') - array([1, 2, 3]) + array([[1, 2, 3], + [4, 5, 6]]) + + Mem-map the stored array, and then access the second row + directly from disk: + + >>> X = np.load('/tmp/123.npy', mmap_mode='r') + >>> X[1, :] + memmap([4, 5, 6]) """ if isinstance(file, basestring): @@ -123,9 +138,6 @@ def load(file, memmap=False): else: fid = file - if memmap: - raise NotImplementedError - # Code to distinguish from NumPy binary files and pickles. _ZIP_PREFIX = 'PK\x03\x04' N = len(format.MAGIC_PREFIX) @@ -134,7 +146,10 @@ def load(file, memmap=False): if magic.startswith(_ZIP_PREFIX): # zip-file (assume .npz) return NpzFile(fid) elif magic == format.MAGIC_PREFIX: # .npy file - return format.read_array(fid) + if mmap_mode: + return format.open_memmap(file, mode=mmap_mode) + else: + return format.read_array(fid) else: # Try a pickle try: return _cload(fid) @@ -264,8 +279,8 @@ def loadtxt(fname, dtype=float, comments='#', delimiter=None, converters=None, Parameters ---------- fname : file or string - File or filename to read. If the filename extension is ``.gz``, - the file is first decompressed. + File or filename to read. If the filename extension is ``.gz`` or + ``.bz2``, the file is first decompressed. dtype : data-type Data type of the resulting array. If this is a record data-type, the resulting array will be 1-dimensional, and each row will be @@ -331,9 +346,12 @@ def loadtxt(fname, dtype=float, comments='#', delimiter=None, converters=None, if fname.endswith('.gz'): import gzip fh = gzip.open(fname) + elif fname.endswith('.bz2'): + import bz2 + fh = bz2.BZ2File(fname) else: fh = file(fname) - elif hasattr(fname, 'seek'): + elif hasattr(fname, 'readline'): fh = fname else: raise ValueError('fname must be a string or file handle') diff --git a/numpy/lib/polynomial.py b/numpy/lib/polynomial.py index 4a70c9fa3..504a1271f 100644 --- a/numpy/lib/polynomial.py +++ b/numpy/lib/polynomial.py @@ -15,36 +15,13 @@ from numpy.lib.getlimits import finfo from numpy.lib.twodim_base import diag, vander from numpy.lib.shape_base import hstack, atleast_1d from numpy.lib.function_base import trim_zeros, sort_complex -eigvals = None -lstsq = None +from numpy.linalg import eigvals, lstsq class RankWarning(UserWarning): """Issued by polyfit when Vandermonde matrix is rank deficient. """ pass -def get_linalg_funcs(): - "Look for linear algebra functions in numpy" - global eigvals, lstsq - from numpy.dual import eigvals, lstsq - return - -def _eigvals(arg): - "Return the eigenvalues of the argument" - try: - return eigvals(arg) - except TypeError: - get_linalg_funcs() - return eigvals(arg) - -def _lstsq(X, y, rcond): - "Do least squares on the arguments" - try: - return lstsq(X, y, rcond) - except TypeError: - get_linalg_funcs() - return lstsq(X, y, rcond) - def poly(seq_of_zeros): """ Return polynomial coefficients given a sequence of roots. @@ -94,7 +71,7 @@ def poly(seq_of_zeros): seq_of_zeros = atleast_1d(seq_of_zeros) sh = seq_of_zeros.shape if len(sh) == 2 and sh[0] == sh[1]: - seq_of_zeros = _eigvals(seq_of_zeros) + seq_of_zeros = eigvals(seq_of_zeros) elif len(sh) ==1: pass else: @@ -177,7 +154,7 @@ def roots(p): # build companion matrix and find its eigenvalues (the roots) A = diag(NX.ones((N-2,), p.dtype), -1) A[0, :] = -p[1:] / p[0] - roots = _eigvals(A) + roots = eigvals(A) else: roots = NX.array([]) @@ -500,7 +477,7 @@ def polyfit(x, y, deg, rcond=None, full=False): # solve least squares equation for powers of x v = vander(x, order) - c, resids, rank, s = _lstsq(v, y, rcond) + c, resids, rank, s = lstsq(v, y, rcond) # warn on rank reduction, which indicates an ill conditioned matrix if rank != order and not full: diff --git a/numpy/lib/tests/test_io.py b/numpy/lib/tests/test_io.py index e78fd0579..885fd3616 100644 --- a/numpy/lib/tests/test_io.py +++ b/numpy/lib/tests/test_io.py @@ -2,55 +2,90 @@ from numpy.testing import * import numpy as np import StringIO +from tempfile import NamedTemporaryFile class RoundtripTest: + def roundtrip(self, save_func, *args, **kwargs): + """ + save_func : callable + Function used to save arrays to file. + file_on_disk : bool + If true, store the file on disk, instead of in a + string buffer. + save_kwds : dict + Parameters passed to `save_func`. + load_kwds : dict + Parameters passed to `numpy.load`. + args : tuple of arrays + Arrays stored to file. + + """ + save_kwds = kwargs.get('save_kwds', {}) + load_kwds = kwargs.get('load_kwds', {}) + file_on_disk = kwargs.get('file_on_disk', False) + + if file_on_disk: + target_file = NamedTemporaryFile() + load_file = target_file.name + else: + target_file = StringIO.StringIO() + load_file = target_file + + arr = args + + save_func(target_file, *arr, **save_kwds) + target_file.flush() + target_file.seek(0) + + arr_reloaded = np.load(load_file, **load_kwds) + + self.arr = arr + self.arr_reloaded = arr_reloaded + def test_array(self): - a = np.array( [[1,2],[3,4]], float) - self.do(a) + a = np.array([[1, 2], [3, 4]], float) + self.roundtrip(a) - a = np.array( [[1,2],[3,4]], int) - self.do(a) + a = np.array([[1, 2], [3, 4]], int) + self.roundtrip(a) - a = np.array( [[1+5j,2+6j],[3+7j,4+8j]], dtype=np.csingle) - self.do(a) + a = np.array([[1 + 5j, 2 + 6j], [3 + 7j, 4 + 8j]], dtype=np.csingle) + self.roundtrip(a) - a = np.array( [[1+5j,2+6j],[3+7j,4+8j]], dtype=np.cdouble) - self.do(a) + a = np.array([[1 + 5j, 2 + 6j], [3 + 7j, 4 + 8j]], dtype=np.cdouble) + self.roundtrip(a) def test_1D(self): - a = np.array([1,2,3,4], int) - self.do(a) + a = np.array([1, 2, 3, 4], int) + self.roundtrip(a) + + def test_mmap(self): + a = np.array([[1, 2.5], [4, 7.3]]) + self.roundtrip(a, file_on_disk=True, load_kwds={'mmap_mode': 'r'}) def test_record(self): a = np.array([(1, 2), (3, 4)], dtype=[('x', 'i4'), ('y', 'i4')]) - self.do(a) + self.roundtrip(a) class TestSaveLoad(RoundtripTest, TestCase): - def do(self, a): - c = StringIO.StringIO() - np.save(c, a) - c.seek(0) - a_reloaded = np.load(c) - assert_equal(a, a_reloaded) - + def roundtrip(self, *args, **kwargs): + RoundtripTest.roundtrip(self, np.save, *args, **kwargs) + assert_equal(self.arr[0], self.arr_reloaded) class TestSavezLoad(RoundtripTest, TestCase): - def do(self, *arrays): - c = StringIO.StringIO() - np.savez(c, *arrays) - c.seek(0) - l = np.load(c) - for n, a in enumerate(arrays): - assert_equal(a, l['arr_%d' % n]) + def roundtrip(self, *args, **kwargs): + RoundtripTest.roundtrip(self, np.savez, *args, **kwargs) + for n, arr in enumerate(self.arr): + assert_equal(arr, self.arr_reloaded['arr_%d' % n]) def test_multiple_arrays(self): - a = np.array( [[1,2],[3,4]], float) - b = np.array( [[1+2j,2+7j],[3-6j,4+12j]], complex) - self.do(a,b) + a = np.array([[1, 2], [3, 4]], float) + b = np.array([[1 + 2j, 2 + 7j], [3 - 6j, 4 + 12j]], complex) + self.roundtrip(a,b) def test_named_arrays(self): - a = np.array( [[1,2],[3,4]], float) - b = np.array( [[1+2j,2+7j],[3-6j,4+12j]], complex) + a = np.array([[1, 2], [3, 4]], float) + b = np.array([[1 + 2j, 2 + 7j], [3 - 6j, 4 + 12j]], complex) c = StringIO.StringIO() np.savez(c, file_a=a, file_b=b) c.seek(0) @@ -61,7 +96,7 @@ class TestSavezLoad(RoundtripTest, TestCase): class TestSaveTxt(TestCase): def test_array(self): - a =np.array( [[1,2],[3,4]], float) + a =np.array([[1, 2], [3, 4]], float) c = StringIO.StringIO() np.savetxt(c, a) c.seek(0) @@ -69,14 +104,14 @@ class TestSaveTxt(TestCase): ['1.000000000000000000e+00 2.000000000000000000e+00\n', '3.000000000000000000e+00 4.000000000000000000e+00\n']) - a =np.array( [[1,2],[3,4]], int) + a =np.array([[1, 2], [3, 4]], int) c = StringIO.StringIO() np.savetxt(c, a, fmt='%d') c.seek(0) assert_equal(c.readlines(), ['1 2\n', '3 4\n']) def test_1D(self): - a = np.array([1,2,3,4], int) + a = np.array([1, 2, 3, 4], int) c = StringIO.StringIO() np.savetxt(c, a, fmt='%d') c.seek(0) @@ -146,12 +181,12 @@ class TestLoadTxt(TestCase): c.seek(0) x = np.loadtxt(c, dtype=int) - a = np.array([[1,2],[3,4]], int) + a = np.array([[1, 2], [3, 4]], int) assert_array_equal(x, a) c.seek(0) x = np.loadtxt(c, dtype=float) - a = np.array([[1,2],[3,4]], float) + a = np.array([[1, 2], [3, 4]], float) assert_array_equal(x, a) def test_1D(self): @@ -159,14 +194,14 @@ class TestLoadTxt(TestCase): c.write('1\n2\n3\n4\n') c.seek(0) x = np.loadtxt(c, dtype=int) - a = np.array([1,2,3,4], int) + a = np.array([1, 2, 3, 4], int) assert_array_equal(x, a) c = StringIO.StringIO() c.write('1,2,3,4\n') c.seek(0) x = np.loadtxt(c, dtype=int, delimiter=',') - a = np.array([1,2,3,4], int) + a = np.array([1, 2, 3, 4], int) assert_array_equal(x, a) def test_missing(self): @@ -175,7 +210,7 @@ class TestLoadTxt(TestCase): c.seek(0) x = np.loadtxt(c, dtype=int, delimiter=',', \ converters={3:lambda s: int(s or -999)}) - a = np.array([1,2,3,-999,5], int) + a = np.array([1, 2, 3, -999, 5], int) assert_array_equal(x, a) def test_converters_with_usecols(self): @@ -184,8 +219,8 @@ class TestLoadTxt(TestCase): c.seek(0) x = np.loadtxt(c, dtype=int, delimiter=',', \ converters={3:lambda s: int(s or -999)}, \ - usecols=(1, 3, )) - a = np.array([[2, -999],[7, 9]], int) + usecols=(1, 3,)) + a = np.array([[2, -999], [7, 9]], int) assert_array_equal(x, a) def test_comments(self): @@ -194,7 +229,7 @@ class TestLoadTxt(TestCase): c.seek(0) x = np.loadtxt(c, dtype=int, delimiter=',', \ comments='#') - a = np.array([1,2,3,5], int) + a = np.array([1, 2, 3, 5], int) assert_array_equal(x, a) def test_skiprows(self): @@ -203,7 +238,7 @@ class TestLoadTxt(TestCase): c.seek(0) x = np.loadtxt(c, dtype=int, delimiter=',', \ skiprows=1) - a = np.array([1,2,3,5], int) + a = np.array([1, 2, 3, 5], int) assert_array_equal(x, a) c = StringIO.StringIO() @@ -211,28 +246,28 @@ class TestLoadTxt(TestCase): c.seek(0) x = np.loadtxt(c, dtype=int, delimiter=',', \ skiprows=1) - a = np.array([1,2,3,5], int) + a = np.array([1, 2, 3, 5], int) assert_array_equal(x, a) def test_usecols(self): - a =np.array( [[1,2],[3,4]], float) + a = np.array([[1, 2], [3, 4]], float) c = StringIO.StringIO() np.savetxt(c, a) c.seek(0) x = np.loadtxt(c, dtype=float, usecols=(1,)) assert_array_equal(x, a[:,1]) - a =np.array( [[1,2,3],[3,4,5]], float) + a =np.array([[1, 2, 3], [3, 4, 5]], float) c = StringIO.StringIO() np.savetxt(c, a) c.seek(0) - x = np.loadtxt(c, dtype=float, usecols=(1,2)) - assert_array_equal(x, a[:,1:]) + x = np.loadtxt(c, dtype=float, usecols=(1, 2)) + assert_array_equal(x, a[:, 1:]) # Testing with arrays instead of tuples. c.seek(0) - x = np.loadtxt(c, dtype=float, usecols=np.array([1,2])) - assert_array_equal(x, a[:,1:]) + x = np.loadtxt(c, dtype=float, usecols=np.array([1, 2])) + assert_array_equal(x, a[:, 1:]) # Checking with dtypes defined converters. data = '''JOE 70.1 25.3 @@ -241,9 +276,9 @@ class TestLoadTxt(TestCase): c = StringIO.StringIO(data) names = ['stid', 'temp'] dtypes = ['S4', 'f8'] - arr = np.loadtxt(c, usecols=(0,2),dtype=zip(names,dtypes)) - assert_equal(arr['stid'], ["JOE", "BOB"]) - assert_equal(arr['temp'], [25.3, 27.9]) + arr = np.loadtxt(c, usecols=(0, 2), dtype=zip(names, dtypes)) + assert_equal(arr['stid'], ["JOE", "BOB"]) + assert_equal(arr['temp'], [25.3, 27.9]) def test_fancy_dtype(self): c = StringIO.StringIO() @@ -251,7 +286,7 @@ class TestLoadTxt(TestCase): c.seek(0) dt = np.dtype([('x', int), ('y', [('t', int), ('s', float)])]) x = np.loadtxt(c, dtype=dt, delimiter=',') - a = np.array([(1,(2,3.0)),(4,(5,6.0))], dt) + a = np.array([(1, (2, 3.0)), (4, (5, 6.0))], dt) assert_array_equal(x, a) def test_empty_file(self): @@ -262,11 +297,13 @@ class TestLoadTxt(TestCase): c = StringIO.StringIO() c.writelines(['1 21\n', '3 42\n']) c.seek(0) - data = np.loadtxt(c, usecols=(1,), converters={0: lambda s: int(s, 16)}) + data = np.loadtxt(c, usecols=(1,), + converters={0: lambda s: int(s, 16)}) assert_array_equal(data, [21, 42]) c.seek(0) - data = np.loadtxt(c, usecols=(1,), converters={1: lambda s: int(s, 16)}) + data = np.loadtxt(c, usecols=(1,), + converters={1: lambda s: int(s, 16)}) assert_array_equal(data, [33, 66]) class Testfromregex(TestCase): @@ -277,7 +314,8 @@ class Testfromregex(TestCase): dt = [('num', np.float64), ('val', 'S3')] x = np.fromregex(c, r"([0-9.]+)\s+(...)", dt) - a = np.array([(1.312, 'foo'), (1.534, 'bar'), (4.444, 'qux')], dtype=dt) + a = np.array([(1.312, 'foo'), (1.534, 'bar'), (4.444, 'qux')], + dtype=dt) assert_array_equal(x, a) def test_record_2(self): @@ -288,7 +326,8 @@ class Testfromregex(TestCase): dt = [('num', np.int32), ('val', 'S3')] x = np.fromregex(c, r"(\d+)\s+(...)", dt) - a = np.array([(1312, 'foo'), (1534, 'bar'), (4444, 'qux')], dtype=dt) + a = np.array([(1312, 'foo'), (1534, 'bar'), (4444, 'qux')], + dtype=dt) assert_array_equal(x, a) def test_record_3(self): diff --git a/numpy/ma/core.py b/numpy/ma/core.py index 7bf71ca13..6b4dc98e6 100644 --- a/numpy/ma/core.py +++ b/numpy/ma/core.py @@ -19,7 +19,7 @@ Improvements suggested by Reggie Dugard (reggie_AT_merfinllc_DOT_com) __author__ = "Pierre GF Gerard-Marchant" __docformat__ = "restructuredtext en" -__all__ = ['MAError', 'MaskType', 'MaskedArray', +__all__ = ['MAError', 'MaskError', 'MaskType', 'MaskedArray', 'bool_', 'abs', 'absolute', 'add', 'all', 'allclose', 'allequal', 'alltrue', 'amax', 'amin', 'anom', 'anomalies', 'any', 'arange', @@ -28,13 +28,14 @@ __all__ = ['MAError', 'MaskType', 'MaskedArray', 'array', 'asarray','asanyarray', 'bitwise_and', 'bitwise_or', 'bitwise_xor', 'ceil', 'choose', 'clip', 'common_fill_value', 'compress', - 'compressed', 'concatenate', 'conjugate', 'cos', 'cosh', 'count', - 'default_fill_value', 'diagonal', 'divide', 'dump', 'dumps', - 'empty', 'empty_like', 'equal', 'exp', - 'fabs', 'fmod', 'filled', 'floor', 'floor_divide','fix_invalid', - 'frombuffer', 'fromfunction', + 'compressed', 'concatenate', 'conjugate', 'copy', 'cos', 'cosh', + 'count', 'cumprod', 'cumsum', + 'default_fill_value', 'diag', 'diagonal', 'divide', 'dump', 'dumps', + 'empty', 'empty_like', 'equal', 'exp', 'expand_dims', + 'fabs', 'flatten_mask', 'fmod', 'filled', 'floor', 'floor_divide', + 'fix_invalid', 'frombuffer', 'fromfunction', 'getdata','getmask', 'getmaskarray', 'greater', 'greater_equal', - 'hypot', + 'harden_mask', 'hypot', 'identity', 'ids', 'indices', 'inner', 'innerproduct', 'isMA', 'isMaskedArray', 'is_mask', 'is_masked', 'isarray', 'left_shift', 'less', 'less_equal', 'load', 'loads', 'log', 'log10', @@ -49,12 +50,13 @@ __all__ = ['MAError', 'MaskType', 'MaskedArray', 'mod', 'multiply', 'negative', 'nomask', 'nonzero', 'not_equal', 'ones', 'outer', 'outerproduct', - 'power', 'product', 'ptp', 'put', 'putmask', + 'power', 'prod', 'product', 'ptp', 'put', 'putmask', 'rank', 'ravel', 'remainder', 'repeat', 'reshape', 'resize', - 'right_shift', 'round_', - 'set_fill_value', 'shape', 'sin', 'sinh', 'size', 'sometrue', 'sort', - 'sqrt', 'std', 'subtract', 'sum', 'swapaxes', - 'take', 'tan', 'tanh', 'transpose', 'true_divide', + 'right_shift', 'round_', 'round', + 'set_fill_value', 'shape', 'sin', 'sinh', 'size', 'sometrue', + 'sort', 'soften_mask', 'sqrt', 'squeeze', 'std', 'subtract', 'sum', + 'swapaxes', + 'take', 'tan', 'tanh', 'trace', 'transpose', 'true_divide', 'var', 'where', 'zeros'] @@ -95,12 +97,31 @@ def doc_note(initialdoc, note): """ return newdoc % (initialdoc, note) +def get_object_signature(obj): + """ + Get the signature from obj + """ + import inspect + try: + sig = inspect.formatargspec(*inspect.getargspec(obj)) + except TypeError, errmsg: + msg = "Unable to retrieve the signature of %s '%s'\n"\ + "(Initial error message: %s)" +# warnings.warn(msg % (type(obj), +# getattr(obj, '__name__', '???'), +# errmsg)) + sig = '' + return sig + #####-------------------------------------------------------------------------- #---- --- Exceptions --- #####-------------------------------------------------------------------------- class MAError(Exception): "Class for MA related errors." pass +class MaskError(MAError): + "Class for mask related errors." + pass #####-------------------------------------------------------------------------- @@ -514,17 +535,20 @@ class _MaskedUnaryOperation: # ... but np.putmask looks more efficient, despite the copy. np.putmask(d1, dm, self.fill) # Take care of the masked singletong first ... - if not m.ndim and m: + if (not m.ndim) and m: return masked - # Get the result class ....................... - if isinstance(a, MaskedArray): - subtype = type(a) + elif m is nomask: + result = self.f(d1, *args, **kwargs) else: - subtype = MaskedArray - # Get the result as a view of the subtype ... - result = self.f(d1, *args, **kwargs).view(subtype) - # Fix the mask if we don't have a scalar - if result.ndim > 0: + result = np.where(m, d1, self.f(d1, *args, **kwargs)) + # If result is not a scalar + if result.ndim: + # Get the result subclass: + if isinstance(a, MaskedArray): + subtype = type(a) + else: + subtype = MaskedArray + result = result.view(subtype) result._mask = m result._update_from(a) return result @@ -563,19 +587,45 @@ class _MaskedBinaryOperation: def __call__ (self, a, b, *args, **kwargs): "Execute the call behavior." m = mask_or(getmask(a), getmask(b)) - (d1, d2) = (get_data(a), get_data(b)) - result = self.f(d1, d2, *args, **kwargs).view(get_masked_subclass(a, b)) - if len(result.shape): - if m is not nomask: - result._mask = make_mask_none(result.shape) - result._mask.flat = m + (da, db) = (getdata(a), getdata(b)) + # Easy case: there's no mask... + if m is nomask: + result = self.f(da, db, *args, **kwargs) + # There are some masked elements: run only on the unmasked + else: + result = np.where(m, da, self.f(da, db, *args, **kwargs)) + # Transforms to a (subclass of) MaskedArray if we don't have a scalar + if result.shape: + result = result.view(get_masked_subclass(a, b)) + result._mask = make_mask_none(result.shape) + result._mask.flat = m if isinstance(a, MaskedArray): result._update_from(a) if isinstance(b, MaskedArray): result._update_from(b) + # ... or return masked if we have a scalar and the common mask is True elif m: return masked return result +# +# result = self.f(d1, d2, *args, **kwargs).view(get_masked_subclass(a, b)) +# if len(result.shape): +# if m is not nomask: +# result._mask = make_mask_none(result.shape) +# result._mask.flat = m +# #!!!!! +# # Force m to be at least 1D +# m.shape = m.shape or (1,) +# print "Resetting data" +# result.data[m].flat = d1.flat +# #!!!!! +# if isinstance(a, MaskedArray): +# result._update_from(a) +# if isinstance(b, MaskedArray): +# result._update_from(b) +# elif m: +# return masked +# return result def reduce(self, target, axis=0, dtype=None): """Reduce `target` along the given `axis`.""" @@ -618,11 +668,13 @@ class _MaskedBinaryOperation: m = umath.logical_or.outer(ma, mb) if (not m.ndim) and m: return masked - rcls = get_masked_subclass(a, b) - # We could fill the arguments first, butis it useful ? - # d = self.f.outer(filled(a, self.fillx), filled(b, self.filly)).view(rcls) - d = self.f.outer(getdata(a), getdata(b)).view(rcls) - if d.ndim > 0: + (da, db) = (getdata(a), getdata(b)) + if m is nomask: + d = self.f.outer(da, db) + else: + d = np.where(m, da, self.f.outer(da, db)) + if d.shape: + d = d.view(get_masked_subclass(a, b)) d._mask = m return d @@ -634,7 +686,7 @@ class _MaskedBinaryOperation: if isinstance(target, MaskedArray): tclass = type(target) else: - tclass = masked_array + tclass = MaskedArray t = filled(target, self.filly) return self.f.accumulate(t, axis).view(tclass) @@ -643,7 +695,8 @@ class _MaskedBinaryOperation: #.............................................................................. class _DomainedBinaryOperation: - """Define binary operations that have a domain, like divide. + """ + Define binary operations that have a domain, like divide. They have no reduce, outer or accumulate. @@ -668,25 +721,29 @@ class _DomainedBinaryOperation: ufunc_domain[dbfunc] = domain ufunc_fills[dbfunc] = (fillx, filly) - def __call__(self, a, b): + def __call__(self, a, b, *args, **kwargs): "Execute the call behavior." ma = getmask(a) mb = getmask(b) - d1 = getdata(a) - d2 = get_data(b) - t = narray(self.domain(d1, d2), copy=False) + da = getdata(a) + db = getdata(b) + t = narray(self.domain(da, db), copy=False) if t.any(None): mb = mask_or(mb, t) # The following line controls the domain filling - if t.size == d2.size: - d2 = np.where(t, self.filly, d2) + if t.size == db.size: + db = np.where(t, self.filly, db) else: - d2 = np.where(np.resize(t, d2.shape), self.filly, d2) + db = np.where(np.resize(t, db.shape), self.filly, db) m = mask_or(ma, mb) if (not m.ndim) and m: return masked - result = self.f(d1, d2).view(get_masked_subclass(a, b)) - if result.ndim > 0: + elif (m is nomask): + result = self.f(da, db, *args, **kwargs) + else: + result = np.where(m, da, self.f(da, db, *args, **kwargs)) + if result.shape: + result = result.view(get_masked_subclass(a, b)) result._mask = m if isinstance(a, MaskedArray): result._update_from(a) @@ -780,22 +837,29 @@ def make_mask_descr(ndtype): Each field is set to a bool. """ + def _make_descr(datatype): + "Private function allowing recursion." + # Do we have some name fields ? + if datatype.names: + descr = [] + for name in datatype.names: + field = datatype.fields[name] + if len(field) == 3: + # Prepend the title to the name + name = (field[-1], name) + descr.append((name, _make_descr(field[0]))) + return descr + # Is this some kind of composite a la (np.float,2) + elif datatype.subdtype: + mdescr = list(datatype.subdtype) + mdescr[0] = np.dtype(bool) + return tuple(mdescr) + else: + return np.bool # Make sure we do have a dtype if not isinstance(ndtype, np.dtype): ndtype = np.dtype(ndtype) - # Do we have some name fields ? - if ndtype.names: - mdescr = [list(_) for _ in ndtype.descr] - for m in mdescr: - m[1] = '|b1' - return np.dtype([tuple(_) for _ in mdescr]) - # Is this some kind of composite a la (np.float,2) - elif ndtype.subdtype: - mdescr = list(ndtype.subdtype) - mdescr[0] = np.dtype(bool) - return np.dtype(tuple(mdescr)) - else: - return MaskType + return np.dtype(_make_descr(ndtype)) def get_mask(a): """Return the mask of a, if any, or nomask. @@ -944,6 +1008,61 @@ def mask_or (m1, m2, copy=False, shrink=True): return make_mask(umath.logical_or(m1, m2), copy=copy, shrink=shrink) +def flatten_mask(mask): + """ + Returns a completely flattened version of the mask, where nested fields + are collapsed. + + Parameters + ---------- + mask : array_like + Array of booleans + + Returns + ------- + flattened_mask : ndarray + Boolean array. + + Examples + -------- + >>> mask = np.array([0, 0, 1], dtype=np.bool) + >>> flatten_mask(mask) + array([False, False, True], dtype=bool) + >>> mask = np.array([(0, 0), (0, 1)], dtype=[('a', bool), ('b', bool)]) + >>> flatten_mask(mask) + array([False, False, False, True], dtype=bool) + >>> mdtype = [('a', bool), ('b', [('ba', bool), ('bb', bool)])] + >>> mask = np.array([(0, (0, 0)), (0, (0, 1))], dtype=mdtype) + >>> flatten_mask(mask) + array([False, False, False, False, False, True], dtype=bool) + + """ + # + def _flatmask(mask): + "Flatten the mask and returns a (maybe nested) sequence of booleans." + mnames = mask.dtype.names + if mnames: + return [flatten_mask(mask[name]) for name in mnames] + else: + return mask + # + def _flatsequence(sequence): + "Generates a flattened version of the sequence." + try: + for element in sequence: + if hasattr(element, '__iter__'): + for f in _flatsequence(element): + yield f + else: + yield element + except TypeError: + yield sequence + # + mask = np.asarray(mask) + flattened = _flatsequence(_flatmask(mask)) + return np.array([_ for _ in flattened], dtype=bool) + + #####-------------------------------------------------------------------------- #--- --- Masking functions --- #####-------------------------------------------------------------------------- @@ -1208,8 +1327,8 @@ class _arraymethod(object): # def getdoc(self): "Return the doc of the function (from the doc of the method)." - methdoc = getattr(ndarray, self.__name__, None) - methdoc = getattr(np, self.__name__, methdoc) + methdoc = getattr(ndarray, self.__name__, None) or \ + getattr(np, self.__name__, None) if methdoc is not None: return methdoc.__doc__ # @@ -1327,7 +1446,7 @@ class MaskedArray(ndarray): # Process data............ _data = np.array(data, dtype=dtype, copy=copy, subok=True, ndmin=ndmin) _baseclass = getattr(data, '_baseclass', type(_data)) - # Check that we'ew not erasing the mask.......... + # Check that we're not erasing the mask.......... if isinstance(data, MaskedArray) and (data.shape != _data.shape): copy = True # Careful, cls might not always be MaskedArray... @@ -1359,7 +1478,13 @@ class MaskedArray(ndarray): _data._mask = np.zeros(_data.shape, dtype=mdtype) # Check whether we missed something elif isinstance(data, (tuple,list)): - mask = np.array([getmaskarray(m) for m in data], dtype=mdtype) + try: + # If data is a sequence of masked array + mask = np.array([getmaskarray(m) for m in data], + dtype=mdtype) + except ValueError: + # If data is nested + mask = nomask # Force shrinking of the mask if needed (and possible) if (mdtype == MaskType) and mask.any(): _data._mask = mask @@ -1392,7 +1517,7 @@ class MaskedArray(ndarray): else: msg = "Mask and data not compatible: data size is %i, "+\ "mask size is %i." - raise MAError, msg % (nd, nm) + raise MaskError, msg % (nd, nm) copy = True # Set the mask to the new value if _data._mask is nomask: @@ -1404,8 +1529,16 @@ class MaskedArray(ndarray): _data._sharedmask = not copy else: if names_: - for n in names_: - _data._mask[n] |= mask[n] + def _recursive_or(a, b): + "do a|=b on each field of a, recursively" + for name in a.dtype.names: + (af, bf) = (a[name], b[name]) + if af.dtype.names: + _recursive_or(af, bf) + else: + af |= bf + return + _recursive_or(_data._mask, mask) else: _data._mask = np.logical_or(mask, _data._mask) _data._sharedmask = False @@ -1601,7 +1734,7 @@ class MaskedArray(ndarray): # A record ................ if isinstance(dout, np.void): mask = _mask[indx] - if mask.view((bool, len(mask.dtype))).any(): + if flatten_mask(mask).any(): dout = masked_array(dout, mask=mask) else: return dout @@ -1633,7 +1766,7 @@ class MaskedArray(ndarray): """ if self is masked: - raise MAError, 'Cannot alter the masked element.' + raise MaskError, 'Cannot alter the masked element.' # This test is useful, but we should keep things light... # if getmask(indx) is not nomask: # msg = "Masked arrays must be filled before they can be used as indices!" @@ -2146,32 +2279,33 @@ masked_%(name)s(data = %(data)s, #............................................ def __iadd__(self, other): "Add other to self in-place." - ndarray.__iadd__(self._data, getdata(other)) m = getmask(other) if self._mask is nomask: self._mask = m - elif m is not nomask: - self._mask += m + else: + if m is not nomask: + self._mask += m + ndarray.__iadd__(self._data, np.where(self._mask, 0, getdata(other))) return self #.... def __isub__(self, other): "Subtract other from self in-place." - ndarray.__isub__(self._data, getdata(other)) m = getmask(other) if self._mask is nomask: self._mask = m elif m is not nomask: self._mask += m + ndarray.__isub__(self._data, np.where(self._mask, 0, getdata(other))) return self #.... def __imul__(self, other): "Multiply self by other in-place." - ndarray.__imul__(self._data, getdata(other)) m = getmask(other) if self._mask is nomask: self._mask = m elif m is not nomask: self._mask += m + ndarray.__imul__(self._data, np.where(self._mask, 1, getdata(other))) return self #.... def __idiv__(self, other): @@ -2184,21 +2318,25 @@ masked_%(name)s(data = %(data)s, if dom_mask.any(): (_, fval) = ufunc_fills[np.divide] other_data = np.where(dom_mask, fval, other_data) - ndarray.__idiv__(self._data, other_data) - self._mask = mask_or(self._mask, new_mask) +# self._mask = mask_or(self._mask, new_mask) + self._mask |= new_mask + ndarray.__idiv__(self._data, np.where(self._mask, 1, other_data)) return self #... def __ipow__(self, other): "Raise self to the power other, in place" - _data = self._data other_data = getdata(other) other_mask = getmask(other) - ndarray.__ipow__(_data, other_data) - invalid = np.logical_not(np.isfinite(_data)) + ndarray.__ipow__(self._data, np.where(self._mask, 1, other_data)) + invalid = np.logical_not(np.isfinite(self._data)) + if invalid.any(): + if self._mask is not nomask: + self._mask |= invalid + else: + self._mask = invalid + np.putmask(self._data, invalid, self.fill_value) new_mask = mask_or(other_mask, invalid) self._mask = mask_or(self._mask, new_mask) - # The following line is potentially problematic, as we change _data... - np.putmask(self._data, invalid, self.fill_value) return self #............................................ def __float__(self): @@ -2217,7 +2355,7 @@ masked_%(name)s(data = %(data)s, raise TypeError("Only length-1 arrays can be converted "\ "to Python scalars") elif self._mask: - raise MAError, 'Cannot convert masked element to a Python int.' + raise MaskError, 'Cannot convert masked element to a Python int.' return int(self.item()) #............................................ def get_imag(self): @@ -2560,9 +2698,7 @@ masked_%(name)s(data = %(data)s, def trace(self, offset=0, axis1=0, axis2=1, dtype=None, out=None): """ - Return the sum along the offset diagonal of the array's - indicated `axis1` and `axis2`. - + (this docstring should be overwritten) """ #!!!: implement out + test! m = self._mask @@ -2573,7 +2709,7 @@ masked_%(name)s(data = %(data)s, else: D = self.diagonal(offset=offset, axis1=axis1, axis2=axis2) return D.astype(dtype).filled(0).sum(axis=None, out=out) - + trace.__doc__ = ndarray.trace.__doc__ def sum(self, axis=None, dtype=None, out=None): """ @@ -2668,8 +2804,8 @@ masked_%(name)s(data = %(data)s, have the same shape and buffer length as the expected output but the type will be cast if necessary. - Warning - ------- + Warnings + -------- The mask is lost if out is not a valid :class:`MaskedArray` ! Returns @@ -2678,8 +2814,8 @@ masked_%(name)s(data = %(data)s, A new array holding the result is returned unless ``out`` is specified, in which case a reference to ``out`` is returned. - Example - ------- + Examples + -------- >>> marr = np.ma.array(np.arange(10), mask=[0,0,0,1,1,1,0,0,0,0]) >>> print marr.cumsum() [0 1 3 -- -- -- 9 16 24 33] @@ -2824,7 +2960,14 @@ masked_%(name)s(data = %(data)s, def mean(self, axis=None, dtype=None, out=None): - "" + """ + Returns the average of the array elements along given axis. + Refer to `numpy.mean` for full documentation. + + See Also + -------- + numpy.mean : equivalent function' + """ if self._mask is nomask: result = super(MaskedArray, self).mean(axis=axis, dtype=dtype) else: @@ -2840,7 +2983,6 @@ masked_%(name)s(data = %(data)s, outmask.flat = getattr(result, '_mask', nomask) return out return result - mean.__doc__ = ndarray.mean.__doc__ def anom(self, axis=None, dtype=None): """ @@ -2885,6 +3027,10 @@ masked_%(name)s(data = %(data)s, if out is not None: if isinstance(out, MaskedArray): out.__setmask__(True) + elif out.dtype.kind in 'biu': + errmsg = "Masked data information would be lost in one or "\ + "more location." + raise MaskError(errmsg) else: out.flat = np.nan return out @@ -2937,47 +3083,36 @@ masked_%(name)s(data = %(data)s, #............................................ def argsort(self, axis=None, fill_value=None, kind='quicksort', order=None): - """Return an ndarray of indices that sort the array along the - specified axis. Masked values are filled beforehand to - fill_value. + """ + Return an ndarray of indices that sort the array along the + specified axis. Masked values are filled beforehand to + fill_value. - Parameters - ---------- - axis : int, optional - Axis to be indirectly sorted. - If not given, uses a flatten version of the array. - fill_value : {var} - Value used to fill in the masked values. - If not given, self.fill_value is used instead. - kind : {string} - Sorting algorithm (default 'quicksort') - Possible values: 'quicksort', 'mergesort', or 'heapsort' + Parameters + ---------- + axis : int, optional + Axis along which to sort. If not given, the flattened array is used. + kind : {'quicksort', 'mergesort', 'heapsort'}, optional + Sorting algorithm. + order : list, optional + When `a` is an array with fields defined, this argument specifies + which fields to compare first, second, etc. Not all fields need be + specified. + Returns + ------- + index_array : ndarray, int + Array of indices that sort `a` along the specified axis. + In other words, ``a[index_array]`` yields a sorted `a`. + + See Also + -------- + sort : Describes sorting algorithms used. + lexsort : Indirect stable sort with multiple keys. + ndarray.sort : Inplace sort. - Notes - ----- - This method executes an indirect sort along the given axis - using the algorithm specified by the kind keyword. It returns - an array of indices of the same shape as 'a' that index data - along the given axis in sorted order. - - The various sorts are characterized by average speed, worst - case performance need for work space, and whether they are - stable. A stable sort keeps items with the same key in the - same relative order. The three available algorithms have the - following properties: - - |------------------------------------------------------| - | kind | speed | worst case | work space | stable| - |------------------------------------------------------| - |'quicksort'| 1 | O(n^2) | 0 | no | - |'mergesort'| 2 | O(n*log(n)) | ~n/2 | yes | - |'heapsort' | 3 | O(n*log(n)) | 0 | no | - |------------------------------------------------------| - - All the sort algorithms make temporary copies of the data when - the sort is not along the last axis. Consequently, sorts along - the last axis are faster and use less space than sorts along - other axis. + Notes + ----- + See `sort` for notes on the different sorting algorithms. """ if fill_value is None: @@ -3069,19 +3204,21 @@ masked_%(name)s(data = %(data)s, def sort(self, axis=-1, kind='quicksort', order=None, endwith=True, fill_value=None): """ - Sort along the given axis. + Return a sorted copy of an array. Parameters ---------- - axis : {int}, optional - Axis to be indirectly sorted. - kind : {'quicksort', 'mergesort', or 'heapsort'}, optional - Sorting algorithm (default 'quicksort') - Possible values: 'quicksort', 'mergesort', or 'heapsort'. - order : {None, var} - If a has fields defined, then the order keyword can be the field name - to sort on or a list (or tuple) of field names to indicate the order - that fields should be used to define the sort. + a : array_like + Array to be sorted. + axis : int or None, optional + Axis along which to sort. If None, the array is flattened before + sorting. The default is -1, which sorts along the last axis. + kind : {'quicksort', 'mergesort', 'heapsort'}, optional + Sorting algorithm. Default is 'quicksort'. + order : list, optional + When `a` is a structured array, this argument specifies which fields + to compare first, second, and so on. This list does not need to + include all of the fields. endwith : {True, False}, optional Whether missing values (if any) should be forced in the upper indices (at the end of the array) (True) or lower indices (at the beginning). @@ -3091,29 +3228,67 @@ masked_%(name)s(data = %(data)s, Returns ------- - - When used as method, returns None. - - When used as a function, returns an array. + sorted_array : ndarray + Array of the same type and shape as `a`. + + See Also + -------- + ndarray.sort : Method to sort an array in-place. + argsort : Indirect sort. + lexsort : Indirect stable sort on multiple keys. + searchsorted : Find elements in a sorted array. Notes ----- - This method sorts 'a' in place along the given axis using - the algorithm specified by the kind keyword. - - The various sorts may characterized by average speed, - worst case performance need for work space, and whether - they are stable. A stable sort keeps items with the same - key in the same relative order and is most useful when - used w/ argsort where the key might differ from the items - being sorted. The three available algorithms have the - following properties: - - |------------------------------------------------------| - | kind | speed | worst case | work space | stable| - |------------------------------------------------------| - |'quicksort'| 1 | O(n^2) | 0 | no | - |'mergesort'| 2 | O(n*log(n)) | ~n/2 | yes | - |'heapsort' | 3 | O(n*log(n)) | 0 | no | - |------------------------------------------------------| + The various sorting algorithms are characterized by their average speed, + worst case performance, work space size, and whether they are stable. A + stable sort keeps items with the same key in the same relative + order. The three available algorithms have the following + properties: + + =========== ======= ============= ============ ======= + kind speed worst case work space stable + =========== ======= ============= ============ ======= + 'quicksort' 1 O(n^2) 0 no + 'mergesort' 2 O(n*log(n)) ~n/2 yes + 'heapsort' 3 O(n*log(n)) 0 no + =========== ======= ============= ============ ======= + + All the sort algorithms make temporary copies of the data when + sorting along any but the last axis. Consequently, sorting along + the last axis is faster and uses less space than sorting along + any other axis. + + Examples + -------- + >>> a = np.array([[1,4],[3,1]]) + >>> np.sort(a) # sort along the last axis + array([[1, 4], + [1, 3]]) + >>> np.sort(a, axis=None) # sort the flattened array + array([1, 1, 3, 4]) + >>> np.sort(a, axis=0) # sort along the first axis + array([[1, 1], + [3, 4]]) + + Use the `order` keyword to specify a field to use when sorting a + structured array: + + >>> dtype = [('name', 'S10'), ('height', float), ('age', int)] + >>> values = [('Arthur', 1.8, 41), ('Lancelot', 1.9, 38), + ... ('Galahad', 1.7, 38)] + >>> a = np.array(values, dtype=dtype) # create a structured array + >>> np.sort(a, order='height') # doctest: +SKIP + array([('Galahad', 1.7, 38), ('Arthur', 1.8, 41), + ('Lancelot', 1.8999999999999999, 38)], + dtype=[('name', '|S10'), ('height', '<f8'), ('age', '<i4')]) + + Sort by age, then height if ages are equal: + + >>> np.sort(a, order=['age', 'height']) # doctest: +SKIP + array([('Galahad', 1.7, 38), ('Lancelot', 1.8999999999999999, 38), + ('Arthur', 1.8, 41)], + dtype=[('name', '|S10'), ('height', '<f8'), ('age', '<i4')]) """ if self._mask is nomask: @@ -3189,6 +3364,10 @@ masked_%(name)s(data = %(data)s, outmask = out._mask = make_mask_none(out.shape) outmask.flat = newmask else: + if out.dtype.kind in 'biu': + errmsg = "Masked data information would be lost in one or more"\ + " location." + raise MaskError(errmsg) np.putmask(out, newmask, np.nan) return out @@ -3251,6 +3430,11 @@ masked_%(name)s(data = %(data)s, outmask = out._mask = make_mask_none(out.shape) outmask.flat = newmask else: + + if out.dtype.kind in 'biu': + errmsg = "Masked data information would be lost in one or more"\ + " location." + raise MaskError(errmsg) np.putmask(out, newmask, np.nan) return out @@ -3633,12 +3817,16 @@ class _frommethod: def __init__(self, methodname): self.__name__ = methodname self.__doc__ = self.getdoc() + # def getdoc(self): "Return the doc of the function (from the doc of the method)." - try: - return getattr(MaskedArray, self.__name__).__doc__ - except: - return getattr(np, self.__name__).__doc__ + meth = getattr(MaskedArray, self.__name__, None) or\ + getattr(np, self.__name__, None) + signature = self.__name__ + get_object_signature(meth) + if meth is not None: + doc = """ %s\n%s""" % (signature, getattr(meth, '__doc__', None)) + return doc + # def __call__(self, a, *args, **params): if isinstance(a, MaskedArray): return getattr(a, self.__name__).__call__(*args, **params) @@ -3657,25 +3845,29 @@ class _frommethod: all = _frommethod('all') anomalies = anom = _frommethod('anom') any = _frommethod('any') -conjugate = _frommethod('conjugate') -ids = _frommethod('ids') -nonzero = _frommethod('nonzero') +compress = _frommethod('compress') +cumprod = _frommethod('cumprod') +cumsum = _frommethod('cumsum') +copy = _frommethod('copy') diagonal = _frommethod('diagonal') +harden_mask = _frommethod('harden_mask') +ids = _frommethod('ids') maximum = _maximum_operation() mean = _frommethod('mean') minimum = _minimum_operation () +nonzero = _frommethod('nonzero') +prod = _frommethod('prod') product = _frommethod('prod') -ptp = _frommethod('ptp') ravel = _frommethod('ravel') repeat = _frommethod('repeat') -round = _frommethod('round') +shrink_mask = _frommethod('shrink_mask') +soften_mask = _frommethod('soften_mask') std = _frommethod('std') sum = _frommethod('sum') swapaxes = _frommethod('swapaxes') take = _frommethod('take') trace = _frommethod('trace') var = _frommethod('var') -compress = _frommethod('compress') #.............................................................................. def power(a, b, third=None): @@ -3683,7 +3875,7 @@ def power(a, b, third=None): """ if third is not None: - raise MAError, "3-argument power not supported." + raise MaskError, "3-argument power not supported." # Get the masks ma = getmask(a) mb = getmask(b) @@ -3697,22 +3889,22 @@ def power(a, b, third=None): else: basetype = MaskedArray # Get the result and view it as a (subclass of) MaskedArray - result = umath.power(fa, fb).view(basetype) + result = np.where(m, fa, umath.power(fa, fb)).view(basetype) + result._update_from(a) # Find where we're in trouble w/ NaNs and Infs invalid = np.logical_not(np.isfinite(result.view(ndarray))) - # Retrieve some extra attributes if needed - if isinstance(result, MaskedArray): - result._update_from(a) # Add the initial mask if m is not nomask: - if np.isscalar(result): + if not (result.ndim): return masked + m |= invalid result._mask = m # Fix the invalid parts if invalid.any(): if not result.ndim: return masked - result[invalid] = masked + elif result._mask is nomask: + result._mask = invalid result._data[invalid] = result.fill_value return result @@ -3825,6 +4017,40 @@ def count(a, axis = None): count.__doc__ = MaskedArray.count.__doc__ +def diag(v, k=0): + """ + Extract a diagonal or construct a diagonal array. + + Parameters + ---------- + v : array_like + If `v` is a 2-dimensional array, return a copy of + its `k`-th diagonal. If `v` is a 1-dimensional array, + return a 2-dimensional array with `v` on the `k`-th diagonal. + k : int, optional + Diagonal in question. The defaults is 0. + + Examples + -------- + >>> x = np.arange(9).reshape((3,3)) + >>> x + array([[0, 1, 2], + [3, 4, 5], + [6, 7, 8]]) + >>> np.diag(x) + array([0, 4, 8]) + >>> np.diag(np.diag(x)) + array([[0, 0, 0], + [0, 4, 0], + [0, 0, 8]]) + + """ + output = np.diag(v, k).view(MaskedArray) + if getmask(v) is not nomask: + output._mask = np.diag(v._mask, k) + return output + + def expand_dims(x, axis): """ Expand the shape of the array by including a new axis before @@ -4085,7 +4311,8 @@ def choose (indices, choices, out=None, mode='raise'): def round_(a, decimals=0, out=None): - """Return a copy of a, rounded to 'decimals' places. + """ + Return a copy of a, rounded to 'decimals' places. When 'decimals' is negative, it specifies the number of positions to the left of the decimal point. The real and imaginary parts of @@ -4114,9 +4341,20 @@ def round_(a, decimals=0, out=None): if hasattr(out, '_mask'): out._mask = getmask(a) return out - +round = round_ def inner(a, b): + """ + Returns the inner product of a and b for arrays of floating point types. + + Like the generic NumPy equivalent the product sum is over the last dimension + of a and b. + + Notes + ----- + The first argument is not conjugated. + + """ fa = filled(a, 0) fb = filled(b, 0) if len(fa.shape) == 0: @@ -4167,7 +4405,7 @@ def allequal (a, b, fill_value=True): def allclose (a, b, masked_equal=True, rtol=1.e-5, atol=1.e-8, fill_value=None): """ - Returns True if two arrays are element-wise equal within a tolerance. + Returns True if two arrays are element-wise equal within a tolerance. The tolerance values are positive, typically very small numbers. The relative difference (`rtol` * `b`) and the absolute difference (`atol`) @@ -4332,9 +4570,18 @@ class _convert2ma: def __init__(self, funcname): self._func = getattr(np, funcname) self.__doc__ = self.getdoc() + # def getdoc(self): "Return the doc of the function (from the doc of the method)." - return self._func.__doc__ + doc = getattr(self._func, '__doc__', None) + sig = get_object_signature(self._func) + if doc: + # Add the signature of the function at the beginning of the doc + if sig: + sig = "%s%s\n" % (self._func.__name__, sig) + doc = sig + doc + return doc + # def __call__(self, a, *args, **params): return self._func.__call__(a, *args, **params).view(MaskedArray) @@ -4348,5 +4595,6 @@ identity = _convert2ma('identity') indices = np.indices ones = _convert2ma('ones') zeros = _convert2ma('zeros') +squeeze = np.squeeze ############################################################################### diff --git a/numpy/ma/extras.py b/numpy/ma/extras.py index d88022888..cf80180e4 100644 --- a/numpy/ma/extras.py +++ b/numpy/ma/extras.py @@ -16,15 +16,15 @@ __all__ = ['apply_along_axis', 'atleast_1d', 'atleast_2d', 'atleast_3d', 'column_stack','compress_cols','compress_rowcols', 'compress_rows', 'count_masked', 'corrcoef', 'cov', 'diagflat', 'dot','dstack', - 'ediff1d','expand_dims', - 'flatnotmasked_contiguous','flatnotmasked_edges', - 'hsplit','hstack', - 'mask_cols','mask_rowcols','mask_rows','masked_all','masked_all_like', - 'median','mr_', - 'notmasked_contiguous','notmasked_edges', + 'ediff1d', + 'flatnotmasked_contiguous', 'flatnotmasked_edges', + 'hsplit', 'hstack', + 'mask_cols', 'mask_rowcols', 'mask_rows', 'masked_all', + 'masked_all_like', 'median', 'mr_', + 'notmasked_contiguous', 'notmasked_edges', 'polyfit', 'row_stack', - 'vander','vstack', + 'vander', 'vstack', ] from itertools import groupby @@ -32,15 +32,15 @@ import warnings import core as ma from core import MaskedArray, MAError, add, array, asarray, concatenate, count,\ - filled, getmask, getmaskarray, masked, masked_array, mask_or, nomask, ones,\ - sort, zeros + filled, getmask, getmaskarray, make_mask_descr, masked, masked_array,\ + mask_or, nomask, ones, sort, zeros #from core import * import numpy as np from numpy import ndarray, array as nxarray import numpy.core.umath as umath from numpy.lib.index_tricks import AxisConcatenator -from numpy.lib.polynomial import _lstsq +from numpy.linalg import lstsq #............................................................................... def issequence(seq): @@ -77,7 +77,7 @@ def masked_all(shape, dtype=float): """ a = masked_array(np.empty(shape, dtype), - mask=np.ones(shape, bool)) + mask=np.ones(shape, make_mask_descr(dtype))) return a def masked_all_like(arr): @@ -85,8 +85,8 @@ def masked_all_like(arr): the array `a`, where all the data are masked. """ - a = masked_array(np.empty_like(arr), - mask=np.ones(arr.shape, bool)) + a = np.empty_like(arr).view(MaskedArray) + a._mask = np.ones(a.shape, dtype=make_mask_descr(a.dtype)) return a @@ -102,11 +102,13 @@ class _fromnxfunction: def getdoc(self): "Retrieves the __doc__ string from the function." - inidoc = getattr(np, self.__name__).__doc__ - if inidoc: + npfunc = getattr(np, self.__name__, None) + doc = getattr(npfunc, '__doc__', None) + if doc: + sig = self.__name__ + ma.get_object_signature(npfunc) locdoc = "Notes\n-----\nThe function is applied to both the _data"\ " and the _mask, if any." - return '\n'.join((inidoc, locdoc)) + return '\n'.join((sig, doc, locdoc)) return @@ -147,16 +149,6 @@ hsplit = _fromnxfunction('hsplit') diagflat = _fromnxfunction('diagflat') -def expand_dims(a, axis): - """Expands the shape of a by including newaxis before axis. - """ - if not isinstance(a, MaskedArray): - return np.expand_dims(a, axis) - elif getmask(a) is nomask: - return np.expand_dims(a, axis).view(MaskedArray) - m = getmaskarray(a) - return masked_array(np.expand_dims(a, axis), - mask=np.expand_dims(m, axis)) #####-------------------------------------------------------------------------- #---- @@ -172,9 +164,8 @@ def flatten_inplace(seq): def apply_along_axis(func1d, axis, arr, *args, **kwargs): - """Execute func1d(arr[i],*args) where func1d takes 1-D arrays and - arr is an N-d array. i varies so as to apply the function along - the given axis for each 1-d subarray in arr. + """ + (This docstring should be overwritten) """ arr = array(arr, copy=False, subok=True) nd = arr.ndim @@ -257,6 +248,8 @@ def apply_along_axis(func1d, axis, arr, *args, **kwargs): result = asarray(outarr, dtype=max_dtypes) result.fill_value = ma.default_fill_value(result) return result +apply_along_axis.__doc__ = np.apply_along_axis.__doc__ + def average(a, axis=None, weights=None, returned=False): """Average the array over the given axis. @@ -446,23 +439,25 @@ def median(a, axis=None, out=None, overwrite_input=False): #.............................................................................. def compress_rowcols(x, axis=None): - """Suppress the rows and/or columns of a 2D array that contains + """ + Suppress the rows and/or columns of a 2D array that contains masked values. The suppression behavior is selected with the `axis`parameter. + - If axis is None, rows and columns are suppressed. - If axis is 0, only rows are suppressed. - If axis is 1 or -1, only columns are suppressed. Parameters ---------- - axis : int, optional - Axis along which to perform the operation. - If None, applies to a flattened version of the array. + axis : int, optional + Axis along which to perform the operation. + If None, applies to a flattened version of the array. Returns ------- - compressed_array : an ndarray. + compressed_array : an ndarray. """ x = asarray(x) @@ -499,9 +494,10 @@ def compress_cols(a): return compress_rowcols(a, 1) def mask_rowcols(a, axis=None): - """Mask whole rows and/or columns of a 2D array that contain + """ + Mask whole rows and/or columns of a 2D array that contain masked values. The masking behavior is selected with the - `axis`parameter. + `axis` parameter. - If axis is None, rows and columns are masked. - If axis is 0, only rows are masked. @@ -509,13 +505,13 @@ def mask_rowcols(a, axis=None): Parameters ---------- - axis : int, optional - Axis along which to perform the operation. - If None, applies to a flattened version of the array. + axis : int, optional + Axis along which to perform the operation. + If None, applies to a flattened version of the array. Returns ------- - a *pure* ndarray. + a *pure* ndarray. """ a = asarray(a) @@ -996,128 +992,20 @@ def notmasked_contiguous(a, axis=None): #####-------------------------------------------------------------------------- def vander(x, n=None): - """%s - Notes - ----- - Masked values in x will result in rows of zeros. + """ + Masked values in the input array result in rows of zeros. """ _vander = np.vander(x, n) m = getmask(x) if m is not nomask: _vander[m] = 0 return _vander +vander.__doc__ = ma.doc_note(np.vander.__doc__, vander.__doc__) def polyfit(x, y, deg, rcond=None, full=False): """ - Least squares polynomial fit. - - Do a best fit polynomial of degree 'deg' of 'x' to 'y'. Return value is a - vector of polynomial coefficients [pk ... p1 p0]. Eg, for ``deg = 2``:: - - p2*x0^2 + p1*x0 + p0 = y1 - p2*x1^2 + p1*x1 + p0 = y1 - p2*x2^2 + p1*x2 + p0 = y2 - ..... - p2*xk^2 + p1*xk + p0 = yk - - Parameters - ---------- - x : array_like - 1D vector of sample points. - y : array_like - 1D vector or 2D array of values to fit. The values should run down the - columns in the 2D case. - deg : integer - Degree of the fitting polynomial - rcond: {None, float}, optional - Relative condition number of the fit. Singular values smaller than this - relative to the largest singular value will be ignored. The defaul value - is len(x)*eps, where eps is the relative precision of the float type, - about 2e-16 in most cases. - full : {False, boolean}, optional - Switch determining nature of return value. When it is False just the - coefficients are returned, when True diagnostic information from the - singular value decomposition is also returned. - - Returns - ------- - coefficients, [residuals, rank, singular_values, rcond] : variable - When full=False, only the coefficients are returned, running down the - appropriate colume when y is a 2D array. When full=True, the rank of the - scaled Vandermonde matrix, its effective rank in light of the rcond - value, its singular values, and the specified value of rcond are also - returned. - - Warns - ----- - RankWarning : if rank is reduced and not full output - The warnings can be turned off by: - >>> import warnings - >>> warnings.simplefilter('ignore',np.RankWarning) - - - See Also - -------- - polyval : computes polynomial values. - - Notes - ----- - If X is a the Vandermonde Matrix computed from x (see - http://mathworld.wolfram.com/VandermondeMatrix.html), then the - polynomial least squares solution is given by the 'p' in - - X*p = y - - where X.shape is a matrix of dimensions (len(x), deg + 1), p is a vector of - dimensions (deg + 1, 1), and y is a vector of dimensions (len(x), 1). - - This equation can be solved as - - p = (XT*X)^-1 * XT * y - - where XT is the transpose of X and -1 denotes the inverse. However, this - method is susceptible to rounding errors and generally the singular value - decomposition of the matrix X is preferred and that is what is done here. - The singular value method takes a paramenter, 'rcond', which sets a limit on - the relative size of the smallest singular value to be used in solving the - equation. This may result in lowering the rank of the Vandermonde matrix, in - which case a RankWarning is issued. If polyfit issues a RankWarning, try a - fit of lower degree or replace x by x - x.mean(), both of which will - generally improve the condition number. The routine already normalizes the - vector x by its maximum absolute value to help in this regard. The rcond - parameter can be set to a value smaller than its default, but the resulting - fit may be spurious. The current default value of rcond is len(x)*eps, where - eps is the relative precision of the floating type being used, generally - around 1e-7 and 2e-16 for IEEE single and double precision respectively. - This value of rcond is fairly conservative but works pretty well when x - - x.mean() is used in place of x. - - - DISCLAIMER: Power series fits are full of pitfalls for the unwary once the - degree of the fit becomes large or the interval of sample points is badly - centered. The problem is that the powers x**n are generally a poor basis for - the polynomial functions on the sample interval, resulting in a Vandermonde - matrix is ill conditioned and coefficients sensitive to rounding erros. The - computation of the polynomial values will also sensitive to rounding errors. - Consequently, the quality of the polynomial fit should be checked against - the data whenever the condition number is large. The quality of polynomial - fits *can not* be taken for granted. If all you want to do is draw a smooth - curve through the y values and polyfit is not doing the job, try centering - the sample range or look into scipy.interpolate, which includes some nice - spline fitting functions that may be of use. - - For more info, see - http://mathworld.wolfram.com/LeastSquaresFittingPolynomial.html, - but note that the k's and n's in the superscripts and subscripts - on that page. The linear algebra is correct, however. - - - - Notes - ----- - Any masked values in x is propagated in y, and vice-versa. - + Any masked values in x is propagated in y, and vice-versa. """ order = int(deg) + 1 x = asarray(x) @@ -1145,7 +1033,7 @@ def polyfit(x, y, deg, rcond=None, full=False): x = x / scale # solve least squares equation for powers of x v = vander(x, order) - c, resids, rank, s = _lstsq(v, y.filled(0), rcond) + c, resids, rank, s = lstsq(v, y.filled(0), rcond) # warn on rank reduction, which indicates an ill conditioned matrix if rank != order and not full: warnings.warn("Polyfit may be poorly conditioned", np.RankWarning) @@ -1159,5 +1047,6 @@ def polyfit(x, y, deg, rcond=None, full=False): return c, resids, rank, s, rcond else : return c +polyfit.__doc__ = ma.doc_note(np.polyfit.__doc__, polyfit.__doc__) ################################################################################ diff --git a/numpy/ma/tests/test_core.py b/numpy/ma/tests/test_core.py index b51a90712..8a0ca08ac 100644 --- a/numpy/ma/tests/test_core.py +++ b/numpy/ma/tests/test_core.py @@ -693,7 +693,7 @@ class TestMaskedArrayArithmetic(TestCase): def test_minmax_funcs_with_output(self): "Tests the min/max functions with explicit outputs" mask = np.random.rand(12).round() - xm = array(np.random.uniform(0,10,12),mask=mask) + xm = array(np.random.uniform(0,10,12), mask=mask) xm.shape = (3,4) for funcname in ('min', 'max'): # Initialize @@ -701,11 +701,16 @@ class TestMaskedArrayArithmetic(TestCase): mafunc = getattr(numpy.ma.core, funcname) # Use the np version nout = np.empty((4,), dtype=int) - result = npfunc(xm,axis=0,out=nout) + try: + result = npfunc(xm, axis=0, out=nout) + except MaskError: + pass + nout = np.empty((4,), dtype=float) + result = npfunc(xm, axis=0, out=nout) self.failUnless(result is nout) # Use the ma version nout.fill(-999) - result = mafunc(xm,axis=0,out=nout) + result = mafunc(xm, axis=0, out=nout) self.failUnless(result is nout) @@ -820,6 +825,7 @@ class TestMaskedArrayArithmetic(TestCase): self.failUnless(result is output) self.failUnless(output[0] is masked) + #------------------------------------------------------------------------------ class TestMaskedArrayAttributes(TestCase): @@ -1235,22 +1241,131 @@ class TestMaskedArrayInPlaceArithmetics(TestCase): def test_inplace_division_misc(self): # - x = np.array([1.,1.,1.,-2., pi/2.0, 4., 5., -10., 10., 1., 2., 3.]) - y = np.array([5.,0.,3., 2., -1., -4., 0., -10., 10., 1., 0., 3.]) - m1 = [1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0] - m2 = [0, 0, 1, 0, 0, 1, 1, 0, 0, 0 ,0, 1] + x = [1., 1., 1.,-2., pi/2., 4., 5., -10., 10., 1., 2., 3.] + y = [5., 0., 3., 2., -1., -4., 0., -10., 10., 1., 0., 3.] + m1 = [1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0] + m2 = [0, 0, 1, 0, 0, 1, 1, 0, 0, 0 , 0, 1] xm = masked_array(x, mask=m1) ym = masked_array(y, mask=m2) # z = xm/ym assert_equal(z._mask, [1,1,1,0,0,1,1,0,0,0,1,1]) - assert_equal(z._data, [0.2,1.,1./3.,-1.,-pi/2.,-1.,5.,1.,1.,1.,2.,1.]) + assert_equal(z._data, [1.,1.,1.,-1.,-pi/2.,4.,5.,1.,1.,1.,2.,3.]) + #assert_equal(z._data, [0.2,1.,1./3.,-1.,-pi/2.,-1.,5.,1.,1.,1.,2.,1.]) # xm = xm.copy() xm /= ym assert_equal(xm._mask, [1,1,1,0,0,1,1,0,0,0,1,1]) - assert_equal(xm._data, [1/5.,1.,1./3.,-1.,-pi/2.,-1.,5.,1.,1.,1.,2.,1.]) - + assert_equal(z._data, [1.,1.,1.,-1.,-pi/2.,4.,5.,1.,1.,1.,2.,3.]) + #assert_equal(xm._data, [1/5.,1.,1./3.,-1.,-pi/2.,-1.,5.,1.,1.,1.,2.,1.]) + + + def test_datafriendly_add(self): + "Test keeping data w/ (inplace) addition" + x = array([1, 2, 3], mask=[0, 0, 1]) + # Test add w/ scalar + xx = x + 1 + assert_equal(xx.data, [2, 3, 3]) + assert_equal(xx.mask, [0, 0, 1]) + # Test iadd w/ scalar + x += 1 + assert_equal(x.data, [2, 3, 3]) + assert_equal(x.mask, [0, 0, 1]) + # Test add w/ array + x = array([1, 2, 3], mask=[0, 0, 1]) + xx = x + array([1, 2, 3], mask=[1, 0, 0]) + assert_equal(xx.data, [1, 4, 3]) + assert_equal(xx.mask, [1, 0, 1]) + # Test iadd w/ array + x = array([1, 2, 3], mask=[0, 0, 1]) + x += array([1, 2, 3], mask=[1, 0, 0]) + assert_equal(x.data, [1, 4, 3]) + assert_equal(x.mask, [1, 0, 1]) + + + def test_datafriendly_sub(self): + "Test keeping data w/ (inplace) subtraction" + # Test sub w/ scalar + x = array([1, 2, 3], mask=[0, 0, 1]) + xx = x - 1 + assert_equal(xx.data, [0, 1, 3]) + assert_equal(xx.mask, [0, 0, 1]) + # Test isub w/ scalar + x = array([1, 2, 3], mask=[0, 0, 1]) + x -= 1 + assert_equal(x.data, [0, 1, 3]) + assert_equal(x.mask, [0, 0, 1]) + # Test sub w/ array + x = array([1, 2, 3], mask=[0, 0, 1]) + xx = x - array([1, 2, 3], mask=[1, 0, 0]) + assert_equal(xx.data, [1, 0, 3]) + assert_equal(xx.mask, [1, 0, 1]) + # Test isub w/ array + x = array([1, 2, 3], mask=[0, 0, 1]) + x -= array([1, 2, 3], mask=[1, 0, 0]) + assert_equal(x.data, [1, 0, 3]) + assert_equal(x.mask, [1, 0, 1]) + + + def test_datafriendly_mul(self): + "Test keeping data w/ (inplace) multiplication" + # Test mul w/ scalar + x = array([1, 2, 3], mask=[0, 0, 1]) + xx = x * 2 + assert_equal(xx.data, [2, 4, 3]) + assert_equal(xx.mask, [0, 0, 1]) + # Test imul w/ scalar + x = array([1, 2, 3], mask=[0, 0, 1]) + x *= 2 + assert_equal(x.data, [2, 4, 3]) + assert_equal(x.mask, [0, 0, 1]) + # Test mul w/ array + x = array([1, 2, 3], mask=[0, 0, 1]) + xx = x * array([10, 20, 30], mask=[1, 0, 0]) + assert_equal(xx.data, [1, 40, 3]) + assert_equal(xx.mask, [1, 0, 1]) + # Test imul w/ array + x = array([1, 2, 3], mask=[0, 0, 1]) + x *= array([10, 20, 30], mask=[1, 0, 0]) + assert_equal(x.data, [1, 40, 3]) + assert_equal(x.mask, [1, 0, 1]) + + + def test_datafriendly_div(self): + "Test keeping data w/ (inplace) division" + # Test div on scalar + x = array([1, 2, 3], mask=[0, 0, 1]) + xx = x / 2. + assert_equal(xx.data, [1/2., 2/2., 3]) + assert_equal(xx.mask, [0, 0, 1]) + # Test idiv on scalar + x = array([1., 2., 3.], mask=[0, 0, 1]) + x /= 2. + assert_equal(x.data, [1/2., 2/2., 3]) + assert_equal(x.mask, [0, 0, 1]) + # Test div on array + x = array([1., 2., 3.], mask=[0, 0, 1]) + xx = x / array([10., 20., 30.], mask=[1, 0, 0]) + assert_equal(xx.data, [1., 2./20., 3.]) + assert_equal(xx.mask, [1, 0, 1]) + # Test idiv on array + x = array([1., 2., 3.], mask=[0, 0, 1]) + x /= array([10., 20., 30.], mask=[1, 0, 0]) + assert_equal(x.data, [1., 2/20., 3.]) + assert_equal(x.mask, [1, 0, 1]) + + + def test_datafriendly_pow(self): + "Test keeping data w/ (inplace) power" + # Test pow on scalar + x = array([1., 2., 3.], mask=[0, 0, 1]) + xx = x ** 2.5 + assert_equal(xx.data, [1., 2.**2.5, 3.]) + assert_equal(xx.mask, [0, 0, 1]) + # Test ipow on scalar + x **= 2.5 + assert_equal(x.data, [1., 2.**2.5, 3]) + assert_equal(x.mask, [0, 0, 1]) #------------------------------------------------------------------------------ @@ -1952,6 +2067,22 @@ class TestMaskArrayMathMethod(TestCase): _ = method(out=nout, ddof=1) self.failUnless(np.isnan(nout)) + + def test_diag(self): + "Test diag" + x = arange(9).reshape((3,3)) + x[1,1] = masked + out = np.diag(x) + assert_equal(out, [0, 4, 8]) + out = diag(x) + assert_equal(out, [0, 4, 8]) + assert_equal(out.mask, [0, 1, 0]) + out = diag(out) + control = array([[0, 0, 0], [0, 4, 0], [0, 0, 8]], + mask = [[0, 0, 0], [0, 1, 0], [0, 0, 0]]) + assert_equal(out, control) + + #------------------------------------------------------------------------------ class TestMaskedArrayMathMethodsComplex(TestCase): @@ -2132,8 +2263,8 @@ class TestMaskedArrayFunctions(TestCase): def test_power(self): x = -1.1 - assert_almost_equal(power(x,2.), 1.21) - self.failUnless(power(x,masked) is masked) + assert_almost_equal(power(x, 2.), 1.21) + self.failUnless(power(x, masked) is masked) x = array([-1.1,-1.1,1.1,1.1,0.]) b = array([0.5,2.,0.5,2.,-1.], mask=[0,0,0,0,1]) y = power(x,b) @@ -2312,17 +2443,31 @@ class TestMaskedArrayFunctions(TestCase): def test_make_mask_descr(self): "Test make_mask_descr" + # Flexible ntype = [('a',np.float), ('b',np.float)] test = make_mask_descr(ntype) assert_equal(test, [('a',np.bool),('b',np.bool)]) - # + # Standard w/ shape ntype = (np.float, 2) test = make_mask_descr(ntype) assert_equal(test, (np.bool,2)) - # + # Standard standard ntype = np.float test = make_mask_descr(ntype) assert_equal(test, np.dtype(np.bool)) + # Nested + ntype = [('a', np.float), ('b', [('ba', np.float), ('bb', np.float)])] + test = make_mask_descr(ntype) + control = np.dtype([('a', 'b1'), ('b', [('ba', 'b1'), ('bb', 'b1')])]) + assert_equal(test, control) + # Named+ shape + ntype = [('a', (np.float, 2))] + test = make_mask_descr(ntype) + assert_equal(test, np.dtype([('a', (np.bool, 2))])) + # 2 names + ntype = [(('A', 'a'), float)] + test = make_mask_descr(ntype) + assert_equal(test, np.dtype([(('A', 'a'), bool)])) def test_make_mask(self): @@ -2388,6 +2533,24 @@ class TestMaskedArrayFunctions(TestCase): pass + def test_flatten_mask(self): + "Tests flatten mask" + # Standarad dtype + mask = np.array([0, 0, 1], dtype=np.bool) + assert_equal(flatten_mask(mask), mask) + # Flexible dtype + mask = np.array([(0, 0), (0, 1)], dtype=[('a', bool), ('b', bool)]) + test = flatten_mask(mask) + control = np.array([0, 0, 0, 1], dtype=bool) + assert_equal(test, control) + + mdtype = [('a', bool), ('b', [('ba', bool), ('bb', bool)])] + data = [(0, (0, 0)), (0, (0, 1))] + mask = np.array(data, dtype=mdtype) + test = flatten_mask(mask) + control = np.array([ 0, 0, 0, 0, 0, 1], dtype=bool) + assert_equal(test, control) + #------------------------------------------------------------------------------ class TestMaskedFields(TestCase): diff --git a/numpy/ma/tests/test_extras.py b/numpy/ma/tests/test_extras.py index f41cff952..344dfce5a 100644 --- a/numpy/ma/tests/test_extras.py +++ b/numpy/ma/tests/test_extras.py @@ -17,6 +17,62 @@ from numpy.ma.testutils import * from numpy.ma.core import * from numpy.ma.extras import * + +class TestGeneric(TestCase): + # + def test_masked_all(self): + "Tests masked_all" + # Standard dtype + test = masked_all((2,), dtype=float) + control = array([1, 1], mask=[1, 1], dtype=float) + assert_equal(test, control) + # Flexible dtype + dt = np.dtype({'names': ['a', 'b'], 'formats': ['f', 'f']}) + test = masked_all((2,), dtype=dt) + control = array([(0, 0), (0, 0)], mask=[(1, 1), (1, 1)], dtype=dt) + assert_equal(test, control) + test = masked_all((2,2), dtype=dt) + control = array([[(0, 0), (0, 0)], [(0, 0), (0, 0)]], + mask=[[(1, 1), (1, 1)], [(1, 1), (1, 1)]], + dtype=dt) + assert_equal(test, control) + # Nested dtype + dt = np.dtype([('a','f'), ('b', [('ba', 'f'), ('bb', 'f')])]) + test = masked_all((2,), dtype=dt) + control = array([(1, (1, 1)), (1, (1, 1))], + mask=[(1, (1, 1)), (1, (1, 1))], dtype=dt) + assert_equal(test, control) + test = masked_all((2,), dtype=dt) + control = array([(1, (1, 1)), (1, (1, 1))], + mask=[(1, (1, 1)), (1, (1, 1))], dtype=dt) + assert_equal(test, control) + test = masked_all((1,1), dtype=dt) + control = array([[(1, (1, 1))]], mask=[[(1, (1, 1))]], dtype=dt) + assert_equal(test, control) + + + def test_masked_all_like(self): + "Tests masked_all" + # Standard dtype + base = array([1, 2], dtype=float) + test = masked_all_like(base) + control = array([1, 1], mask=[1, 1], dtype=float) + assert_equal(test, control) + # Flexible dtype + dt = np.dtype({'names': ['a', 'b'], 'formats': ['f', 'f']}) + base = array([(0, 0), (0, 0)], mask=[(1, 1), (1, 1)], dtype=dt) + test = masked_all_like(base) + control = array([(10, 10), (10, 10)], mask=[(1, 1), (1, 1)], dtype=dt) + assert_equal(test, control) + # Nested dtype + dt = np.dtype([('a','f'), ('b', [('ba', 'f'), ('bb', 'f')])]) + control = array([(1, (1, 1)), (1, (1, 1))], + mask=[(1, (1, 1)), (1, (1, 1))], dtype=dt) + test = masked_all_like(control) + assert_equal(test, control) + # + + class TestAverage(TestCase): "Several tests of average. Why so many ? Good point..." def test_testAverage1(self): diff --git a/numpy/ma/testutils.py b/numpy/ma/testutils.py index 28754bccc..df7484f0a 100644 --- a/numpy/ma/testutils.py +++ b/numpy/ma/testutils.py @@ -167,22 +167,24 @@ def assert_array_compare(comparison, x, y, err_msg='', verbose=True, header='', """Asserts that a comparison relation between two masked arrays is satisfied elementwise.""" # Fill the data first - xf = filled(x) - yf = filled(y) +# xf = filled(x) +# yf = filled(y) # Allocate a common mask and refill m = mask_or(getmask(x), getmask(y)) - x = masked_array(xf, copy=False, mask=m) - y = masked_array(yf, copy=False, mask=m) + x = masked_array(x, copy=False, mask=m, subok=False) + y = masked_array(y, copy=False, mask=m, subok=False) if ((x is masked) and not (y is masked)) or \ ((y is masked) and not (x is masked)): msg = build_err_msg([x, y], err_msg=err_msg, verbose=verbose, header=header, names=('x', 'y')) raise ValueError(msg) # OK, now run the basic tests on filled versions + comparison = getattr(np, comparison.__name__, lambda x,y: True) return utils.assert_array_compare(comparison, - x.filled(fill_value), y.filled(fill_value), - err_msg=err_msg, - verbose=verbose, header=header) + x.filled(fill_value), + y.filled(fill_value), + err_msg=err_msg, + verbose=verbose, header=header) def assert_array_equal(x, y, err_msg='', verbose=True): |
