diff options
author | David Cournapeau <cournape@gmail.com> | 2009-02-20 16:37:01 +0000 |
---|---|---|
committer | David Cournapeau <cournape@gmail.com> | 2009-02-20 16:37:01 +0000 |
commit | da364a18e447c334dfa2ca5083b08e1b6a7c0d10 (patch) | |
tree | 7783575e8fe37f8e13ddfc050e9c9d94529bca8e /numpy/lib | |
parent | 745cfe4c60a59c01a8c2fd6ef68a8ca87f2180f5 (diff) | |
parent | a271ac2e41aacab2903864cf371c4ba94cf4ec3f (diff) | |
download | numpy-da364a18e447c334dfa2ca5083b08e1b6a7c0d10.tar.gz |
Merged revisions 6185-6187,6191-6221,6235-6238,6240-6241,6244,6250-6251,6253,6256,6258,6260-6261,6263,6265-6266,6268,6271,6283-6286,6291-6316,6320-6352,6354,6356,6358-6368,6370-6373,6398-6400,6410,6421-6424 via svnmerge from
http://svn.scipy.org/svn/numpy/trunk
................
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.
................
r6191 | cdavid | 2008-12-23 13:10:59 +0900 (Tue, 23 Dec 2008) | 1 line
Fix typos in the comments for manifest.
................
r6192 | cdavid | 2008-12-23 13:11:12 +0900 (Tue, 23 Dec 2008) | 1 line
Use msvcrt values if available for manifest generation: only there starting from python 2.6.1.
................
r6193 | pearu | 2008-12-23 18:02:15 +0900 (Tue, 23 Dec 2008) | 1 line
Fix issue 964: f2py python 2.6, 2.6.1 support.
................
r6194 | pierregm | 2008-12-24 08:43:43 +0900 (Wed, 24 Dec 2008) | 12 lines
testutils:
* assert_equal : use assert_equal_array on records
* assert_array_compare : prevent the common mask to be back-propagated to the initial input arrays.
* assert_equal_array : use operator.__eq__ instead of ma.equal
* assert_equal_less: use operator.__less__ instead of ma.less
core:
* Fixed _check_fill_value for nested flexible types
* Add a ndtype option to _make_mask_descr
* Fixed mask_or for nested flexible types
* Fixed the printing of masked arrays w/ flexible types.
................
r6195 | cdavid | 2008-12-26 21:16:45 +0900 (Fri, 26 Dec 2008) | 1 line
Update to handle numscons 0.10.0 and above.
................
r6196 | cdavid | 2008-12-26 21:36:19 +0900 (Fri, 26 Dec 2008) | 1 line
Do not import msvcrt globally in mingw32compiler module, since the module is imported on all platforms.
................
r6197 | cdavid | 2008-12-26 23:39:55 +0900 (Fri, 26 Dec 2008) | 1 line
Do not test for functions already tested by python configure script.
................
r6198 | cdavid | 2008-12-27 14:56:58 +0900 (Sat, 27 Dec 2008) | 1 line
BUG: Add a runtime check about endianness, to detect bug 4728 in python on Mac OS X.
................
r6199 | cdavid | 2008-12-27 19:06:25 +0900 (Sat, 27 Dec 2008) | 1 line
Fix some typo/syntax errors when converting dict access to a function in manifest generation.
................
r6200 | cdavid | 2008-12-27 19:15:30 +0900 (Sat, 27 Dec 2008) | 1 line
BUG (#970): fix a python 2.6 bug in distutils which caused an unhelpful Error:None message when trying to build with no VS installed and without the -c mingw32 option.
................
r6201 | cdavid | 2008-12-27 19:30:49 +0900 (Sat, 27 Dec 2008) | 1 line
Improve the error message when initializing compiler failed.
................
r6202 | cdavid | 2008-12-27 19:32:05 +0900 (Sat, 27 Dec 2008) | 1 line
Try to initialize the msvc compiler before the general code to detect the error early.
................
r6203 | cdavid | 2008-12-27 19:43:41 +0900 (Sat, 27 Dec 2008) | 1 line
BUG (#970): this commit should fix the actual bug, which albeeit linked to commir r6200, was caused in anoter code path.
................
r6204 | cdavid | 2008-12-27 19:57:05 +0900 (Sat, 27 Dec 2008) | 1 line
Fix manifest generation.
................
r6205 | cdavid | 2008-12-27 20:46:08 +0900 (Sat, 27 Dec 2008) | 1 line
BUG (#827): close temp file before reopning them on windows, and make sure they are not automatically deleted on close either (2.6and higher specific).
................
r6206 | cdavid | 2008-12-27 21:18:47 +0900 (Sat, 27 Dec 2008) | 1 line
Do not define the union for runtime endianness detection if we don't check endianness.
................
r6207 | cdavid | 2008-12-27 22:48:52 +0900 (Sat, 27 Dec 2008) | 1 line
Start working on formatting failure on 2.6: copy how python does complex formatting.
................
r6208 | cdavid | 2008-12-27 23:44:11 +0900 (Sat, 27 Dec 2008) | 1 line
Fix formatting for purely imaginary complex numbers.
................
r6209 | cdavid | 2008-12-27 23:53:15 +0900 (Sat, 27 Dec 2008) | 1 line
More work on formatting float.
................
r6210 | cdavid | 2008-12-27 23:59:41 +0900 (Sat, 27 Dec 2008) | 1 line
Finish formatting fixes for float scalar arrays.
................
r6211 | cdavid | 2008-12-28 00:12:20 +0900 (Sun, 28 Dec 2008) | 1 line
Include umath_funcs_c99 in multiarray so that we can use isinf and co macros.
................
r6212 | cdavid | 2008-12-28 01:15:04 +0900 (Sun, 28 Dec 2008) | 1 line
Include config.h before our C99 math compat layer.
................
r6213 | cdavid | 2008-12-28 01:15:41 +0900 (Sun, 28 Dec 2008) | 1 line
Fix formatting.
................
r6214 | cdavid | 2008-12-28 01:16:18 +0900 (Sun, 28 Dec 2008) | 1 line
Do not define FMTR and FMTI macros, as those are already defined on some platforms.
................
r6215 | cdavid | 2008-12-28 01:16:52 +0900 (Sun, 28 Dec 2008) | 1 line
More formatting fixes.
................
r6216 | cdavid | 2008-12-28 01:17:27 +0900 (Sun, 28 Dec 2008) | 1 line
Remove undef of removed macro.
................
r6217 | cdavid | 2008-12-28 01:33:40 +0900 (Sun, 28 Dec 2008) | 1 line
Do not use PyOS_ascii_formatd, as it does not handle long double correctly.
................
r6218 | cdavid | 2008-12-28 02:19:40 +0900 (Sun, 28 Dec 2008) | 1 line
Try ugly hack to circumvent long double brokenness with mingw.
................
r6219 | cdavid | 2008-12-28 02:25:50 +0900 (Sun, 28 Dec 2008) | 1 line
Use ugly hack for mingw long double pb with complex format function as well.
................
r6220 | cdavid | 2008-12-28 12:18:20 +0900 (Sun, 28 Dec 2008) | 1 line
Revert formatting changes: ascii_formatd only works for double, so we can't use it as it is for our formatting needs.
................
r6221 | cdavid | 2008-12-28 15:44:06 +0900 (Sun, 28 Dec 2008) | 1 line
Do not add doc sources through add_data_dir: it will put the docs alongside numpy, as a separate package, which is not what we want. Use the manifest instead, since that's the only way I know of to include something in sdist-generated tarballs.
................
r6235 | cdavid | 2008-12-29 16:57:52 +0900 (Mon, 29 Dec 2008) | 13 lines
Merged revisions 6233-6234 via svnmerge from
http://svn.scipy.org/svn/numpy/branches/fix_float_format
........
r6233 | cdavid | 2008-12-29 12:49:09 +0900 (Mon, 29 Dec 2008) | 1 line
Use parametric tests for format tests so that it is clearer which type is failing.
........
r6234 | cdavid | 2008-12-29 12:49:27 +0900 (Mon, 29 Dec 2008) | 1 line
Fix formatting tests: cfloat and cdouble as well as np.float and np.double are the same; make sure we test 4 bytes float.
........
................
r6236 | cdavid | 2008-12-29 17:02:15 +0900 (Mon, 29 Dec 2008) | 1 line
Add nan/inf tests for formatting.
................
r6237 | cdavid | 2008-12-29 17:26:04 +0900 (Mon, 29 Dec 2008) | 1 line
Add test for real float types locale independance.
................
r6238 | cdavid | 2008-12-29 17:35:06 +0900 (Mon, 29 Dec 2008) | 1 line
Clearer error messages for formatting failures.
................
r6240 | cdavid | 2008-12-30 12:48:11 +0900 (Tue, 30 Dec 2008) | 1 line
Add tests for print of float types.
................
r6241 | cdavid | 2008-12-30 12:56:54 +0900 (Tue, 30 Dec 2008) | 1 line
Add print tests for complex types.
................
r6244 | cdavid | 2008-12-30 13:20:48 +0900 (Tue, 30 Dec 2008) | 1 line
Fix test for print: forgot to make sure the value is a float before comparing it.
................
r6250 | cdavid | 2008-12-30 14:02:28 +0900 (Tue, 30 Dec 2008) | 17 lines
Merged revisions 6247-6249 via svnmerge from
http://svn.scipy.org/svn/numpy/branches/fix_float_format
........
r6247 | cdavid | 2008-12-30 13:41:37 +0900 (Tue, 30 Dec 2008) | 1 line
Handle 1e10 specially, as it is the limit where exp notation is shorter than decimal for single precision, but not for double (python native one).
........
r6248 | cdavid | 2008-12-30 13:47:38 +0900 (Tue, 30 Dec 2008) | 1 line
Refactor a bit redirected output print test.
........
r6249 | cdavid | 2008-12-30 13:49:31 +0900 (Tue, 30 Dec 2008) | 1 line
Fix test for single precision print.
........
................
r6251 | cdavid | 2008-12-30 14:12:50 +0900 (Tue, 30 Dec 2008) | 1 line
Use np.inf instead of float('inf'), as the later does not work on windows for python < 2.6.
................
r6253 | cdavid | 2008-12-30 14:15:09 +0900 (Tue, 30 Dec 2008) | 1 line
Fix typo in test.
................
r6256 | cdavid | 2008-12-30 14:34:22 +0900 (Tue, 30 Dec 2008) | 1 line
Special case float tests on windows: python 2.5 and below have >=3 digits in the exp.
................
r6258 | cdavid | 2008-12-30 14:42:03 +0900 (Tue, 30 Dec 2008) | 1 line
Hardcode reference for inf/nan-involved values.
................
r6260 | cdavid | 2008-12-30 14:50:18 +0900 (Tue, 30 Dec 2008) | 1 line
Fix more formatting tests on win32.
................
r6261 | cdavid | 2008-12-30 14:52:16 +0900 (Tue, 30 Dec 2008) | 1 line
Fix some more redirected output print tests.
................
r6263 | cdavid | 2008-12-30 15:01:31 +0900 (Tue, 30 Dec 2008) | 1 line
More fixes for print tests.
................
r6265 | cdavid | 2008-12-30 15:03:56 +0900 (Tue, 30 Dec 2008) | 1 line
Fix typo.
................
r6266 | cdavid | 2008-12-30 15:08:06 +0900 (Tue, 30 Dec 2008) | 1 line
Fix typo.
................
r6268 | cdavid | 2008-12-30 15:12:26 +0900 (Tue, 30 Dec 2008) | 1 line
complex scalar arrays cannot be created from real/imag args: wrap init values in a complex.
................
r6271 | cdavid | 2008-12-30 15:32:03 +0900 (Tue, 30 Dec 2008) | 1 line
Do not use dict for reference: hashing on scalar arrays does not work as I expected.
................
r6283 | ptvirtan | 2008-12-31 10:14:47 +0900 (Wed, 31 Dec 2008) | 1 line
Fix #951: make tests to clean temp files properly
................
r6284 | jarrod.millman | 2009-01-01 08:25:03 +0900 (Thu, 01 Jan 2009) | 2 lines
ran reindent
................
r6285 | alan.mcintyre | 2009-01-01 08:46:34 +0900 (Thu, 01 Jan 2009) | 15 lines
Remove the following deprecated items from numpy.testing:
- ParametricTestCase
- The following arguments from numpy.testing.Tester.test(): level,
verbosity,
all, sys_argv, testcase_pattern
- Path manipulation functions: set_package_path, set_local_path,
restore_path
- NumpyTestCase, NumpyTest
Also separated testing parameter setup from NoseTester.test into
NoseTester.prepare_test_args for use in a utility script for valgrind
testing (see NumPy ticket #784).
................
r6286 | jarrod.millman | 2009-01-01 16:56:53 +0900 (Thu, 01 Jan 2009) | 2 lines
add default include dir for Fedora/Red Hat (see SciPy ticket 817)
................
r6291 | cdavid | 2009-01-04 19:57:39 +0900 (Sun, 04 Jan 2009) | 1 line
Do not import md5 on python >= 2.6; use hashlib instead.
................
r6292 | cdavid | 2009-01-04 20:08:16 +0900 (Sun, 04 Jan 2009) | 1 line
Do not use popen* but subprocess.Popen instead.
................
r6293 | cdavid | 2009-01-04 21:03:29 +0900 (Sun, 04 Jan 2009) | 1 line
Revert md5 change: hashlib.md5 is not a drop-in replacement for md5.
................
r6294 | pierregm | 2009-01-05 05:16:00 +0900 (Mon, 05 Jan 2009) | 2 lines
* adapted default_fill_value for flexible datatype
* fixed max/minimum_fill_value for flexible datatype
................
r6295 | stefan | 2009-01-06 06:51:18 +0900 (Tue, 06 Jan 2009) | 1 line
Credit more developers.
................
r6296 | pierregm | 2009-01-06 07:52:21 +0900 (Tue, 06 Jan 2009) | 1 line
*moved the printing templates out of MaskedArray.__repr__
................
r6297 | stefan | 2009-01-06 19:09:00 +0900 (Tue, 06 Jan 2009) | 1 line
Use new-style classes with multiple-inheritance to address bug in IronPython.
................
r6298 | pierregm | 2009-01-07 05:35:37 +0900 (Wed, 07 Jan 2009) | 1 line
* Bugfix #961
................
r6299 | pierregm | 2009-01-08 03:14:12 +0900 (Thu, 08 Jan 2009) | 1 line
* Fixed iadd/isub/imul when the base array has no mask but the other array does
................
r6300 | pierregm | 2009-01-08 07:34:51 +0900 (Thu, 08 Jan 2009) | 3 lines
* Renamed `torecords` to `toflex`, keeping `torecords` as an alias
* Introduced `fromflex`, to reconstruct a masked_array from the output of `toflex` (can?\226?\128?\153t `use fromrecords` as it would clash with `numpy.ma.mrecords.fromrecords`)
* Fixed a bug in MaskedBinaryOperation (#979) (wrong array broadcasting)
................
r6301 | cdavid | 2009-01-08 18:19:00 +0900 (Thu, 08 Jan 2009) | 1 line
Avoid putting things into stderr when errors occurs in f2py wrappers; put all the info in the python error string instead.
................
r6302 | cdavid | 2009-01-09 00:11:32 +0900 (Fri, 09 Jan 2009) | 1 line
Fix python 2.4 issue.
................
r6303 | chanley | 2009-01-09 01:30:01 +0900 (Fri, 09 Jan 2009) | 1 line
Fix test_print.py function _test_locale_independance() since str(1.2) does not use the LC_NUMERIC locale to convert numbers. Fix from Mark Sienkiewicz.
................
r6304 | cdavid | 2009-01-09 04:22:21 +0900 (Fri, 09 Jan 2009) | 1 line
Revert buggy test fix for locale independecce.
................
r6305 | pierregm | 2009-01-09 05:02:29 +0900 (Fri, 09 Jan 2009) | 2 lines
* Add __eq__ and __ne__ for support of flexible arrays.
* Fixed .filled for nested structures
................
r6306 | pierregm | 2009-01-09 06:51:04 +0900 (Fri, 09 Jan 2009) | 1 line
* Remove a debugging print statement.
................
r6307 | jarrod.millman | 2009-01-09 11:14:35 +0900 (Fri, 09 Jan 2009) | 2 lines
Updated license file
................
r6308 | cdavid | 2009-01-09 14:26:58 +0900 (Fri, 09 Jan 2009) | 1 line
Tag formatting unit tests as known failures.
................
r6309 | jarrod.millman | 2009-01-09 17:59:29 +0900 (Fri, 09 Jan 2009) | 2 lines
should be more reliable way to determine what bit platform
................
r6310 | jarrod.millman | 2009-01-09 18:14:17 +0900 (Fri, 09 Jan 2009) | 2 lines
better default library paths for 64bit arch
................
r6311 | jarrod.millman | 2009-01-09 18:57:15 +0900 (Fri, 09 Jan 2009) | 2 lines
simplification suggested by stefan
................
r6312 | jarrod.millman | 2009-01-09 19:02:09 +0900 (Fri, 09 Jan 2009) | 2 lines
switch the order [lib,lib64] --> [lib64,lib]
................
r6313 | jarrod.millman | 2009-01-09 19:18:29 +0900 (Fri, 09 Jan 2009) | 2 lines
removed unneeded import
................
r6314 | jarrod.millman | 2009-01-10 04:37:16 +0900 (Sat, 10 Jan 2009) | 2 lines
can't use append an int to a string
................
r6315 | pierregm | 2009-01-10 05:18:12 +0900 (Sat, 10 Jan 2009) | 2 lines
* Added flatten_structured_arrays
* Fixed _get_recordarray for nested structures
................
r6316 | pierregm | 2009-01-10 10:53:05 +0900 (Sat, 10 Jan 2009) | 1 line
* Add flatten_structured_array to the namespace
................
r6320 | pierregm | 2009-01-14 06:01:58 +0900 (Wed, 14 Jan 2009) | 9 lines
numpy.ma.core:
* introduced baseclass, sharedmask and hardmask as readonly properties of MaskedArray
* docstrings update
numpy.ma.extras:
* docstring updates
docs/reference
* introduced maskedarray, maskedarray.baseclass, maskedarray.generic
................
r6321 | stefan | 2009-01-14 16:14:27 +0900 (Wed, 14 Jan 2009) | 2 lines
Docstring: remove old floating point arithmetic, parallel
execution and postponed import references.
................
r6322 | stefan | 2009-01-14 16:55:16 +0900 (Wed, 14 Jan 2009) | 1 line
Fix printing of limits.
................
r6323 | stefan | 2009-01-14 16:56:10 +0900 (Wed, 14 Jan 2009) | 1 line
Fix finfo to work on all instances, not just NumPy scalars.
................
r6324 | pierregm | 2009-01-17 09:15:15 +0900 (Sat, 17 Jan 2009) | 1 line
* fixed _arraymethod.__call__ for structured arrays
................
r6325 | ptvirtan | 2009-01-18 06:24:13 +0900 (Sun, 18 Jan 2009) | 3 lines
Make `trapz` accept 1-D `x` parameter for n-d `y`, even if axis != -1.
Additional tests included.
................
r6326 | pierregm | 2009-01-19 17:53:53 +0900 (Mon, 19 Jan 2009) | 3 lines
* renamed FlatIter to MaskedIterator
* added __getitem__ to MaskedIterator
................
r6327 | pierregm | 2009-01-19 18:01:24 +0900 (Mon, 19 Jan 2009) | 2 lines
* replace np.asarray by np.asanyarray in unique1d
................
r6328 | pierregm | 2009-01-19 18:04:20 +0900 (Mon, 19 Jan 2009) | 2 lines
* add intersect1d, intersect1d_nu, setdiff1d, setmember1d, setxor1d, unique1d, union1d
* use np.atleast1d instead of ma.atleast1d
................
r6329 | pierregm | 2009-01-20 06:22:52 +0900 (Tue, 20 Jan 2009) | 3 lines
* lib : introduced _iotools
* lib.io : introduced genfromtxt, ndfromtxt, mafromtxt, recfromtxt, recfromcsv.
................
r6330 | pierregm | 2009-01-22 14:37:36 +0900 (Thu, 22 Jan 2009) | 1 line
* genfromtxt : if names is True, accept a line starting with a comment character as header.
................
r6331 | pierregm | 2009-01-22 14:40:25 +0900 (Thu, 22 Jan 2009) | 1 line
* added recfunctions, a collection of utilities to manipulate structured arrays.
................
r6332 | pierregm | 2009-01-23 03:21:32 +0900 (Fri, 23 Jan 2009) | 2 lines
* fixed a machine-dependent issue on default int ('<i4' on OS X, '<i8' on linux) ?
* fixed an machine-dependent issue on argsort ?
................
r6333 | cdavid | 2009-01-24 17:02:14 +0900 (Sat, 24 Jan 2009) | 1 line
Fix compilation error on 2.4.
................
r6334 | pierregm | 2009-01-27 06:04:26 +0900 (Tue, 27 Jan 2009) | 7 lines
* _iotools.StringConverter :
- add a _checked attribute to indicate whether the converter has been upgraded or not.
- switched the default value for bool to False
* io.genfromtxt:
- fixed for the case where a whole column is masked: switch to bool or the common dtype (if needed)
................
r6335 | pierregm | 2009-01-27 11:46:26 +0900 (Tue, 27 Jan 2009) | 1 line
* prevent MaskedBinaryOperation and DomainedBinaryOperation to shrink the mask of the output when at least one of the inputs has a mask full of False
................
r6336 | matthew.brett@gmail.com | 2009-01-30 09:26:44 +0900 (Fri, 30 Jan 2009) | 1 line
New docstrings for byteorder and newbyteorder()
................
r6337 | pierregm | 2009-02-02 14:20:17 +0900 (Mon, 02 Feb 2009) | 2 lines
* Added a 'autoconvert' option to stack_arrays.
* Fixed 'stack_arrays' to work with fields with titles.
................
r6338 | pierregm | 2009-02-04 02:11:44 +0900 (Wed, 04 Feb 2009) | 1 line
* Make sure that StringConverter.update sets the type to object if it can't define it.
................
r6339 | pierregm | 2009-02-05 05:52:36 +0900 (Thu, 05 Feb 2009) | 2 lines
* test__iotools : prevent test_upgrademapper if dateutil is not installed
* MaskedArray.__rmul__ : switch to multiply(self, other)
................
r6340 | pierregm | 2009-02-05 06:53:05 +0900 (Thu, 05 Feb 2009) | 1 line
test_upgrademapper : got rid of the dateutil import
................
r6341 | pierregm | 2009-02-05 13:31:51 +0900 (Thu, 05 Feb 2009) | 2 lines
* genfromtxt : Fixed when a dtype involving objects is explicitly given. Raise a NotImplementedError if the dtype is nested.
* _iotools : make sure StringConverter gets properly initiated when a function returning a np.object is used as input parameter.
................
r6342 | alan.mcintyre | 2009-02-06 05:11:40 +0900 (Fri, 06 Feb 2009) | 6 lines
Issue #957:
- Fix problems with test decorators when used on test generators.
- The skip/fail arguments for skipif and knownfailureif can now be
either a bool or a callable that returns a bool.
- Added tests for the test decorators.
................
r6343 | ptvirtan | 2009-02-06 09:27:08 +0900 (Fri, 06 Feb 2009) | 1 line
doc/numpydoc: work better together with Sphinx's config option
................
r6344 | ptvirtan | 2009-02-06 09:51:41 +0900 (Fri, 06 Feb 2009) | 1 line
doc: Move maskedarray docs upward in TOC
................
r6345 | oliphant | 2009-02-06 15:25:50 +0900 (Fri, 06 Feb 2009) | 1 line
Avoid re-creating the sequence when there is only one field in the regular expression.
................
r6346 | oliphant | 2009-02-06 15:31:11 +0900 (Fri, 06 Feb 2009) | 1 line
Removed an unneccessary return statement in a unit test.
................
r6347 | pearu | 2009-02-06 23:36:58 +0900 (Fri, 06 Feb 2009) | 1 line
Fix a bug: python system_info.py failed because _pkg_config_info defined section to be None.
................
r6348 | pearu | 2009-02-06 23:38:57 +0900 (Fri, 06 Feb 2009) | 1 line
Fix another bug, see last commit.
................
r6349 | pierregm | 2009-02-07 18:19:12 +0900 (Sat, 07 Feb 2009) | 2 lines
MaskedArray.resize : systematically raise a TypeError exception, as a masked array never owns its data
MaskedIterator : fixed to allow .flat on masked matrices
................
r6350 | pierregm | 2009-02-08 03:51:31 +0900 (Sun, 08 Feb 2009) | 1 line
................
r6351 | ptvirtan | 2009-02-10 05:18:08 +0900 (Tue, 10 Feb 2009) | 1 line
Fix #955: fix errobj leak in scalarmath floating point error handling
................
r6352 | pierregm | 2009-02-10 09:42:40 +0900 (Tue, 10 Feb 2009) | 1 line
* prevent modifications to the mask to be back-propagated w/ __array_wrap__
................
r6354 | cdavid | 2009-02-10 19:44:01 +0900 (Tue, 10 Feb 2009) | 1 line
Fix hyphen (patch from debian package).
................
r6356 | pierregm | 2009-02-11 10:51:28 +0900 (Wed, 11 Feb 2009) | 1 line
* MaskedArray.__array_wrap__ : forces the domain (if any) to a ndarray (fill with True)
................
r6358 | oliphant | 2009-02-12 13:22:03 +0900 (Thu, 12 Feb 2009) | 1 line
Add multiple-field access by making a copy of the array and filling with the selected fields.
................
r6359 | stefan | 2009-02-12 14:44:07 +0900 (Thu, 12 Feb 2009) | 2 lines
Trust user's specification of MACOSX_DEPLOYMENT_TARGET [patch by Brian
Granger].
................
r6360 | cdavid | 2009-02-14 23:54:26 +0900 (Sat, 14 Feb 2009) | 1 line
Merge fix_float_format branch into the trunk.
................
r6361 | cdavid | 2009-02-15 00:02:39 +0900 (Sun, 15 Feb 2009) | 1 line
Fix typo in multiarray tests.
................
r6362 | cdavid | 2009-02-15 00:03:22 +0900 (Sun, 15 Feb 2009) | 1 line
Remove leftover in TestIO.
................
r6363 | cdavid | 2009-02-15 02:03:51 +0900 (Sun, 15 Feb 2009) | 1 line
Include C99 math compatbility layer in multiarray - isnan and co needed by numpyos.c
................
r6364 | ptvirtan | 2009-02-15 07:09:26 +0900 (Sun, 15 Feb 2009) | 1 line
More add_newdocs entries, and make add_newdoc capable of adding docs also to normal Python objects.
................
r6365 | ptvirtan | 2009-02-15 07:10:24 +0900 (Sun, 15 Feb 2009) | 1 line
Move (un)packbits docstrings to add_newdocs.py. Fix typos.
................
r6366 | ptvirtan | 2009-02-15 07:11:19 +0900 (Sun, 15 Feb 2009) | 1 line
Document constants in numpy.doc.constants
................
r6367 | ptvirtan | 2009-02-15 07:38:32 +0900 (Sun, 15 Feb 2009) | 1 line
Move numpy.lib __doc__ back to info.py; was moved to __init__.py by mistake.
................
r6368 | pierregm | 2009-02-15 07:42:29 +0900 (Sun, 15 Feb 2009) | 1 line
* genfromtxt : fixed case when using explicit converters and explicit dtype.
................
r6370 | ptvirtan | 2009-02-16 00:44:47 +0900 (Mon, 16 Feb 2009) | 1 line
sphinxext: clean up plot directive, and merge some features from matplotlib
................
r6371 | charris | 2009-02-18 06:19:10 +0900 (Wed, 18 Feb 2009) | 2 lines
Coding style cleanups.
................
r6372 | cdavid | 2009-02-18 14:06:57 +0900 (Wed, 18 Feb 2009) | 1 line
Fix some const issues in NumPyOs_ascii_strtod.
................
r6373 | charris | 2009-02-18 14:36:35 +0900 (Wed, 18 Feb 2009) | 1 line
Coding style cleanups.
................
r6398 | charris | 2009-02-19 05:54:52 +0900 (Thu, 19 Feb 2009) | 2 lines
Coding style cleanups.
................
r6399 | charris | 2009-02-19 09:45:14 +0900 (Thu, 19 Feb 2009) | 2 lines
Coding style cleanups.
................
r6400 | charris | 2009-02-19 13:58:23 +0900 (Thu, 19 Feb 2009) | 1 line
Coding style cleanups.
................
r6410 | cdavid | 2009-02-19 19:05:28 +0900 (Thu, 19 Feb 2009) | 1 line
Tag known failure on win32.
................
r6421 | stefan | 2009-02-20 04:28:08 +0900 (Fri, 20 Feb 2009) | 1 line
Fix tests using strptime to be Python 2.4 compatible.
................
r6422 | charris | 2009-02-20 08:25:01 +0900 (Fri, 20 Feb 2009) | 2 lines
Coding style cleanups.
................
r6423 | charris | 2009-02-20 12:40:53 +0900 (Fri, 20 Feb 2009) | 1 line
Coding style cleanups.
................
r6424 | cdavid | 2009-02-20 22:30:20 +0900 (Fri, 20 Feb 2009) | 1 line
Unhelpful message for compaq fortran compiler.
................
Diffstat (limited to 'numpy/lib')
-rw-r--r-- | numpy/lib/__init__.py | 148 | ||||
-rw-r--r-- | numpy/lib/_iotools.py | 493 | ||||
-rw-r--r-- | numpy/lib/arraysetops.py | 10 | ||||
-rw-r--r-- | numpy/lib/function_base.py | 77 | ||||
-rw-r--r-- | numpy/lib/getlimits.py | 48 | ||||
-rw-r--r-- | numpy/lib/index_tricks.py | 2 | ||||
-rw-r--r-- | numpy/lib/info.py | 251 | ||||
-rw-r--r-- | numpy/lib/io.py | 521 | ||||
-rw-r--r-- | numpy/lib/recfunctions.py | 942 | ||||
-rw-r--r-- | numpy/lib/src/_compiled_base.c | 84 | ||||
-rw-r--r-- | numpy/lib/tests/test__iotools.py | 167 | ||||
-rw-r--r-- | numpy/lib/tests/test_function_base.py | 38 | ||||
-rw-r--r-- | numpy/lib/tests/test_getlimits.py | 4 | ||||
-rw-r--r-- | numpy/lib/tests/test_io.py | 476 | ||||
-rw-r--r-- | numpy/lib/tests/test_recfunctions.py | 606 | ||||
-rw-r--r-- | numpy/lib/utils.py | 8 |
16 files changed, 3466 insertions, 409 deletions
diff --git a/numpy/lib/__init__.py b/numpy/lib/__init__.py index 296ca7135..07f6d5c27 100644 --- a/numpy/lib/__init__.py +++ b/numpy/lib/__init__.py @@ -1,151 +1,3 @@ -""" -Basic functions used by several sub-packages and -useful to have in the main name-space. - -Type Handling -------------- -================ =================== -iscomplexobj Test for complex object, scalar result -isrealobj Test for real object, scalar result -iscomplex Test for complex elements, array result -isreal Test for real elements, array result -imag Imaginary part -real Real part -real_if_close Turns complex number with tiny imaginary part to real -isneginf Tests for negative infinity, array result -isposinf Tests for positive infinity, array result -isnan Tests for nans, array result -isinf Tests for infinity, array result -isfinite Tests for finite numbers, array result -isscalar True if argument is a scalar -nan_to_num Replaces NaN's with 0 and infinities with large numbers -cast Dictionary of functions to force cast to each type -common_type Determine the minimum common type code for a group - of arrays -mintypecode Return minimal allowed common typecode. -================ =================== - -Index Tricks ------------- -================ =================== -mgrid Method which allows easy construction of N-d - 'mesh-grids' -``r_`` Append and construct arrays: turns slice objects into - ranges and concatenates them, for 2d arrays appends rows. -index_exp Konrad Hinsen's index_expression class instance which - can be useful for building complicated slicing syntax. -================ =================== - -Useful Functions ----------------- -================ =================== -select Extension of where to multiple conditions and choices -extract Extract 1d array from flattened array according to mask -insert Insert 1d array of values into Nd array according to mask -linspace Evenly spaced samples in linear space -logspace Evenly spaced samples in logarithmic space -fix Round x to nearest integer towards zero -mod Modulo mod(x,y) = x % y except keeps sign of y -amax Array maximum along axis -amin Array minimum along axis -ptp Array max-min along axis -cumsum Cumulative sum along axis -prod Product of elements along axis -cumprod Cumluative product along axis -diff Discrete differences along axis -angle Returns angle of complex argument -unwrap Unwrap phase along given axis (1-d algorithm) -sort_complex Sort a complex-array (based on real, then imaginary) -trim_zeros Trim the leading and trailing zeros from 1D array. -vectorize A class that wraps a Python function taking scalar - arguments into a generalized function which can handle - arrays of arguments using the broadcast rules of - numerix Python. -================ =================== - -Shape Manipulation ------------------- -================ =================== -squeeze Return a with length-one dimensions removed. -atleast_1d Force arrays to be > 1D -atleast_2d Force arrays to be > 2D -atleast_3d Force arrays to be > 3D -vstack Stack arrays vertically (row on row) -hstack Stack arrays horizontally (column on column) -column_stack Stack 1D arrays as columns into 2D array -dstack Stack arrays depthwise (along third dimension) -split Divide array into a list of sub-arrays -hsplit Split into columns -vsplit Split into rows -dsplit Split along third dimension -================ =================== - -Matrix (2D Array) Manipulations -------------------------------- -================ =================== -fliplr 2D array with columns flipped -flipud 2D array with rows flipped -rot90 Rotate a 2D array a multiple of 90 degrees -eye Return a 2D array with ones down a given diagonal -diag Construct a 2D array from a vector, or return a given - diagonal from a 2D array. -mat Construct a Matrix -bmat Build a Matrix from blocks -================ =================== - -Polynomials ------------ -================ =================== -poly1d A one-dimensional polynomial class -poly Return polynomial coefficients from roots -roots Find roots of polynomial given coefficients -polyint Integrate polynomial -polyder Differentiate polynomial -polyadd Add polynomials -polysub Substract polynomials -polymul Multiply polynomials -polydiv Divide polynomials -polyval Evaluate polynomial at given argument -================ =================== - -Import Tricks -------------- -================ =================== -ppimport Postpone module import until trying to use it -ppimport_attr Postpone module import until trying to use its attribute -ppresolve Import postponed module and return it. -================ =================== - -Machine Arithmetics -------------------- -================ =================== -machar_single Single precision floating point arithmetic parameters -machar_double Double precision floating point arithmetic parameters -================ =================== - -Threading Tricks ----------------- -================ =================== -ParallelExec Execute commands in parallel thread. -================ =================== - -1D Array Set Operations ------------------------ -Set operations for 1D numeric arrays based on sort() function. - -================ =================== -ediff1d Array difference (auxiliary function). -unique1d Unique elements of 1D array. -intersect1d Intersection of 1D arrays with unique elements. -intersect1d_nu Intersection of 1D arrays with any elements. -setxor1d Set exclusive-or of 1D arrays with unique elements. -setmember1d Return an array of shape of ar1 containing 1 where - the elements of ar1 are in ar2 and 0 otherwise. -union1d Union of 1D arrays with unique elements. -setdiff1d Set difference of 1D arrays with unique elements. -================ =================== - -""" from info import __doc__ from numpy.version import version as __version__ diff --git a/numpy/lib/_iotools.py b/numpy/lib/_iotools.py new file mode 100644 index 000000000..23053bf4d --- /dev/null +++ b/numpy/lib/_iotools.py @@ -0,0 +1,493 @@ +""" +A collection of functions designed to help I/O with ascii file. + +""" +__docformat__ = "restructuredtext en" + +import numpy as np +import numpy.core.numeric as nx +from __builtin__ import bool, int, long, float, complex, object, unicode, str + + +def _is_string_like(obj): + """ + Check whether obj behaves like a string. + """ + try: + obj + '' + except (TypeError, ValueError): + return False + return True + + +def _to_filehandle(fname, flag='r', return_opened=False): + """ + Returns the filehandle corresponding to a string or a file. + If the string ends in '.gz', the file is automatically unzipped. + + Parameters + ---------- + fname : string, filehandle + Name of the file whose filehandle must be returned. + flag : string, optional + Flag indicating the status of the file ('r' for read, 'w' for write). + return_opened : boolean, optional + Whether to return the opening status of the file. + """ + if _is_string_like(fname): + if fname.endswith('.gz'): + import gzip + fhd = gzip.open(fname, flag) + elif fname.endswith('.bz2'): + import bz2 + fhd = bz2.BZ2File(fname) + else: + fhd = file(fname, flag) + opened = True + elif hasattr(fname, 'seek'): + fhd = fname + opened = False + else: + raise ValueError('fname must be a string or file handle') + if return_opened: + return fhd, opened + return fhd + + +def has_nested_fields(ndtype): + """ + Returns whether one or several fields of a structured array are nested. + """ + for name in ndtype.names or (): + if ndtype[name].names: + return True + return False + + +def flatten_dtype(ndtype): + """ + Unpack a structured data-type. + + """ + names = ndtype.names + if names is None: + return [ndtype] + else: + types = [] + for field in names: + (typ, _) = ndtype.fields[field] + flat_dt = flatten_dtype(typ) + types.extend(flat_dt) + return types + + +class LineSplitter: + """ + Defines a function to split a string at a given delimiter or at given places. + + Parameters + ---------- + comment : {'#', string} + Character used to mark the beginning of a comment. + delimiter : var, optional + If a string, character used to delimit consecutive fields. + If an integer or a sequence of integers, width(s) of each field. + autostrip : boolean, optional + Whether to strip each individual fields + """ + + def autostrip(self, method): + "Wrapper to strip each member of the output of `method`." + return lambda input: [_.strip() for _ in method(input)] + # + def __init__(self, delimiter=None, comments='#', autostrip=True): + self.comments = comments + # Delimiter is a character + if (delimiter is None) or _is_string_like(delimiter): + delimiter = delimiter or None + _handyman = self._delimited_splitter + # Delimiter is a list of field widths + elif hasattr(delimiter, '__iter__'): + _handyman = self._variablewidth_splitter + idx = np.cumsum([0]+list(delimiter)) + delimiter = [slice(i,j) for (i,j) in zip(idx[:-1], idx[1:])] + # Delimiter is a single integer + elif int(delimiter): + (_handyman, delimiter) = (self._fixedwidth_splitter, int(delimiter)) + else: + (_handyman, delimiter) = (self._delimited_splitter, None) + self.delimiter = delimiter + if autostrip: + self._handyman = self.autostrip(_handyman) + else: + self._handyman = _handyman + # + def _delimited_splitter(self, line): + line = line.split(self.comments)[0].strip() + if not line: + return [] + return line.split(self.delimiter) + # + def _fixedwidth_splitter(self, line): + line = line.split(self.comments)[0] + if not line: + return [] + fixed = self.delimiter + slices = [slice(i, i+fixed) for i in range(len(line))[::fixed]] + return [line[s] for s in slices] + # + def _variablewidth_splitter(self, line): + line = line.split(self.comments)[0] + if not line: + return [] + slices = self.delimiter + return [line[s] for s in slices] + # + def __call__(self, line): + return self._handyman(line) + + + +class NameValidator: + """ + Validates a list of strings to use as field names. + The strings are stripped of any non alphanumeric character, and spaces + are replaced by `_`. If the optional input parameter `case_sensitive` + is False, the strings are set to upper case. + + During instantiation, the user can define a list of names to exclude, as + well as a list of invalid characters. Names in the exclusion list + are appended a '_' character. + + Once an instance has been created, it can be called with a list of names + and a list of valid names will be created. + The `__call__` method accepts an optional keyword, `default`, that sets + the default name in case of ambiguity. By default, `default = 'f'`, so + that names will default to `f0`, `f1` + + Parameters + ---------- + excludelist : sequence, optional + A list of names to exclude. This list is appended to the default list + ['return','file','print']. Excluded names are appended an underscore: + for example, `file` would become `file_`. + deletechars : string, optional + A string combining invalid characters that must be deleted from the names. + casesensitive : {True, False, 'upper', 'lower'}, optional + If True, field names are case_sensitive. + If False or 'upper', field names are converted to upper case. + If 'lower', field names are converted to lower case. + """ + # + defaultexcludelist = ['return','file','print'] + defaultdeletechars = set("""~!@#$%^&*()-=+~\|]}[{';: /?.>,<""") + # + def __init__(self, excludelist=None, deletechars=None, case_sensitive=None): + # + if excludelist is None: + excludelist = [] + excludelist.extend(self.defaultexcludelist) + self.excludelist = excludelist + # + if deletechars is None: + delete = self.defaultdeletechars + else: + delete = set(deletechars) + delete.add('"') + self.deletechars = delete + + if (case_sensitive is None) or (case_sensitive is True): + self.case_converter = lambda x: x + elif (case_sensitive is False) or ('u' in case_sensitive): + self.case_converter = lambda x: x.upper() + elif 'l' in case_sensitive: + self.case_converter = lambda x: x.lower() + else: + self.case_converter = lambda x: x + # + def validate(self, names, default='f'): + # + if names is None: + return + # + validatednames = [] + seen = dict() + # + deletechars = self.deletechars + excludelist = self.excludelist + # + case_converter = self.case_converter + # + for i, item in enumerate(names): + item = case_converter(item) + item = item.strip().replace(' ', '_') + item = ''.join([c for c in item if c not in deletechars]) + if not len(item): + item = '%s%d' % (default, i) + elif item in excludelist: + item += '_' + cnt = seen.get(item, 0) + if cnt > 0: + validatednames.append(item + '_%d' % cnt) + else: + validatednames.append(item) + seen[item] = cnt+1 + return validatednames + # + def __call__(self, names, default='f'): + return self.validate(names, default) + + + +def str2bool(value): + """ + Tries to transform a string supposed to represent a boolean to a boolean. + + Raises + ------ + ValueError + If the string is not 'True' or 'False' (case independent) + """ + value = value.upper() + if value == 'TRUE': + return True + elif value == 'FALSE': + return False + else: + raise ValueError("Invalid boolean") + + + +class StringConverter: + """ + Factory class for function transforming a string into another object (int, + float). + + After initialization, an instance can be called to transform a string + into another object. If the string is recognized as representing a missing + value, a default value is returned. + + Parameters + ---------- + dtype_or_func : {None, dtype, function}, optional + Input data type, used to define a basic function and a default value + for missing data. For example, when `dtype` is float, the :attr:`func` + attribute is set to ``float`` and the default value to `np.nan`. + Alternatively, function used to convert a string to another object. + In that later case, it is recommended to give an associated default + value as input. + default : {None, var}, optional + Value to return by default, that is, when the string to be converted + is flagged as missing. + missing_values : {sequence}, optional + Sequence of strings indicating a missing value. + locked : {boolean}, optional + Whether the StringConverter should be locked to prevent automatic + upgrade or not. + + Attributes + ---------- + func : function + Function used for the conversion + default : var + Default value to return when the input corresponds to a missing value. + type : type + Type of the output. + _status : integer + Integer representing the order of the conversion. + _mapper : sequence of tuples + Sequence of tuples (dtype, function, default value) to evaluate in order. + _locked : boolean + Whether the StringConverter is locked, thereby preventing automatic any + upgrade or not. + + """ + # + _mapper = [(nx.bool_, str2bool, False), + (nx.integer, int, -1), + (nx.floating, float, nx.nan), + (complex, complex, nx.nan+0j), + (nx.string_, str, '???')] + (_defaulttype, _defaultfunc, _defaultfill) = zip(*_mapper) + # + @classmethod + def _getsubdtype(cls, val): + """Returns the type of the dtype of the input variable.""" + return np.array(val).dtype.type + # + @classmethod + def upgrade_mapper(cls, func, default=None): + """ + Upgrade the mapper of a StringConverter by adding a new function and its + corresponding default. + + The input function (or sequence of functions) and its associated default + value (if any) is inserted in penultimate position of the mapper. + The corresponding type is estimated from the dtype of the default value. + + Parameters + ---------- + func : var + Function, or sequence of functions + + Examples + -------- + >>> import dateutil.parser + >>> import datetime + >>> dateparser = datetutil.parser.parse + >>> defaultdate = datetime.date(2000, 1, 1) + >>> StringConverter.upgrade_mapper(dateparser, default=defaultdate) + """ + # Func is a single functions + if hasattr(func, '__call__'): + cls._mapper.insert(-1, (cls._getsubdtype(default), func, default)) + return + elif hasattr(func, '__iter__'): + if isinstance(func[0], (tuple, list)): + for _ in func: + cls._mapper.insert(-1, _) + return + if default is None: + default = [None] * len(func) + else: + default = list(default) + default.append([None] * (len(func)-len(default))) + for (fct, dft) in zip(func, default): + cls._mapper.insert(-1, (cls._getsubdtype(dft), fct, dft)) + # + def __init__(self, dtype_or_func=None, default=None, missing_values=None, + locked=False): + # Defines a lock for upgrade + self._locked = bool(locked) + # No input dtype: minimal initialization + if dtype_or_func is None: + self.func = str2bool + self._status = 0 + self.default = default or False + ttype = np.bool + else: + # Is the input a np.dtype ? + try: + self.func = None + ttype = np.dtype(dtype_or_func).type + except TypeError: + # dtype_or_func must be a function, then + if not hasattr(dtype_or_func, '__call__'): + errmsg = "The input argument `dtype` is neither a function"\ + " or a dtype (got '%s' instead)" + raise TypeError(errmsg % type(dtype_or_func)) + # Set the function + self.func = dtype_or_func + # If we don't have a default, try to guess it or set it to None + if default is None: + try: + default = self.func('0') + except ValueError: + default = None + ttype = self._getsubdtype(default) + # Set the status according to the dtype + _status = -1 + for (i, (deftype, func, default_def)) in enumerate(self._mapper): + if np.issubdtype(ttype, deftype): + _status = i + self.default = default or default_def + break + if _status == -1: + # We never found a match in the _mapper... + _status = 0 + self.default = default + self._status = _status + # If the input was a dtype, set the function to the last we saw + if self.func is None: + self.func = func + # If the status is 1 (int), change the function to smthg more robust + if self.func == self._mapper[1][1]: + self.func = lambda x : int(float(x)) + # Store the list of strings corresponding to missing values. + if missing_values is None: + self.missing_values = set(['']) + else: + self.missing_values = set(list(missing_values) + ['']) + # + self._callingfunction = self._strict_call + self.type = ttype + self._checked = False + # + def _loose_call(self, value): + try: + return self.func(value) + except ValueError: + return self.default + # + def _strict_call(self, value): + try: + return self.func(value) + except ValueError: + if value.strip() in self.missing_values: + if not self._status: + self._checked = False + return self.default + raise ValueError("Cannot convert string '%s'" % value) + # + def __call__(self, value): + return self._callingfunction(value) + # + def upgrade(self, value): + """ + Tries to find the best converter for `value`, by testing different + converters in order. + The order in which the converters are tested is read from the + :attr:`_status` attribute of the instance. + """ + self._checked = True + try: + self._strict_call(value) + except ValueError: + # Raise an exception if we locked the converter... + if self._locked: + raise ValueError("Converter is locked and cannot be upgraded") + _statusmax = len(self._mapper) + # Complains if we try to upgrade by the maximum + if self._status == _statusmax: + raise ValueError("Could not find a valid conversion function") + elif self._status < _statusmax - 1: + self._status += 1 + (self.type, self.func, self.default) = self._mapper[self._status] + self.upgrade(value) + # + def update(self, func, default=None, missing_values='', locked=False): + """ + Sets the :attr:`func` and :attr:`default` attributes directly. + + Parameters + ---------- + func : function + Conversion function. + default : {var}, optional + Default value to return when a missing value is encountered. + missing_values : {var}, optional + Sequence of strings representing missing values. + locked : {False, True}, optional + Whether the status should be locked to prevent automatic upgrade. + """ + self.func = func + self._locked = locked + # Don't reset the default to None if we can avoid it + if default is not None: + self.default = default + # Add the missing values to the existing set + if missing_values is not None: + if _is_string_like(missing_values): + self.missing_values.add(missing_values) + elif hasattr(missing_values, '__iter__'): + for val in missing_values: + self.missing_values.add(val) + else: + self.missing_values = [] + # Update the type + try: + tester = func('0') + except ValueError: + tester = None + self.type = self._getsubdtype(tester) + diff --git a/numpy/lib/arraysetops.py b/numpy/lib/arraysetops.py index 49a1d3e89..f023f6027 100644 --- a/numpy/lib/arraysetops.py +++ b/numpy/lib/arraysetops.py @@ -52,13 +52,19 @@ def ediff1d(ary, to_end=None, to_begin=None): If provided, this number will be taked onto the beginning of the returned differences. + Notes + ----- + When applied to masked arrays, this function drops the mask information + if the `to_begin` and/or `to_end` parameters are used + + Returns ------- ed : array The differences. Loosely, this will be (ary[1:] - ary[:-1]). """ - ary = np.asarray(ary).flat + ary = np.asanyarray(ary).flat ed = ary[1:] - ary[:-1] arrays = [ed] if to_begin is not None: @@ -132,7 +138,7 @@ def unique1d(ar1, return_index=False, return_inverse=False): "the output was (indices, unique_arr), but " "has now been reversed to be more consistent.") - ar = np.asarray(ar1).flatten() + ar = np.asanyarray(ar1).flatten() if ar.size == 0: if return_inverse and return_index: return ar, np.empty(0, np.bool), np.empty(0, np.bool) diff --git a/numpy/lib/function_base.py b/numpy/lib/function_base.py index 8bf20d3fb..283e3faff 100644 --- a/numpy/lib/function_base.py +++ b/numpy/lib/function_base.py @@ -228,10 +228,10 @@ def histogram(a, bins=10, range=None, normed=False, weights=None, new=None): * None : the new behaviour is used, no warning is printed. * True : the new behaviour is used and a warning is raised about the future removal of the `new` keyword. - * False : the old behaviour is used and a DeprecationWarning + * False : the old behaviour is used and a DeprecationWarning is raised. - As of NumPy 1.3, this keyword should not be used explicitly since it - will disappear in NumPy 1.4. + As of NumPy 1.3, this keyword should not be used explicitly since it + will disappear in NumPy 1.4. Returns ------- @@ -267,9 +267,9 @@ def histogram(a, bins=10, range=None, normed=False, weights=None, new=None): # Old behavior if new == False: warnings.warn(""" - The histogram semantics being used is now deprecated and - will disappear in NumPy 1.4. Please update your code to - use the default semantics. + The histogram semantics being used is now deprecated and + will disappear in NumPy 1.4. Please update your code to + use the default semantics. """, DeprecationWarning) a = asarray(a).ravel() @@ -320,8 +320,8 @@ def histogram(a, bins=10, range=None, normed=False, weights=None, new=None): elif new in [True, None]: if new is True: warnings.warn(""" - The new semantics of histogram is now the default and the `new` - keyword will be removed in NumPy 1.4. + The new semantics of histogram is now the default and the `new` + keyword will be removed in NumPy 1.4. """, Warning) a = asarray(a) if weights is not None: @@ -1073,53 +1073,6 @@ def diff(a, n=1, axis=-1): else: return a[slice1]-a[slice2] -try: - add_docstring(digitize, -r"""digitize(x,bins) - -Return the index of the bin to which each value of x belongs. - -Each index i returned is such that bins[i-1] <= x < bins[i] if -bins is monotonically increasing, or bins [i-1] > x >= bins[i] if -bins is monotonically decreasing. - -Beyond the bounds of the bins 0 or len(bins) is returned as appropriate. - -""") -except RuntimeError: - pass - -try: - add_docstring(bincount, -r"""bincount(x,weights=None) - -Return the number of occurrences of each value in x. - -x must be a list of non-negative integers. The output, b[i], -represents the number of times that i is found in x. If weights -is specified, every occurrence of i at a position p contributes -weights[p] instead of 1. - -See also: histogram, digitize, unique. - -""") -except RuntimeError: - pass - -try: - add_docstring(add_docstring, -r"""docstring(obj, docstring) - -Add a docstring to a built-in obj if possible. -If the obj already has a docstring raise a RuntimeError -If this routine does not know how to add a docstring to the object -raise a TypeError - -""") -except RuntimeError: - pass - - def interp(x, xp, fp, left=None, right=None): """ One-dimensional linear interpolation. @@ -2818,9 +2771,9 @@ def trapz(y, x=None, dx=1.0, axis=-1): y : array_like Input array to integrate. x : array_like, optional - If `x` is None, then spacing between all `y` elements is 1. + If `x` is None, then spacing between all `y` elements is `dx`. dx : scalar, optional - If `x` is None, spacing given by `dx` is assumed. + If `x` is None, spacing given by `dx` is assumed. Default is 1. axis : int, optional Specify the axis. @@ -2836,7 +2789,15 @@ def trapz(y, x=None, dx=1.0, axis=-1): if x is None: d = dx else: - d = diff(x,axis=axis) + x = asarray(x) + if x.ndim == 1: + d = diff(x) + # reshape to correct shape + shape = [1]*y.ndim + shape[axis] = d.shape[0] + d = d.reshape(shape) + else: + d = diff(x, axis=axis) nd = len(y.shape) slice1 = [slice(None)]*nd slice2 = [slice(None)]*nd diff --git a/numpy/lib/getlimits.py b/numpy/lib/getlimits.py index bc5fbbf5e..ccb0e2b3d 100644 --- a/numpy/lib/getlimits.py +++ b/numpy/lib/getlimits.py @@ -88,6 +88,12 @@ class finfo(object): _finfo_cache = {} def __new__(cls, dtype): + try: + dtype = np.dtype(dtype) + except TypeError: + # In case a float instance was given + dtype = np.dtype(type(dtype)) + obj = cls._finfo_cache.get(dtype,None) if obj is not None: return obj @@ -115,7 +121,7 @@ class finfo(object): return obj def _init(self, dtype): - self.dtype = dtype + self.dtype = np.dtype(dtype) if dtype is ntypes.double: itype = ntypes.int64 fmt = '%24.16e' @@ -149,23 +155,23 @@ class finfo(object): self.nexp = machar.iexp self.nmant = machar.it self.machar = machar - self._str_tiny = machar._str_xmin - self._str_max = machar._str_xmax - self._str_epsneg = machar._str_epsneg - self._str_eps = machar._str_eps - self._str_resolution = machar._str_resolution + self._str_tiny = machar._str_xmin.strip() + self._str_max = machar._str_xmax.strip() + self._str_epsneg = machar._str_epsneg.strip() + self._str_eps = machar._str_eps.strip() + self._str_resolution = machar._str_resolution.strip() return self def __str__(self): return '''\ Machine parameters for %(dtype)s --------------------------------------------------------------------- -precision=%(precision)3s resolution=%(_str_resolution)s -machep=%(machep)6s eps= %(_str_eps)s -negep =%(negep)6s epsneg= %(_str_epsneg)s -minexp=%(minexp)6s tiny= %(_str_tiny)s -maxexp=%(maxexp)6s max= %(_str_max)s -nexp =%(nexp)6s min= -max +precision=%(precision)3s resolution= %(_str_resolution)s +machep=%(machep)6s eps= %(_str_eps)s +negep =%(negep)6s epsneg= %(_str_epsneg)s +minexp=%(minexp)6s tiny= %(_str_tiny)s +maxexp=%(maxexp)6s max= %(_str_max)s +nexp =%(nexp)6s min= -max --------------------------------------------------------------------- ''' % self.__dict__ @@ -220,8 +226,11 @@ class iinfo: _min_vals = {} _max_vals = {} - def __init__(self, type): - self.dtype = np.dtype(type) + def __init__(self, int_type): + try: + self.dtype = np.dtype(int_type) + except TypeError: + self.dtype = np.dtype(type(int_type)) self.kind = self.dtype.kind self.bits = self.dtype.itemsize * 8 self.key = "%s%d" % (self.kind, self.bits) @@ -256,6 +265,17 @@ class iinfo: max = property(max) + def __str__(self): + """String representation.""" + return '''\ +Machine parameters for %(dtype)s +--------------------------------------------------------------------- +min = %(min)s +max = %(max)s +--------------------------------------------------------------------- +''' % {'dtype': self.dtype, 'min': self.min, 'max': self.max} + + if __name__ == '__main__': f = finfo(ntypes.single) print 'single epsilon:',f.eps diff --git a/numpy/lib/index_tricks.py b/numpy/lib/index_tricks.py index 3021635dc..fcd3909af 100644 --- a/numpy/lib/index_tricks.py +++ b/numpy/lib/index_tricks.py @@ -212,6 +212,8 @@ class nd_grid(object): mgrid = nd_grid(sparse=False) ogrid = nd_grid(sparse=True) +mgrid.__doc__ = None # set in numpy.add_newdocs +ogrid.__doc__ = None # set in numpy.add_newdocs class AxisConcatenator(object): """Translates slice objects to concatenation along an axis. diff --git a/numpy/lib/info.py b/numpy/lib/info.py index 0944fa9b5..f93234d57 100644 --- a/numpy/lib/info.py +++ b/numpy/lib/info.py @@ -1,134 +1,149 @@ -__doc_title__ = """Basic functions used by several sub-packages and -useful to have in the main name-space.""" -__doc__ = __doc_title__ + """ - -Type handling -============== -iscomplexobj -- Test for complex object, scalar result -isrealobj -- Test for real object, scalar result -iscomplex -- Test for complex elements, array result -isreal -- Test for real elements, array result -imag -- Imaginary part -real -- Real part -real_if_close -- Turns complex number with tiny imaginary part to real -isneginf -- Tests for negative infinity ---| -isposinf -- Tests for positive infinity | -isnan -- Tests for nans |---- array results -isinf -- Tests for infinity | -isfinite -- Tests for finite numbers ---| -isscalar -- True if argument is a scalar -nan_to_num -- Replaces NaN's with 0 and infinities with large numbers -cast -- Dictionary of functions to force cast to each type -common_type -- Determine the 'minimum common type code' for a group - of arrays -mintypecode -- Return minimal allowed common typecode. +""" +Basic functions used by several sub-packages and +useful to have in the main name-space. -Index tricks -================== -mgrid -- Method which allows easy construction of N-d 'mesh-grids' -r_ -- Append and construct arrays: turns slice objects into - ranges and concatenates them, for 2d arrays appends - rows. -index_exp -- Konrad Hinsen's index_expression class instance which - can be useful for building complicated slicing syntax. +Type Handling +------------- +================ =================== +iscomplexobj Test for complex object, scalar result +isrealobj Test for real object, scalar result +iscomplex Test for complex elements, array result +isreal Test for real elements, array result +imag Imaginary part +real Real part +real_if_close Turns complex number with tiny imaginary part to real +isneginf Tests for negative infinity, array result +isposinf Tests for positive infinity, array result +isnan Tests for nans, array result +isinf Tests for infinity, array result +isfinite Tests for finite numbers, array result +isscalar True if argument is a scalar +nan_to_num Replaces NaN's with 0 and infinities with large numbers +cast Dictionary of functions to force cast to each type +common_type Determine the minimum common type code for a group + of arrays +mintypecode Return minimal allowed common typecode. +================ =================== -Useful functions -================== -select -- Extension of where to multiple conditions and choices -extract -- Extract 1d array from flattened array according to mask -insert -- Insert 1d array of values into Nd array according to mask -linspace -- Evenly spaced samples in linear space -logspace -- Evenly spaced samples in logarithmic space -fix -- Round x to nearest integer towards zero -mod -- Modulo mod(x,y) = x % y except keeps sign of y -amax -- Array maximum along axis -amin -- Array minimum along axis -ptp -- Array max-min along axis -cumsum -- Cumulative sum along axis -prod -- Product of elements along axis -cumprod -- Cumluative product along axis -diff -- Discrete differences along axis -angle -- Returns angle of complex argument -unwrap -- Unwrap phase along given axis (1-d algorithm) -sort_complex -- Sort a complex-array (based on real, then imaginary) -trim_zeros -- trim the leading and trailing zeros from 1D array. +Index Tricks +------------ +================ =================== +mgrid Method which allows easy construction of N-d + 'mesh-grids' +``r_`` Append and construct arrays: turns slice objects into + ranges and concatenates them, for 2d arrays appends rows. +index_exp Konrad Hinsen's index_expression class instance which + can be useful for building complicated slicing syntax. +================ =================== -vectorize -- a class that wraps a Python function taking scalar - arguments into a generalized function which - can handle arrays of arguments using the broadcast - rules of numerix Python. +Useful Functions +---------------- +================ =================== +select Extension of where to multiple conditions and choices +extract Extract 1d array from flattened array according to mask +insert Insert 1d array of values into Nd array according to mask +linspace Evenly spaced samples in linear space +logspace Evenly spaced samples in logarithmic space +fix Round x to nearest integer towards zero +mod Modulo mod(x,y) = x % y except keeps sign of y +amax Array maximum along axis +amin Array minimum along axis +ptp Array max-min along axis +cumsum Cumulative sum along axis +prod Product of elements along axis +cumprod Cumluative product along axis +diff Discrete differences along axis +angle Returns angle of complex argument +unwrap Unwrap phase along given axis (1-d algorithm) +sort_complex Sort a complex-array (based on real, then imaginary) +trim_zeros Trim the leading and trailing zeros from 1D array. +vectorize A class that wraps a Python function taking scalar + arguments into a generalized function which can handle + arrays of arguments using the broadcast rules of + numerix Python. +================ =================== -Shape manipulation -=================== -squeeze -- Return a with length-one dimensions removed. -atleast_1d -- Force arrays to be > 1D -atleast_2d -- Force arrays to be > 2D -atleast_3d -- Force arrays to be > 3D -vstack -- Stack arrays vertically (row on row) -hstack -- Stack arrays horizontally (column on column) -column_stack -- Stack 1D arrays as columns into 2D array -dstack -- Stack arrays depthwise (along third dimension) -split -- Divide array into a list of sub-arrays -hsplit -- Split into columns -vsplit -- Split into rows -dsplit -- Split along third dimension +Shape Manipulation +------------------ +================ =================== +squeeze Return a with length-one dimensions removed. +atleast_1d Force arrays to be > 1D +atleast_2d Force arrays to be > 2D +atleast_3d Force arrays to be > 3D +vstack Stack arrays vertically (row on row) +hstack Stack arrays horizontally (column on column) +column_stack Stack 1D arrays as columns into 2D array +dstack Stack arrays depthwise (along third dimension) +split Divide array into a list of sub-arrays +hsplit Split into columns +vsplit Split into rows +dsplit Split along third dimension +================ =================== -Matrix (2d array) manipluations -=============================== -fliplr -- 2D array with columns flipped -flipud -- 2D array with rows flipped -rot90 -- Rotate a 2D array a multiple of 90 degrees -eye -- Return a 2D array with ones down a given diagonal -diag -- Construct a 2D array from a vector, or return a given - diagonal from a 2D array. -mat -- Construct a Matrix -bmat -- Build a Matrix from blocks +Matrix (2D Array) Manipulations +------------------------------- +================ =================== +fliplr 2D array with columns flipped +flipud 2D array with rows flipped +rot90 Rotate a 2D array a multiple of 90 degrees +eye Return a 2D array with ones down a given diagonal +diag Construct a 2D array from a vector, or return a given + diagonal from a 2D array. +mat Construct a Matrix +bmat Build a Matrix from blocks +================ =================== Polynomials -============ -poly1d -- A one-dimensional polynomial class - -poly -- Return polynomial coefficients from roots -roots -- Find roots of polynomial given coefficients -polyint -- Integrate polynomial -polyder -- Differentiate polynomial -polyadd -- Add polynomials -polysub -- Substract polynomials -polymul -- Multiply polynomials -polydiv -- Divide polynomials -polyval -- Evaluate polynomial at given argument +----------- +================ =================== +poly1d A one-dimensional polynomial class +poly Return polynomial coefficients from roots +roots Find roots of polynomial given coefficients +polyint Integrate polynomial +polyder Differentiate polynomial +polyadd Add polynomials +polysub Substract polynomials +polymul Multiply polynomials +polydiv Divide polynomials +polyval Evaluate polynomial at given argument +================ =================== -Import tricks -============= -ppimport -- Postpone module import until trying to use it -ppimport_attr -- Postpone module import until trying to use its - attribute -ppresolve -- Import postponed module and return it. +Import Tricks +------------- +================ =================== +ppimport Postpone module import until trying to use it +ppimport_attr Postpone module import until trying to use its attribute +ppresolve Import postponed module and return it. +================ =================== -Machine arithmetics -=================== -machar_single -- MachAr instance storing the parameters of system - single precision floating point arithmetics -machar_double -- MachAr instance storing the parameters of system - double precision floating point arithmetics +Machine Arithmetics +------------------- +================ =================== +machar_single Single precision floating point arithmetic parameters +machar_double Double precision floating point arithmetic parameters +================ =================== -Threading tricks -================ -ParallelExec -- Execute commands in parallel thread. +Threading Tricks +---------------- +================ =================== +ParallelExec Execute commands in parallel thread. +================ =================== -1D array set operations -======================= +1D Array Set Operations +----------------------- Set operations for 1D numeric arrays based on sort() function. -ediff1d -- Array difference (auxiliary function). -unique1d -- Unique elements of 1D array. -intersect1d -- Intersection of 1D arrays with unique elements. -intersect1d_nu -- Intersection of 1D arrays with any elements. -setxor1d -- Set exclusive-or of 1D arrays with unique elements. -setmember1d -- Return an array of shape of ar1 containing 1 where - the elements of ar1 are in ar2 and 0 otherwise. -union1d -- Union of 1D arrays with unique elements. -setdiff1d -- Set difference of 1D arrays with unique elements. +================ =================== +ediff1d Array difference (auxiliary function). +unique1d Unique elements of 1D array. +intersect1d Intersection of 1D arrays with unique elements. +intersect1d_nu Intersection of 1D arrays with any elements. +setxor1d Set exclusive-or of 1D arrays with unique elements. +setmember1d Return an array of shape of ar1 containing 1 where + the elements of ar1 are in ar2 and 0 otherwise. +union1d Union of 1D arrays with unique elements. +setdiff1d Set difference of 1D arrays with unique elements. +================ =================== """ diff --git a/numpy/lib/io.py b/numpy/lib/io.py index e9a012db1..12765e17c 100644 --- a/numpy/lib/io.py +++ b/numpy/lib/io.py @@ -1,4 +1,5 @@ __all__ = ['savetxt', 'loadtxt', + 'genfromtxt', 'ndfromtxt', 'mafromtxt', 'recfromtxt', 'recfromcsv', 'load', 'loads', 'save', 'savez', 'packbits', 'unpackbits', @@ -15,7 +16,11 @@ from cPickle import load as _cload, loads from _datasource import DataSource from _compiled_base import packbits, unpackbits +from _iotools import LineSplitter, NameValidator, StringConverter, \ + _is_string_like, has_nested_fields, flatten_dtype + _file = file +_string_like = _is_string_like class BagObj(object): """A simple class that converts attribute lookups to @@ -264,10 +269,6 @@ def _getconv(dtype): return str -def _string_like(obj): - try: obj + '' - except (TypeError, ValueError): return 0 - return 1 def loadtxt(fname, dtype=float, comments='#', delimiter=None, converters=None, skiprows=0, usecols=None, unpack=False): @@ -342,7 +343,7 @@ def loadtxt(fname, dtype=float, comments='#', delimiter=None, converters=None, if usecols is not None: usecols = list(usecols) - if _string_like(fname): + if _is_string_like(fname): if fname.endswith('.gz'): import gzip fh = gzip.open(fname) @@ -520,7 +521,7 @@ def savetxt(fname, X, fmt='%.18e',delimiter=' '): """ - if _string_like(fname): + if _is_string_like(fname): if fname.endswith('.gz'): import gzip fh = gzip.open(fname,'wb') @@ -603,8 +604,508 @@ def fromregex(file, regexp, dtype): seq = regexp.findall(file.read()) if seq and not isinstance(seq[0], tuple): - # make sure np.array doesn't interpret strings as binary data - # by always producing a list of tuples - seq = [(x,) for x in seq] - output = np.array(seq, dtype=dtype) + # Only one group is in the regexp. + # Create the new array as a single data-type and then + # re-interpret as a single-field structured array. + newdtype = np.dtype(dtype[dtype.names[0]]) + output = np.array(seq, dtype=newdtype) + output.dtype = dtype + else: + output = np.array(seq, dtype=dtype) + return output + + + + +#####-------------------------------------------------------------------------- +#---- --- ASCII functions --- +#####-------------------------------------------------------------------------- + + + +def genfromtxt(fname, dtype=float, comments='#', delimiter=None, skiprows=0, + converters=None, missing='', missing_values=None, usecols=None, + names=None, excludelist=None, deletechars=None, + case_sensitive=True, unpack=None, usemask=False, loose=True): + """ + Load data from a text file. + + Each line past the first `skiprows` ones is split at the `delimiter` + character, and characters following the `comments` character are discarded. + + + + Parameters + ---------- + fname : file or string + 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 flexible data-type, + the resulting array will be 1-dimensional, and each row will be + interpreted as an element of the array. In this case, the number + of columns used must match the number of fields in the data-type, + and the names of each field will be set by the corresponding name + of the dtype. + If None, the dtypes will be determined by the contents of each + column, individually. + comments : {string}, optional + The character used to indicate the start of a comment. + All the characters occurring on a line after a comment are discarded + delimiter : {string}, optional + The string used to separate values. By default, any consecutive + whitespace act as delimiter. + skiprows : {int}, optional + Numbers of lines to skip at the beginning of the file. + converters : {None, dictionary}, optional + A dictionary mapping column number to a function that will convert + values in the column to a number. Converters can also be used to + provide a default value for missing data: + ``converters = {3: lambda s: float(s or 0)}``. + missing : {string}, optional + A string representing a missing value, irrespective of the column where + it appears (e.g., `'missing'` or `'unused'`). + missing_values : {None, dictionary}, optional + A dictionary mapping a column number to a string indicating whether the + corresponding field should be masked. + usecols : {None, sequence}, optional + Which columns to read, with 0 being the first. For example, + ``usecols = (1,4,5)`` will extract the 2nd, 5th and 6th columns. + names : {None, True, string, sequence}, optional + If `names` is True, the field names are read from the first valid line + after the first `skiprows` lines. + If `names` is a sequence or a single-string of comma-separated names, + the names will be used to define the field names in a flexible dtype. + If `names` is None, the names of the dtype fields will be used, if any. + excludelist : {sequence}, optional + A list of names to exclude. This list is appended to the default list + ['return','file','print']. Excluded names are appended an underscore: + for example, `file` would become `file_`. + deletechars : {string}, optional + A string combining invalid characters that must be deleted from the names. + case_sensitive : {True, False, 'upper', 'lower'}, optional + If True, field names are case_sensitive. + If False or 'upper', field names are converted to upper case. + If 'lower', field names are converted to lower case. + unpack : {bool}, optional + If True, the returned array is transposed, so that arguments may be + unpacked using ``x, y, z = loadtxt(...)`` + usemask : {bool}, optional + If True, returns a masked array. + If False, return a regular standard array. + + Returns + ------- + out : MaskedArray + Data read from the text file. + + Notes + -------- + * When spaces are used as delimiters, or when no delimiter has been given + as input, there should not be any missing data between two fields. + * When the variable are named (either by a flexible dtype or with `names`, + there must not be any header in the file (else a :exc:ValueError exception + is raised). + + Warnings + -------- + * Individual values are not stripped of spaces by default. + When using a custom converter, make sure the function does remove spaces. + + See Also + -------- + numpy.loadtxt : equivalent function when no data is missing. + + """ + # + if usemask: + from numpy.ma import MaskedArray, make_mask_descr + # Check the input dictionary of converters + user_converters = converters or {} + if not isinstance(user_converters, dict): + errmsg = "The input argument 'converter' should be a valid dictionary "\ + "(got '%s' instead)" + raise TypeError(errmsg % type(user_converters)) + # Check the input dictionary of missing values + user_missing_values = missing_values or {} + if not isinstance(user_missing_values, dict): + errmsg = "The input argument 'missing_values' should be a valid "\ + "dictionary (got '%s' instead)" + raise TypeError(errmsg % type(missing_values)) + defmissing = [_.strip() for _ in missing.split(',')] + [''] + + # Initialize the filehandle, the LineSplitter and the NameValidator +# fhd = _to_filehandle(fname) + if isinstance(fname, basestring): + fhd = np.lib._datasource.open(fname) + elif not hasattr(fname, 'read'): + raise TypeError("The input should be a string or a filehandle. "\ + "(got %s instead)" % type(fname)) + else: + fhd = fname + split_line = LineSplitter(delimiter=delimiter, comments=comments, + autostrip=False)._handyman + validate_names = NameValidator(excludelist=excludelist, + deletechars=deletechars, + case_sensitive=case_sensitive) + + # Get the first valid lines after the first skiprows ones + for i in xrange(skiprows): + fhd.readline() + first_values = None + while not first_values: + first_line = fhd.readline() + if first_line == '': + raise IOError('End-of-file reached before encountering data.') + if names is True: + first_values = first_line.strip().split(delimiter) + else: + first_values = split_line(first_line) + if names is True: + fval = first_values[0].strip() + if fval in comments: + del first_values[0] + + # Check the columns to use + if usecols is not None: + usecols = list(usecols) + nbcols = len(usecols or first_values) + + # Check the names and overwrite the dtype.names if needed + if dtype is not None: + dtype = np.dtype(dtype) + dtypenames = getattr(dtype, 'names', None) + if names is True: + names = validate_names([_.strip() for _ in first_values]) + first_line ='' + elif _is_string_like(names): + names = validate_names([_.strip() for _ in names.split(',')]) + elif names: + names = validate_names(names) + elif dtypenames: + dtype.names = validate_names(dtypenames) + if names and dtypenames: + dtype.names = names + + # If usecols is a list of names, convert to a list of indices + if usecols: + for (i, current) in enumerate(usecols): + if _is_string_like(current): + usecols[i] = names.index(current) + + # If user_missing_values has names as keys, transform them to indices + missing_values = {} + for (key, val) in user_missing_values.iteritems(): + # If val is a list, flatten it. In any case, add missing &'' to the list + if isinstance(val, (list, tuple)): + val = [str(_) for _ in val] + else: + val = [str(val),] + val.extend(defmissing) + if _is_string_like(key): + try: + missing_values[names.index(key)] = val + except ValueError: + pass + else: + missing_values[key] = val + + + # Initialize the default converters + if dtype is None: + # Note: we can't use a [...]*nbcols, as we would have 3 times the same + # ... converter, instead of 3 different converters. + converters = [StringConverter(None, + missing_values=missing_values.get(_, defmissing)) + for _ in range(nbcols)] + else: + flatdtypes = flatten_dtype(dtype) + # Initialize the converters + if len(flatdtypes) > 1: + # Flexible type : get a converter from each dtype + converters = [StringConverter(dt, + missing_values=missing_values.get(i, defmissing), + locked=True) + for (i, dt) in enumerate(flatdtypes)] + else: + # Set to a default converter (but w/ different missing values) + converters = [StringConverter(dtype, + missing_values=missing_values.get(_, defmissing), + locked=True) + for _ in range(nbcols)] + missing_values = [_.missing_values for _ in converters] + + # Update the converters to use the user-defined ones + uc_update = [] + for (i, conv) in user_converters.iteritems(): + # If the converter is specified by column names, use the index instead + if _is_string_like(i): + i = names.index(i) + if usecols: + try: + i = usecols.index(i) + except ValueError: + # Unused converter specified + continue + converters[i].update(conv, default=None, + missing_values=missing_values[i], + locked=True) + uc_update.append((i, conv)) + # Make sure we have the corrected keys in user_converters... + user_converters.update(uc_update) + + # Reset the names to match the usecols + if (not first_line) and usecols: + names = [names[_] for _ in usecols] + + rows = [] + append_to_rows = rows.append + if usemask: + masks = [] + append_to_masks = masks.append + # Parse each line + for line in itertools.chain([first_line,], fhd): + values = split_line(line) + # Skip an empty line + if len(values) == 0: + continue + # Select only the columns we need + if usecols: + values = [values[_] for _ in usecols] + # Check whether we need to update the converter + if dtype is None: + for (converter, item) in zip(converters, values): + converter.upgrade(item) + # Store the values + append_to_rows(tuple(values)) + if usemask: + append_to_masks(tuple([val.strip() in mss + for (val, mss) in zip(values, + missing_values)])) + + # Convert each value according to the converter: + # We want to modify the list in place to avoid creating a new one... + if loose: + conversionfuncs = [conv._loose_call for conv in converters] + else: + conversionfuncs = [conv._strict_call for conv in converters] + for (i, vals) in enumerate(rows): + rows[i] = tuple([convert(val) + for (convert, val) in zip(conversionfuncs, vals)]) + + # Reset the dtype + data = rows + if dtype is None: + # Get the dtypes from the types of the converters + coldtypes = [conv.type for conv in converters] + # Find the columns with strings... + strcolidx = [i for (i, v) in enumerate(coldtypes) + if v in (type('S'), np.string_)] + # ... and take the largest number of chars. + for i in strcolidx: + coldtypes[i] = "|S%i" % max(len(row[i]) for row in data) + # + if names is None: + # If the dtype is uniform, don't define names, else use '' + base = set([c.type for c in converters if c._checked]) + + if len(base) == 1: + (ddtype, mdtype) = (list(base)[0], np.bool) + else: + ddtype = [('', dt) for dt in coldtypes] + mdtype = [('', np.bool) for dt in coldtypes] + else: + ddtype = zip(names, coldtypes) + mdtype = zip(names, [np.bool] * len(coldtypes)) + output = np.array(data, dtype=ddtype) + if usemask: + outputmask = np.array(masks, dtype=mdtype) + else: + # Overwrite the initial dtype names if needed + if names and dtype.names: + dtype.names = names + flatdtypes = flatten_dtype(dtype) + # Case 1. We have a structured type + if len(flatdtypes) > 1: + # Nested dtype, eg [('a', int), ('b', [('b0', int), ('b1', 'f4')])] + # First, create the array using a flattened dtype: + # [('a', int), ('b1', int), ('b2', float)] + # Then, view the array using the specified dtype. + if has_nested_fields(dtype): + if 'O' in (_.char for _ in flatdtypes): + errmsg = "Nested fields involving objects "\ + "are not supported..." + raise NotImplementedError(errmsg) + rows = np.array(data, dtype=[('', t) for t in flatdtypes]) + output = rows.view(dtype) + else: + output = np.array(data, dtype=dtype) + # Now, process the rowmasks the same way + if usemask: + rowmasks = np.array(masks, + dtype=np.dtype([('', np.bool) + for t in flatdtypes])) + # Construct the new dtype + mdtype = make_mask_descr(dtype) + outputmask = rowmasks.view(mdtype) + # Case #2. We have a basic dtype + else: + # We used some user-defined converters + if user_converters: + ishomogeneous = True + descr = [] + for (i, ttype) in enumerate([conv.type for conv in converters]): + # Keep the dtype of the current converter + if i in user_converters: + ishomogeneous &= (ttype == dtype.type) + if ttype == np.string_: + ttype = "|S%i" % max(len(row[i]) for row in data) + descr.append(('', ttype)) + else: + descr.append(('', dtype)) + # So we changed the dtype ? + if not ishomogeneous: + # We have more than one field + if len(descr) > 1: + dtype = np.dtype(descr) + # We have only one field: drop the name if not needed. + else: + dtype = np.dtype(ttype) + # + output = np.array(data, dtype) + if usemask: + if dtype.names: + mdtype = [(_, np.bool) for _ in dtype.names] + else: + mdtype = np.bool + outputmask = np.array(masks, dtype=mdtype) + # Try to take care of the missing data we missed + if usemask and output.dtype.names: + for (name, conv) in zip(names or (), converters): + missing_values = [conv(_) for _ in conv.missing_values if _ != ''] + for mval in missing_values: + outputmask[name] |= (output[name] == mval) + # Construct the final array + if usemask: + output = output.view(MaskedArray) + output._mask = outputmask + if unpack: + return output.squeeze().T + return output.squeeze() + + + +def ndfromtxt(fname, dtype=float, comments='#', delimiter=None, skiprows=0, + converters=None, missing='', missing_values=None, + usecols=None, unpack=None, names=None, + excludelist=None, deletechars=None, case_sensitive=True,): + """ + Load ASCII data stored in fname and returns a ndarray. + + Complete description of all the optional input parameters is available in + the docstring of the `genfromtxt` function. + + See Also + -------- + numpy.genfromtxt : generic function. + + """ + kwargs = dict(dtype=dtype, comments=comments, delimiter=delimiter, + skiprows=skiprows, converters=converters, + missing=missing, missing_values=missing_values, + usecols=usecols, unpack=unpack, names=names, + excludelist=excludelist, deletechars=deletechars, + case_sensitive=case_sensitive, usemask=False) + return genfromtxt(fname, **kwargs) + +def mafromtxt(fname, dtype=float, comments='#', delimiter=None, skiprows=0, + converters=None, missing='', missing_values=None, + usecols=None, unpack=None, names=None, + excludelist=None, deletechars=None, case_sensitive=True,): + """ + Load ASCII data stored in fname and returns a MaskedArray. + + Complete description of all the optional input parameters is available in + the docstring of the `genfromtxt` function. + + See Also + -------- + numpy.genfromtxt : generic function. + """ + kwargs = dict(dtype=dtype, comments=comments, delimiter=delimiter, + skiprows=skiprows, converters=converters, + missing=missing, missing_values=missing_values, + usecols=usecols, unpack=unpack, names=names, + excludelist=excludelist, deletechars=deletechars, + case_sensitive=case_sensitive, + usemask=True) + return genfromtxt(fname, **kwargs) + + +def recfromtxt(fname, dtype=None, comments='#', delimiter=None, skiprows=0, + converters=None, missing='', missing_values=None, + usecols=None, unpack=None, names=None, + excludelist=None, deletechars=None, case_sensitive=True, + usemask=False): + """ + Load ASCII data stored in fname and returns a standard recarray (if + `usemask=False`) or a MaskedRecords (if `usemask=True`). + + Complete description of all the optional input parameters is available in + the docstring of the `genfromtxt` function. + + See Also + -------- + numpy.genfromtxt : generic function + + Warnings + -------- + * by default, `dtype=None`, which means that the dtype of the output array + will be determined from the data. + """ + kwargs = dict(dtype=dtype, comments=comments, delimiter=delimiter, + skiprows=skiprows, converters=converters, + missing=missing, missing_values=missing_values, + usecols=usecols, unpack=unpack, names=names, + excludelist=excludelist, deletechars=deletechars, + case_sensitive=case_sensitive, usemask=usemask) + output = genfromtxt(fname, **kwargs) + if usemask: + from numpy.ma.mrecords import MaskedRecords + output = output.view(MaskedRecords) + else: + output = output.view(np.recarray) + return output + + +def recfromcsv(fname, dtype=None, comments='#', skiprows=0, + converters=None, missing='', missing_values=None, + usecols=None, unpack=None, names=True, + excludelist=None, deletechars=None, case_sensitive='lower', + usemask=False): + """ + Load ASCII data stored in comma-separated file and returns a recarray (if + `usemask=False`) or a MaskedRecords (if `usemask=True`). + + Complete description of all the optional input parameters is available in + the docstring of the `genfromtxt` function. + + See Also + -------- + numpy.genfromtxt : generic function + """ + kwargs = dict(dtype=dtype, comments=comments, delimiter=",", + skiprows=skiprows, converters=converters, + missing=missing, missing_values=missing_values, + usecols=usecols, unpack=unpack, names=names, + excludelist=excludelist, deletechars=deletechars, + case_sensitive=case_sensitive, usemask=usemask) + output = genfromtxt(fname, **kwargs) + if usemask: + from numpy.ma.mrecords import MaskedRecords + output = output.view(MaskedRecords) + else: + output = output.view(np.recarray) + return output + diff --git a/numpy/lib/recfunctions.py b/numpy/lib/recfunctions.py new file mode 100644 index 000000000..b3eecdc0e --- /dev/null +++ b/numpy/lib/recfunctions.py @@ -0,0 +1,942 @@ +""" +Collection of utilities to manipulate structured arrays. + +Most of these functions were initially implemented by John Hunter for matplotlib. +They have been rewritten and extended for convenience. + + +""" + + +import itertools +from itertools import chain as iterchain, repeat as iterrepeat, izip as iterizip +import numpy as np +from numpy import ndarray, recarray +import numpy.ma as ma +from numpy.ma import MaskedArray +from numpy.ma.mrecords import MaskedRecords + +from numpy.lib._iotools import _is_string_like + +_check_fill_value = np.ma.core._check_fill_value + +__all__ = ['append_fields', + 'drop_fields', + 'find_duplicates', + 'get_fieldstructure', + 'join_by', + 'merge_arrays', + 'rec_append_fields', 'rec_drop_fields', 'rec_join', + 'recursive_fill_fields', 'rename_fields', + 'stack_arrays', + ] + + +def recursive_fill_fields(input, output): + """ + Fills fields from output with fields from input, + with support for nested structures. + + Parameters + ---------- + input : ndarray + Input array. + output : ndarray + Output array. + + Notes + ----- + * `output` should be at least the same size as `input` + + Examples + -------- + >>> a = np.array([(1, 10.), (2, 20.)], dtype=[('A', int), ('B', float)]) + >>> b = np.zeros((3,), dtype=a.dtype) + >>> recursive_fill_fields(a, b) + np.array([(1, 10.), (2, 20.), (0, 0.)], dtype=[('A', int), ('B', float)]) + + """ + newdtype = output.dtype + for field in newdtype.names: + try: + current = input[field] + except ValueError: + continue + if current.dtype.names: + recursive_fill_fields(current, output[field]) + else: + output[field][:len(current)] = current + return output + + + +def get_names(adtype): + """ + Returns the field names of the input datatype as a tuple. + + Parameters + ---------- + adtype : dtype + Input datatype + + Examples + -------- + >>> get_names(np.empty((1,), dtype=int)) is None + True + >>> get_names(np.empty((1,), dtype=[('A',int), ('B', float)])) + ('A', 'B') + >>> adtype = np.dtype([('a', int), ('b', [('ba', int), ('bb', int)])]) + >>> get_names(adtype) + ('a', ('b', ('ba', 'bb'))) + """ + listnames = [] + names = adtype.names + for name in names: + current = adtype[name] + if current.names: + listnames.append((name, tuple(get_names(current)))) + else: + listnames.append(name) + return tuple(listnames) or None + + +def get_names_flat(adtype): + """ + Returns the field names of the input datatype as a tuple. Nested structure + are flattend beforehand. + + Parameters + ---------- + adtype : dtype + Input datatype + + Examples + -------- + >>> get_names_flat(np.empty((1,), dtype=int)) is None + True + >>> get_names_flat(np.empty((1,), dtype=[('A',int), ('B', float)])) + ('A', 'B') + >>> adtype = np.dtype([('a', int), ('b', [('ba', int), ('bb', int)])]) + >>> get_names_flat(adtype) + ('a', 'b', 'ba', 'bb') + """ + listnames = [] + names = adtype.names + for name in names: + listnames.append(name) + current = adtype[name] + if current.names: + listnames.extend(get_names_flat(current)) + return tuple(listnames) or None + + +def flatten_descr(ndtype): + """ + Flatten a structured data-type description. + + Examples + -------- + >>> ndtype = np.dtype([('a', '<i4'), ('b', [('ba', '<f8'), ('bb', '<i4')])]) + >>> flatten_descr(ndtype) + (('a', dtype('int32')), ('ba', dtype('float64')), ('bb', dtype('int32'))) + + """ + names = ndtype.names + if names is None: + return ndtype.descr + else: + descr = [] + for field in names: + (typ, _) = ndtype.fields[field] + if typ.names: + descr.extend(flatten_descr(typ)) + else: + descr.append((field, typ)) + return tuple(descr) + + +def zip_descr(seqarrays, flatten=False): + """ + Combine the dtype description of a series of arrays. + + Parameters + ---------- + seqarrays : sequence of arrays + Sequence of arrays + flatten : {boolean}, optional + Whether to collapse nested descriptions. + """ + newdtype = [] + if flatten: + for a in seqarrays: + newdtype.extend(flatten_descr(a.dtype)) + else: + for a in seqarrays: + current = a.dtype + names = current.names or () + if len(names) > 1: + newdtype.append(('', current.descr)) + else: + newdtype.extend(current.descr) + return np.dtype(newdtype).descr + + +def get_fieldstructure(adtype, lastname=None, parents=None,): + """ + Returns a dictionary with fields as keys and a list of parent fields as values. + + This function is used to simplify access to fields nested in other fields. + + Parameters + ---------- + adtype : np.dtype + Input datatype + lastname : optional + Last processed field name (used internally during recursion). + parents : dictionary + Dictionary of parent fields (used interbally during recursion). + + Examples + -------- + >>> ndtype = np.dtype([('A', int), + ... ('B', [('BA', int), + ... ('BB', [('BBA', int), ('BBB', int)])])]) + >>> get_fieldstructure(ndtype) + {'A': [], 'B': [], 'BA': ['B'], 'BB': ['B'], + 'BBA': ['B', 'BB'], 'BBB': ['B', 'BB']} + + """ + if parents is None: + parents = {} + names = adtype.names + for name in names: + current = adtype[name] + if current.names: + if lastname: + parents[name] = [lastname,] + else: + parents[name] = [] + parents.update(get_fieldstructure(current, name, parents)) + else: + lastparent = [_ for _ in (parents.get(lastname, []) or [])] + if lastparent: +# if (lastparent[-1] != lastname): + lastparent.append(lastname) + elif lastname: + lastparent = [lastname,] + parents[name] = lastparent or [] + return parents or None + + +def _izip_fields_flat(iterable): + """ + Returns an iterator of concatenated fields from a sequence of arrays, + collapsing any nested structure. + """ + for element in iterable: + if isinstance(element, np.void): + for f in _izip_fields_flat(tuple(element)): + yield f + else: + yield element + + +def _izip_fields(iterable): + """ + Returns an iterator of concatenated fields from a sequence of arrays. + """ + for element in iterable: + if hasattr(element, '__iter__') and not isinstance(element, basestring): + for f in _izip_fields(element): + yield f + elif isinstance(element, np.void) and len(tuple(element)) == 1: + for f in _izip_fields(element): + yield f + else: + yield element + + +def izip_records(seqarrays, fill_value=None, flatten=True): + """ + Returns an iterator of concatenated items from a sequence of arrays. + + Parameters + ---------- + seqarray : sequence of arrays + Sequence of arrays. + fill_value : {None, integer} + Value used to pad shorter iterables. + flatten : {True, False}, + Whether to + """ + # OK, that's a complete ripoff from Python2.6 itertools.izip_longest + def sentinel(counter = ([fill_value]*(len(seqarrays)-1)).pop): + "Yields the fill_value or raises IndexError" + yield counter() + # + fillers = iterrepeat(fill_value) + iters = [iterchain(it, sentinel(), fillers) for it in seqarrays] + # Should we flatten the items, or just use a nested approach + if flatten: + zipfunc = _izip_fields_flat + else: + zipfunc = _izip_fields + # + try: + for tup in iterizip(*iters): + yield tuple(zipfunc(tup)) + except IndexError: + pass + + +def _fix_output(output, usemask=True, asrecarray=False): + """ + Private function: return a recarray, a ndarray, a MaskedArray + or a MaskedRecords depending on the input parameters + """ + if not isinstance(output, MaskedArray): + usemask = False + if usemask: + if asrecarray: + output = output.view(MaskedRecords) + else: + output = ma.filled(output) + if asrecarray: + output = output.view(recarray) + return output + + +def _fix_defaults(output, defaults=None): + """ + Update the fill_value and masked data of `output` + from the default given in a dictionary defaults. + """ + names = output.dtype.names + (data, mask, fill_value) = (output.data, output.mask, output.fill_value) + for (k, v) in (defaults or {}).iteritems(): + if k in names: + fill_value[k] = v + data[k][mask[k]] = v + return output + + +def merge_arrays(seqarrays, + fill_value=-1, flatten=False, usemask=True, asrecarray=False): + """ + Merge arrays field by field. + + Parameters + ---------- + seqarrays : sequence of ndarrays + Sequence of arrays + fill_value : {float}, optional + Filling value used to pad missing data on the shorter arrays. + flatten : {False, True}, optional + Whether to collapse nested fields. + usemask : {False, True}, optional + Whether to return a masked array or not. + asrecarray : {False, True}, optional + Whether to return a recarray (MaskedRecords) or not. + + Examples + -------- + >>> merge_arrays((np.array([1, 2]), np.array([10., 20., 30.]))) + masked_array(data = [(1, 10.0) (2, 20.0) (--, 30.0)], + mask = [(False, False) (False, False) (True, False)], + fill_value=(999999, 1e+20) + dtype=[('f0', '<i4'), ('f1', '<f8')]) + >>> merge_arrays((np.array([1, 2]), np.array([10., 20., 30.])), + ... usemask=False) + array(data = [(1, 10.0) (2, 20.0) (-1, 30.0)], + dtype=[('f0', '<i4'), ('f1', '<f8')]) + >>> merge_arrays((np.array([1, 2]).view([('a', int)]), + np.array([10., 20., 30.])), + usemask=False, asrecarray=True) + rec.array(data = [(1, 10.0) (2, 20.0) (-1, 30.0)], + dtype=[('a', int), ('f1', '<f8')]) + """ + if (len(seqarrays) == 1): + seqarrays = seqarrays[0] + if isinstance(seqarrays, ndarray): + seqdtype = seqarrays.dtype + if (not flatten) or \ + (zip_descr((seqarrays,), flatten=True) == seqdtype.descr): + seqarrays = seqarrays.ravel() + if not seqdtype.names: + seqarrays = seqarrays.view([('', seqdtype)]) + if usemask: + if asrecarray: + return seqarrays.view(MaskedRecords) + return seqarrays.view(MaskedArray) + elif asrecarray: + return seqarrays.view(recarray) + return seqarrays + else: + seqarrays = (seqarrays,) + # Get the dtype + newdtype = zip_descr(seqarrays, flatten=flatten) + # Get the data and the fill_value from each array + seqdata = [ma.getdata(a.ravel()) for a in seqarrays] + seqmask = [ma.getmaskarray(a).ravel() for a in seqarrays] + fill_value = [_check_fill_value(fill_value, a.dtype) for a in seqdata] + # Make an iterator from each array, padding w/ fill_values + maxlength = max(len(a) for a in seqarrays) + for (i, (a, m, fval)) in enumerate(zip(seqdata, seqmask, fill_value)): + # Flatten the fill_values if there's only one field + if isinstance(fval, (ndarray, np.void)): + fmsk = ma.ones((1,), m.dtype)[0] + if len(fval.dtype) == 1: + fval = fval.item()[0] + fmsk = True + else: + # fval and fmsk should be np.void objects + fval = np.array([fval,], dtype=a.dtype)[0] +# fmsk = np.array([fmsk,], dtype=m.dtype)[0] + else: + fmsk = True + nbmissing = (maxlength-len(a)) + seqdata[i] = iterchain(a, [fval]*nbmissing) + seqmask[i] = iterchain(m, [fmsk]*nbmissing) + # + data = izip_records(seqdata, flatten=flatten) + data = tuple(data) + if usemask: + mask = izip_records(seqmask, fill_value=True, flatten=flatten) + mask = tuple(mask) + output = ma.array(np.fromiter(data, dtype=newdtype)) + output._mask[:] = list(mask) + if asrecarray: + output = output.view(MaskedRecords) + else: + output = np.fromiter(data, dtype=newdtype) + if asrecarray: + output = output.view(recarray) + return output + + + +def drop_fields(base, drop_names, usemask=True, asrecarray=False): + """ + Return a new array with fields in `drop_names` dropped. + + Nested fields are supported. + + Parameters + ---------- + base : array + Input array + drop_names : string or sequence + String or sequence of strings corresponding to the names of the fields + to drop. + usemask : {False, True}, optional + Whether to return a masked array or not. + asrecarray : string or sequence + Whether to return a recarray or a mrecarray (`asrecarray=True`) or + a plain ndarray or masked array with flexible dtype (`asrecarray=False`) + + Examples + -------- + >>> a = np.array([(1, (2, 3.0)), (4, (5, 6.0))], + dtype=[('a', int), ('b', [('ba', float), ('bb', int)])]) + >>> drop_fields(a, 'a') + array([((2.0, 3),), ((5.0, 6),)], + dtype=[('b', [('ba', '<f8'), ('bb', '<i4')])]) + >>> drop_fields(a, 'ba') + array([(1, (3,)), (4, (6,))], + dtype=[('a', '<i4'), ('b', [('bb', '<i4')])]) + >>> drop_fields(a, ['ba', 'bb']) + array([(1,), (4,)], + dtype=[('a', '<i4')]) + """ + if _is_string_like(drop_names): + drop_names = [drop_names,] + else: + drop_names = set(drop_names) + # + def _drop_descr(ndtype, drop_names): + names = ndtype.names + newdtype = [] + for name in names: + current = ndtype[name] + if name in drop_names: + continue + if current.names: + descr = _drop_descr(current, drop_names) + if descr: + newdtype.append((name, descr)) + else: + newdtype.append((name, current)) + return newdtype + # + newdtype = _drop_descr(base.dtype, drop_names) + if not newdtype: + return None + # + output = np.empty(base.shape, dtype=newdtype) + output = recursive_fill_fields(base, output) + return _fix_output(output, usemask=usemask, asrecarray=asrecarray) + + +def rec_drop_fields(base, drop_names): + """ + Returns a new numpy.recarray with fields in `drop_names` dropped. + """ + return drop_fields(base, drop_names, usemask=False, asrecarray=True) + + + +def rename_fields(base, namemapper): + """ + Rename the fields from a flexible-datatype ndarray or recarray. + + Nested fields are supported. + + Parameters + ---------- + base : ndarray + Input array whose fields must be modified. + namemapper : dictionary + Dictionary mapping old field names to their new version. + + Examples + -------- + >>> a = np.array([(1, (2, [3.0, 30.])), (4, (5, [6.0, 60.]))], + dtype=[('a', int), + ('b', [('ba', float), ('bb', (float, 2))])]) + >>> rename_fields(a, {'a':'A', 'bb':'BB'}) + array([(1, (2.0, 3)), (4, (5.0, 6))], + dtype=[('A', '<i4'), ('b', [('ba', '<f8'), ('BB', '<i4')])]) + + """ + def _recursive_rename_fields(ndtype, namemapper): + newdtype = [] + for name in ndtype.names: + newname = namemapper.get(name, name) + current = ndtype[name] + if current.names: + newdtype.append((newname, + _recursive_rename_fields(current, namemapper))) + else: + newdtype.append((newname, current)) + return newdtype + newdtype = _recursive_rename_fields(base.dtype, namemapper) + return base.view(newdtype) + + +def append_fields(base, names, data=None, dtypes=None, + fill_value=-1, usemask=True, asrecarray=False): + """ + Add new fields to an existing array. + + The names of the fields are given with the `names` arguments, + the corresponding values with the `data` arguments. + If a single field is appended, `names`, `data` and `dtypes` do not have + to be lists but just values. + + Parameters + ---------- + base : array + Input array to extend. + names : string, sequence + String or sequence of strings corresponding to the names + of the new fields. + data : array or sequence of arrays + Array or sequence of arrays storing the fields to add to the base. + dtypes : sequence of datatypes + Datatype or sequence of datatypes. + If None, the datatypes are estimated from the `data`. + fill_value : {float}, optional + Filling value used to pad missing data on the shorter arrays. + usemask : {False, True}, optional + Whether to return a masked array or not. + asrecarray : {False, True}, optional + Whether to return a recarray (MaskedRecords) or not. + + """ + # Check the names + if isinstance(names, (tuple, list)): + if len(names) != len(data): + err_msg = "The number of arrays does not match the number of names" + raise ValueError(err_msg) + elif isinstance(names, basestring): + names = [names,] + data = [data,] + # + if dtypes is None: + data = [np.array(a, copy=False, subok=True) for a in data] + data = [a.view([(name, a.dtype)]) for (name, a) in zip(names, data)] + elif not hasattr(dtypes, '__iter__'): + dtypes = [dtypes,] + if len(data) != len(dtypes): + if len(dtypes) == 1: + dtypes = dtypes * len(data) + else: + msg = "The dtypes argument must be None, "\ + "a single dtype or a list." + raise ValueError(msg) + data = [np.array(a, copy=False, subok=True, dtype=d).view([(n, d)]) + for (a, n, d) in zip(data, names, dtypes)] + # + base = merge_arrays(base, usemask=usemask, fill_value=fill_value) + if len(data) > 1: + data = merge_arrays(data, flatten=True, usemask=usemask, + fill_value=fill_value) + else: + data = data.pop() + # + output = ma.masked_all(max(len(base), len(data)), + dtype=base.dtype.descr + data.dtype.descr) + output = recursive_fill_fields(base, output) + output = recursive_fill_fields(data, output) + # + return _fix_output(output, usemask=usemask, asrecarray=asrecarray) + + + +def rec_append_fields(base, names, data, dtypes=None): + """ + Add new fields to an existing array. + + The names of the fields are given with the `names` arguments, + the corresponding values with the `data` arguments. + If a single field is appended, `names`, `data` and `dtypes` do not have + to be lists but just values. + + Parameters + ---------- + base : array + Input array to extend. + names : string, sequence + String or sequence of strings corresponding to the names + of the new fields. + data : array or sequence of arrays + Array or sequence of arrays storing the fields to add to the base. + dtypes : sequence of datatypes, optional + Datatype or sequence of datatypes. + If None, the datatypes are estimated from the `data`. + + See Also + -------- + append_fields + + Returns + ------- + appended_array : np.recarray + """ + return append_fields(base, names, data=data, dtypes=dtypes, + asrecarray=True, usemask=False) + + + +def stack_arrays(arrays, defaults=None, usemask=True, asrecarray=False, + autoconvert=False): + """ + Superposes arrays fields by fields + + Parameters + ---------- + seqarrays : array or sequence + Sequence of input arrays. + defaults : dictionary, optional + Dictionary mapping field names to the corresponding default values. + usemask : {True, False}, optional + Whether to return a MaskedArray (or MaskedRecords is `asrecarray==True`) + or a ndarray. + asrecarray : {False, True}, optional + Whether to return a recarray (or MaskedRecords if `usemask==True`) or + just a flexible-type ndarray. + autoconvert : {False, True}, optional + Whether automatically cast the type of the field to the maximum. + + Examples + -------- + >>> x = np.array([1, 2,]) + >>> stack_arrays(x) is x + True + >>> z = np.array([('A', 1), ('B', 2)], dtype=[('A', '|S3'), ('B', float)]) + >>> zz = np.array([('a', 10., 100.), ('b', 20., 200.), ('c', 30., 300.)], + dtype=[('A', '|S3'), ('B', float), ('C', float)]) + >>> test = stack_arrays((z,zz)) + >>> masked_array(data = [('A', 1.0, --) ('B', 2.0, --) ('a', 10.0, 100.0) + ... ('b', 20.0, 200.0) ('c', 30.0, 300.0)], + ... mask = [(False, False, True) (False, False, True) (False, False, False) + ... (False, False, False) (False, False, False)], + ... fill_value=('N/A', 1e+20, 1e+20) + ... dtype=[('A', '|S3'), ('B', '<f8'), ('C', '<f8')]) + + """ + if isinstance(arrays, ndarray): + return arrays + elif len(arrays) == 1: + return arrays[0] + seqarrays = [np.asanyarray(a).ravel() for a in arrays] + nrecords = [len(a) for a in seqarrays] + ndtype = [a.dtype for a in seqarrays] + fldnames = [d.names for d in ndtype] + # + dtype_l = ndtype[0] + newdescr = dtype_l.descr + names = [_[0] for _ in newdescr] + for dtype_n in ndtype[1:]: + for descr in dtype_n.descr: + name = descr[0] or '' + if name not in names: + newdescr.append(descr) + names.append(name) + else: + nameidx = names.index(name) + current_descr = newdescr[nameidx] + if autoconvert: + if np.dtype(descr[1]) > np.dtype(current_descr[-1]): + current_descr = list(current_descr) + current_descr[-1] = descr[1] + newdescr[nameidx] = tuple(current_descr) + elif descr[1] != current_descr[-1]: + raise TypeError("Incompatible type '%s' <> '%s'" %\ + (dict(newdescr)[name], descr[1])) + # Only one field: use concatenate + if len(newdescr) == 1: + output = ma.concatenate(seqarrays) + else: + # + output = ma.masked_all((np.sum(nrecords),), newdescr) + offset = np.cumsum(np.r_[0, nrecords]) + seen = [] + for (a, n, i, j) in zip(seqarrays, fldnames, offset[:-1], offset[1:]): + names = a.dtype.names + if names is None: + output['f%i' % len(seen)][i:j] = a + else: + for name in n: + output[name][i:j] = a[name] + if name not in seen: + seen.append(name) + # + return _fix_output(_fix_defaults(output, defaults), + usemask=usemask, asrecarray=asrecarray) + + + +def find_duplicates(a, key=None, ignoremask=True, return_index=False): + """ + Find the duplicates in a structured array along a given key + + Parameters + ---------- + a : array-like + Input array + key : {string, None}, optional + Name of the fields along which to check the duplicates. + If None, the search is performed by records + ignoremask : {True, False}, optional + Whether masked data should be discarded or considered as duplicates. + return_index : {False, True}, optional + Whether to return the indices of the duplicated values. + + Examples + -------- + >>> ndtype = [('a', int)] + >>> a = ma.array([1, 1, 1, 2, 2, 3, 3], + ... mask=[0, 0, 1, 0, 0, 0, 1]).view(ndtype) + >>> find_duplicates(a, ignoremask=True, return_index=True) + """ + a = np.asanyarray(a).ravel() + # Get a dictionary of fields + fields = get_fieldstructure(a.dtype) + # Get the sorting data (by selecting the corresponding field) + base = a + if key: + for f in fields[key]: + base = base[f] + base = base[key] + # Get the sorting indices and the sorted data + sortidx = base.argsort() + sortedbase = base[sortidx] + sorteddata = sortedbase.filled() + # Compare the sorting data + flag = (sorteddata[:-1] == sorteddata[1:]) + # If masked data must be ignored, set the flag to false where needed + if ignoremask: + sortedmask = sortedbase.recordmask + flag[sortedmask[1:]] = False + flag = np.concatenate(([False], flag)) + # We need to take the point on the left as well (else we're missing it) + flag[:-1] = flag[:-1] + flag[1:] + duplicates = a[sortidx][flag] + if return_index: + return (duplicates, sortidx[flag]) + else: + return duplicates + + + +def join_by(key, r1, r2, jointype='inner', r1postfix='1', r2postfix='2', + defaults=None, usemask=True, asrecarray=False): + """ + Join arrays `r1` and `r2` on key `key`. + + The key should be either a string or a sequence of string corresponding + to the fields used to join the array. + An exception is raised if the `key` field cannot be found in the two input + arrays. + Neither `r1` nor `r2` should have any duplicates along `key`: the presence + of duplicates will make the output quite unreliable. Note that duplicates + are not looked for by the algorithm. + + Parameters + ---------- + key : {string, sequence} + A string or a sequence of strings corresponding to the fields used + for comparison. + r1, r2 : arrays + Structured arrays. + jointype : {'inner', 'outer', 'leftouter'}, optional + If 'inner', returns the elements common to both r1 and r2. + If 'outer', returns the common elements as well as the elements of r1 + not in r2 and the elements of not in r2. + If 'leftouter', returns the common elements and the elements of r1 not + in r2. + r1postfix : string, optional + String appended to the names of the fields of r1 that are present in r2 + but absent of the key. + r2postfix : string, optional + String appended to the names of the fields of r2 that are present in r1 + but absent of the key. + defaults : {dictionary}, optional + Dictionary mapping field names to the corresponding default values. + usemask : {True, False}, optional + Whether to return a MaskedArray (or MaskedRecords is `asrecarray==True`) + or a ndarray. + asrecarray : {False, True}, optional + Whether to return a recarray (or MaskedRecords if `usemask==True`) or + just a flexible-type ndarray. + + Notes + ----- + * The output is sorted along the key. + * A temporary array is formed by dropping the fields not in the key for the + two arrays and concatenating the result. This array is then sorted, and + the common entries selected. The output is constructed by filling the fields + with the selected entries. Matching is not preserved if there are some + duplicates... + + """ + # Check jointype + if jointype not in ('inner', 'outer', 'leftouter'): + raise ValueError("The 'jointype' argument should be in 'inner', "\ + "'outer' or 'leftouter' (got '%s' instead)" % jointype) + # If we have a single key, put it in a tuple + if isinstance(key, basestring): + key = (key, ) + + # Check the keys + for name in key: + if name not in r1.dtype.names: + raise ValueError('r1 does not have key field %s'%name) + if name not in r2.dtype.names: + raise ValueError('r2 does not have key field %s'%name) + + # Make sure we work with ravelled arrays + r1 = r1.ravel() + r2 = r2.ravel() + (nb1, nb2) = (len(r1), len(r2)) + (r1names, r2names) = (r1.dtype.names, r2.dtype.names) + + # Make temporary arrays of just the keys + r1k = drop_fields(r1, [n for n in r1names if n not in key]) + r2k = drop_fields(r2, [n for n in r2names if n not in key]) + + # Concatenate the two arrays for comparison + aux = ma.concatenate((r1k, r2k)) + idx_sort = aux.argsort(order=key) + aux = aux[idx_sort] + # + # Get the common keys + flag_in = ma.concatenate(([False], aux[1:] == aux[:-1])) + flag_in[:-1] = flag_in[1:] + flag_in[:-1] + idx_in = idx_sort[flag_in] + idx_1 = idx_in[(idx_in < nb1)] + idx_2 = idx_in[(idx_in >= nb1)] - nb1 + (r1cmn, r2cmn) = (len(idx_1), len(idx_2)) + if jointype == 'inner': + (r1spc, r2spc) = (0, 0) + elif jointype == 'outer': + idx_out = idx_sort[~flag_in] + idx_1 = np.concatenate((idx_1, idx_out[(idx_out < nb1)])) + idx_2 = np.concatenate((idx_2, idx_out[(idx_out >= nb1)] - nb1)) + (r1spc, r2spc) = (len(idx_1) - r1cmn, len(idx_2) - r2cmn) + elif jointype == 'leftouter': + idx_out = idx_sort[~flag_in] + idx_1 = np.concatenate((idx_1, idx_out[(idx_out < nb1)])) + (r1spc, r2spc) = (len(idx_1) - r1cmn, 0) + # Select the entries from each input + (s1, s2) = (r1[idx_1], r2[idx_2]) + # + # Build the new description of the output array ....... + # Start with the key fields + ndtype = [list(_) for _ in r1k.dtype.descr] + # Add the other fields + ndtype.extend(list(_) for _ in r1.dtype.descr if _[0] not in key) + # Find the new list of names (it may be different from r1names) + names = list(_[0] for _ in ndtype) + for desc in r2.dtype.descr: + desc = list(desc) + name = desc[0] + # Have we seen the current name already ? + if name in names: + nameidx = names.index(name) + current = ndtype[nameidx] + # The current field is part of the key: take the largest dtype + if name in key: + current[-1] = max(desc[1], current[-1]) + # The current field is not part of the key: add the suffixes + else: + current[0] += r1postfix + desc[0] += r2postfix + ndtype.insert(nameidx+1, desc) + #... we haven't: just add the description to the current list + else: + names.extend(desc[0]) + ndtype.append(desc) + # Revert the elements to tuples + ndtype = [tuple(_) for _ in ndtype] + # Find the largest nb of common fields : r1cmn and r2cmn should be equal, but... + cmn = max(r1cmn, r2cmn) + # Construct an empty array + output = ma.masked_all((cmn + r1spc + r2spc,), dtype=ndtype) + names = output.dtype.names + for f in r1names: + selected = s1[f] + if f not in names: + f += r1postfix + current = output[f] + current[:r1cmn] = selected[:r1cmn] + if jointype in ('outer', 'leftouter'): + current[cmn:cmn+r1spc] = selected[r1cmn:] + for f in r2names: + selected = s2[f] + if f not in names: + f += r2postfix + current = output[f] + current[:r2cmn] = selected[:r2cmn] + if (jointype == 'outer') and r2spc: + current[-r2spc:] = selected[r2cmn:] + # Sort and finalize the output + output.sort(order=key) + kwargs = dict(usemask=usemask, asrecarray=asrecarray) + return _fix_output(_fix_defaults(output, defaults), **kwargs) + + +def rec_join(key, r1, r2, jointype='inner', r1postfix='1', r2postfix='2', + defaults=None): + """ + Join arrays `r1` and `r2` on keys. + Alternative to join_by, that always returns a np.recarray. + + See Also + -------- + join_by : equivalent function + """ + kwargs = dict(jointype=jointype, r1postfix=r1postfix, r2postfix=r2postfix, + defaults=defaults, usemask=False, asrecarray=True) + return join_by(key, r1, r2, **kwargs) diff --git a/numpy/lib/src/_compiled_base.c b/numpy/lib/src/_compiled_base.c index ddab9f851..54955e60a 100644 --- a/numpy/lib/src/_compiled_base.c +++ b/numpy/lib/src/_compiled_base.c @@ -494,34 +494,45 @@ arr_add_docstring(PyObject *NPY_UNUSED(dummy), PyObject *args) #define _TESTDOC1(typebase) (obj->ob_type == &Py##typebase##_Type) #define _TESTDOC2(typebase) (obj->ob_type == Py##typebase##_TypePtr) -#define _ADDDOC(typebase, doc, name) { \ +#define _ADDDOC(typebase, doc, name) do { \ Py##typebase##Object *new = (Py##typebase##Object *)obj; \ if (!(doc)) { \ doc = docstr; \ } \ else { \ - PyErr_Format(PyExc_RuntimeError, \ - "%s method %s",name, msg); \ + PyErr_Format(PyExc_RuntimeError, "%s method %s", name, msg); \ return NULL; \ } \ - } + } while (0) + + if (_TESTDOC1(CFunction)) + _ADDDOC(CFunction, new->m_ml->ml_doc, new->m_ml->ml_name); + else if (_TESTDOC1(Type)) + _ADDDOC(Type, new->tp_doc, new->tp_name); + else if (_TESTDOC2(MemberDescr)) + _ADDDOC(MemberDescr, new->d_member->doc, new->d_member->name); + else if (_TESTDOC2(GetSetDescr)) + _ADDDOC(GetSetDescr, new->d_getset->doc, new->d_getset->name); + else if (_TESTDOC2(MethodDescr)) + _ADDDOC(MethodDescr, new->d_method->ml_doc, new->d_method->ml_name); + else { + PyObject *doc_attr; + + doc_attr = PyObject_GetAttrString(obj, "__doc__"); + if (doc_attr != NULL && doc_attr != Py_None) { + PyErr_Format(PyExc_RuntimeError, "object %s", msg); + return NULL; + } + Py_XDECREF(doc_attr); - if _TESTDOC1(CFunction) - _ADDDOC(CFunction, new->m_ml->ml_doc, new->m_ml->ml_name) - else if _TESTDOC1(Type) - _ADDDOC(Type, new->tp_doc, new->tp_name) - else if _TESTDOC2(MemberDescr) - _ADDDOC(MemberDescr, new->d_member->doc, new->d_member->name) - else if _TESTDOC2(GetSetDescr) - _ADDDOC(GetSetDescr, new->d_getset->doc, new->d_getset->name) - else if _TESTDOC2(MethodDescr) - _ADDDOC(MethodDescr, new->d_method->ml_doc, - new->d_method->ml_name) - else { - PyErr_SetString(PyExc_TypeError, - "Cannot set a docstring for that object"); - return NULL; - } + if (PyObject_SetAttrString(obj, "__doc__", str) < 0) { + PyErr_SetString(PyExc_TypeError, + "Cannot set a docstring for that object"); + return NULL; + } + Py_INCREF(Py_None); + return Py_None; + } #undef _TESTDOC1 #undef _TESTDOC2 @@ -533,35 +544,6 @@ arr_add_docstring(PyObject *NPY_UNUSED(dummy), PyObject *args) } -static char packbits_doc[] = - "out = numpy.packbits(myarray, axis=None)\n\n" - " myarray : an integer type array whose elements should be packed to bits\n\n" - " This routine packs the elements of a binary-valued dataset into a\n" - " NumPy array of type uint8 ('B') whose bits correspond to\n" - " the logical (0 or nonzero) value of the input elements.\n" - " The dimension over-which bit-packing is done is given by axis.\n" - " The shape of the output has the same number of dimensions as the input\n" - " (unless axis is None, in which case the output is 1-d).\n" - "\n" - " Example:\n" - " >>> a = array([[[1,0,1],\n" - " ... [0,1,0]],\n" - " ... [[1,1,0],\n" - " ... [0,0,1]]])\n" - " >>> b = numpy.packbits(a,axis=-1)\n" - " >>> b\n" - " array([[[160],[64]],[[192],[32]]], dtype=uint8)\n\n" - " Note that 160 = 128 + 32\n" - " 192 = 128 + 64\n"; - -static char unpackbits_doc[] = - "out = numpy.unpackbits(myarray, axis=None)\n\n" - " myarray - array of uint8 type where each element represents a bit-field\n" - " that should be unpacked into a boolean output array\n\n" - " The shape of the output array is either 1-d (if axis is None) or\n" - " the same shape as the input array with unpacking done along the\n" - " axis specified."; - /* PACKBITS This function packs binary (0 or 1) 1-bit per pixel arrays @@ -809,9 +791,9 @@ static struct PyMethodDef methods[] = { {"add_docstring", (PyCFunction)arr_add_docstring, METH_VARARGS, NULL}, {"packbits", (PyCFunction)io_pack, METH_VARARGS | METH_KEYWORDS, - packbits_doc}, + NULL}, {"unpackbits", (PyCFunction)io_unpack, METH_VARARGS | METH_KEYWORDS, - unpackbits_doc}, + NULL}, {NULL, NULL} /* sentinel */ }; diff --git a/numpy/lib/tests/test__iotools.py b/numpy/lib/tests/test__iotools.py new file mode 100644 index 000000000..2cb8461c3 --- /dev/null +++ b/numpy/lib/tests/test__iotools.py @@ -0,0 +1,167 @@ + +import StringIO + +import numpy as np +from numpy.lib._iotools import LineSplitter, NameValidator, StringConverter,\ + has_nested_fields +from numpy.testing import * + +class TestLineSplitter(TestCase): + "Tests the LineSplitter class." + # + def test_no_delimiter(self): + "Test LineSplitter w/o delimiter" + strg = " 1 2 3 4 5 # test" + test = LineSplitter()(strg) + assert_equal(test, ['1', '2', '3', '4', '5']) + test = LineSplitter('')(strg) + assert_equal(test, ['1', '2', '3', '4', '5']) + + def test_space_delimiter(self): + "Test space delimiter" + strg = " 1 2 3 4 5 # test" + test = LineSplitter(' ')(strg) + assert_equal(test, ['1', '2', '3', '4', '', '5']) + test = LineSplitter(' ')(strg) + assert_equal(test, ['1 2 3 4', '5']) + + def test_tab_delimiter(self): + "Test tab delimiter" + strg= " 1\t 2\t 3\t 4\t 5 6" + test = LineSplitter('\t')(strg) + assert_equal(test, ['1', '2', '3', '4', '5 6']) + strg= " 1 2\t 3 4\t 5 6" + test = LineSplitter('\t')(strg) + assert_equal(test, ['1 2', '3 4', '5 6']) + + def test_other_delimiter(self): + "Test LineSplitter on delimiter" + strg = "1,2,3,4,,5" + test = LineSplitter(',')(strg) + assert_equal(test, ['1', '2', '3', '4', '', '5']) + # + strg = " 1,2,3,4,,5 # test" + test = LineSplitter(',')(strg) + assert_equal(test, ['1', '2', '3', '4', '', '5']) + + def test_constant_fixed_width(self): + "Test LineSplitter w/ fixed-width fields" + strg = " 1 2 3 4 5 # test" + test = LineSplitter(3)(strg) + assert_equal(test, ['1', '2', '3', '4', '', '5', '']) + # + strg = " 1 3 4 5 6# test" + test = LineSplitter(20)(strg) + assert_equal(test, ['1 3 4 5 6']) + # + strg = " 1 3 4 5 6# test" + test = LineSplitter(30)(strg) + assert_equal(test, ['1 3 4 5 6']) + + def test_variable_fixed_width(self): + strg = " 1 3 4 5 6# test" + test = LineSplitter((3,6,6,3))(strg) + assert_equal(test, ['1', '3', '4 5', '6']) + # + strg = " 1 3 4 5 6# test" + test = LineSplitter((6,6,9))(strg) + assert_equal(test, ['1', '3 4', '5 6']) + + +#------------------------------------------------------------------------------- + +class TestNameValidator(TestCase): + # + def test_case_sensitivity(self): + "Test case sensitivity" + names = ['A', 'a', 'b', 'c'] + test = NameValidator().validate(names) + assert_equal(test, ['A', 'a', 'b', 'c']) + test = NameValidator(case_sensitive=False).validate(names) + assert_equal(test, ['A', 'A_1', 'B', 'C']) + test = NameValidator(case_sensitive='upper').validate(names) + assert_equal(test, ['A', 'A_1', 'B', 'C']) + test = NameValidator(case_sensitive='lower').validate(names) + assert_equal(test, ['a', 'a_1', 'b', 'c']) + # + def test_excludelist(self): + "Test excludelist" + names = ['dates', 'data', 'Other Data', 'mask'] + validator = NameValidator(excludelist = ['dates', 'data', 'mask']) + test = validator.validate(names) + assert_equal(test, ['dates_', 'data_', 'Other_Data', 'mask_']) + + +#------------------------------------------------------------------------------- + +class TestStringConverter(TestCase): + "Test StringConverter" + # + def test_creation(self): + "Test creation of a StringConverter" + converter = StringConverter(int, -99999) + assert_equal(converter._status, 1) + assert_equal(converter.default, -99999) + # + def test_upgrade(self): + "Tests the upgrade method." + converter = StringConverter() + assert_equal(converter._status, 0) + converter.upgrade('0') + assert_equal(converter._status, 1) + converter.upgrade('0.') + assert_equal(converter._status, 2) + converter.upgrade('0j') + assert_equal(converter._status, 3) + converter.upgrade('a') + assert_equal(converter._status, len(converter._mapper)-1) + # + def test_missing(self): + "Tests the use of missing values." + converter = StringConverter(missing_values=('missing','missed')) + converter.upgrade('0') + assert_equal(converter('0'), 0) + assert_equal(converter(''), converter.default) + assert_equal(converter('missing'), converter.default) + assert_equal(converter('missed'), converter.default) + try: + converter('miss') + except ValueError: + pass + # + def test_upgrademapper(self): + "Tests updatemapper" + from datetime import date + import time + dateparser = lambda s : date(*time.strptime(s, "%Y-%m-%d")[:3]) + StringConverter.upgrade_mapper(dateparser, date(2000,1,1)) + convert = StringConverter(dateparser, date(2000, 1, 1)) + test = convert('2001-01-01') + assert_equal(test, date(2001, 01, 01)) + test = convert('2009-01-01') + assert_equal(test, date(2009, 01, 01)) + test = convert('') + assert_equal(test, date(2000, 01, 01)) + # + def test_string_to_object(self): + "Make sure that string-to-object functions are properly recognized" + from datetime import date + import time + conv = StringConverter(lambda s: date(*(time.strptime(s)[:3]))) + assert_equal(conv._mapper[-2][0](0), 0j) + assert(hasattr(conv, 'default')) + + +#------------------------------------------------------------------------------- + +class TestMiscFunctions(TestCase): + # + def test_has_nested_dtype(self): + "Test has_nested_dtype" + ndtype = np.dtype(np.float) + assert_equal(has_nested_fields(ndtype), False) + ndtype = np.dtype([('A', '|S3'), ('B', float)]) + assert_equal(has_nested_fields(ndtype), False) + ndtype = np.dtype([('A', int), ('B', [('BA', float), ('BB', '|S1')])]) + assert_equal(has_nested_fields(ndtype), True) + diff --git a/numpy/lib/tests/test_function_base.py b/numpy/lib/tests/test_function_base.py index ca8104b53..143e28ae5 100644 --- a/numpy/lib/tests/test_function_base.py +++ b/numpy/lib/tests/test_function_base.py @@ -430,6 +430,44 @@ class TestTrapz(TestCase): #check integral of normal equals 1 assert_almost_equal(sum(r,axis=0),1,7) + def test_ndim(self): + x = linspace(0, 1, 3) + y = linspace(0, 2, 8) + z = linspace(0, 3, 13) + + wx = ones_like(x) * (x[1]-x[0]) + wx[0] /= 2 + wx[-1] /= 2 + wy = ones_like(y) * (y[1]-y[0]) + wy[0] /= 2 + wy[-1] /= 2 + wz = ones_like(z) * (z[1]-z[0]) + wz[0] /= 2 + wz[-1] /= 2 + + q = x[:,None,None] + y[None,:,None] + z[None,None,:] + + qx = (q*wx[:,None,None]).sum(axis=0) + qy = (q*wy[None,:,None]).sum(axis=1) + qz = (q*wz[None,None,:]).sum(axis=2) + + # n-d `x` + r = trapz(q, x=x[:,None,None], axis=0) + assert_almost_equal(r, qx) + r = trapz(q, x=y[None,:,None], axis=1) + assert_almost_equal(r, qy) + r = trapz(q, x=z[None,None,:], axis=2) + assert_almost_equal(r, qz) + + # 1-d `x` + r = trapz(q, x=x, axis=0) + assert_almost_equal(r, qx) + r = trapz(q, x=y, axis=1) + assert_almost_equal(r, qy) + r = trapz(q, x=z, axis=2) + assert_almost_equal(r, qz) + + class TestSinc(TestCase): def test_simple(self): assert(sinc(0)==1) diff --git a/numpy/lib/tests/test_getlimits.py b/numpy/lib/tests/test_getlimits.py index 3fe939b32..325e5a444 100644 --- a/numpy/lib/tests/test_getlimits.py +++ b/numpy/lib/tests/test_getlimits.py @@ -51,5 +51,9 @@ class TestIinfo(TestCase): assert_equal(iinfo(T).max, T(-1)) +def test_instances(): + iinfo(10) + finfo(3.0) + if __name__ == "__main__": run_module_suite() diff --git a/numpy/lib/tests/test_io.py b/numpy/lib/tests/test_io.py index 885fd3616..c38d83add 100644 --- a/numpy/lib/tests/test_io.py +++ b/numpy/lib/tests/test_io.py @@ -1,10 +1,25 @@ -from numpy.testing import * + import numpy as np +import numpy.ma as ma +from numpy.ma.testutils import * + import StringIO from tempfile import NamedTemporaryFile +import sys, time +from datetime import datetime + + +MAJVER, MINVER = sys.version_info[:2] -class RoundtripTest: +def strptime(s, fmt=None): + """This function is available in the datetime module only + from Python >= 2.5. + + """ + return datetime(*time.strptime(s, fmt)[:3]) + +class RoundtripTest(object): def roundtrip(self, save_func, *args, **kwargs): """ save_func : callable @@ -25,7 +40,14 @@ class RoundtripTest: file_on_disk = kwargs.get('file_on_disk', False) if file_on_disk: - target_file = NamedTemporaryFile() + # Do not delete the file on windows, because we can't + # reopen an already opened file on that platform, so we + # need to close the file and reopen it, implying no + # automatic deletion. + if sys.platform == 'win32' and MAJVER >= 2 and MINVER >= 6: + target_file = NamedTemporaryFile(delete=False) + else: + target_file = NamedTemporaryFile() load_file = target_file.name else: target_file = StringIO.StringIO() @@ -37,6 +59,9 @@ class RoundtripTest: target_file.flush() target_file.seek(0) + if sys.platform == 'win32' and not isinstance(target_file, StringIO.StringIO): + target_file.close() + arr_reloaded = np.load(load_file, **load_kwds) self.arr = arr @@ -59,6 +84,7 @@ class RoundtripTest: a = np.array([1, 2, 3, 4], int) self.roundtrip(a) + @np.testing.dec.knownfailureif(sys.platform=='win32', "Fail on Win32") 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'}) @@ -95,6 +121,7 @@ class TestSavezLoad(RoundtripTest, TestCase): class TestSaveTxt(TestCase): + @np.testing.dec.knownfailureif(sys.platform=='win32', "Fail on Win32") def test_array(self): a =np.array([[1, 2], [3, 4]], float) c = StringIO.StringIO() @@ -319,7 +346,6 @@ class Testfromregex(TestCase): assert_array_equal(x, a) def test_record_2(self): - return # pass this test until #736 is resolved c = StringIO.StringIO() c.write('1312 foo\n1534 bar\n4444 qux') c.seek(0) @@ -341,5 +367,447 @@ class Testfromregex(TestCase): assert_array_equal(x, a) +#####-------------------------------------------------------------------------- + + +class TestFromTxt(TestCase): + # + def test_record(self): + "Test w/ explicit dtype" + data = StringIO.StringIO('1 2\n3 4') +# data.seek(0) + test = np.ndfromtxt(data, dtype=[('x', np.int32), ('y', np.int32)]) + control = np.array([(1, 2), (3, 4)], dtype=[('x', 'i4'), ('y', 'i4')]) + assert_equal(test, control) + # + data = StringIO.StringIO('M 64.0 75.0\nF 25.0 60.0') +# data.seek(0) + descriptor = {'names': ('gender','age','weight'), + 'formats': ('S1', 'i4', 'f4')} + control = np.array([('M', 64.0, 75.0), ('F', 25.0, 60.0)], + dtype=descriptor) + test = np.ndfromtxt(data, dtype=descriptor) + assert_equal(test, control) + + def test_array(self): + "Test outputing a standard ndarray" + data = StringIO.StringIO('1 2\n3 4') + control = np.array([[1,2],[3,4]], dtype=int) + test = np.ndfromtxt(data, dtype=int) + assert_array_equal(test, control) + # + data.seek(0) + control = np.array([[1,2],[3,4]], dtype=float) + test = np.loadtxt(data, dtype=float) + assert_array_equal(test, control) + + def test_1D(self): + "Test squeezing to 1D" + control = np.array([1, 2, 3, 4], int) + # + data = StringIO.StringIO('1\n2\n3\n4\n') + test = np.ndfromtxt(data, dtype=int) + assert_array_equal(test, control) + # + data = StringIO.StringIO('1,2,3,4\n') + test = np.ndfromtxt(data, dtype=int, delimiter=',') + assert_array_equal(test, control) + + def test_comments(self): + "Test the stripping of comments" + control = np.array([1, 2, 3, 5], int) + # Comment on its own line + data = StringIO.StringIO('# comment\n1,2,3,5\n') + test = np.ndfromtxt(data, dtype=int, delimiter=',', comments='#') + assert_equal(test, control) + # Comment at the end of a line + data = StringIO.StringIO('1,2,3,5# comment\n') + test = np.ndfromtxt(data, dtype=int, delimiter=',', comments='#') + assert_equal(test, control) + + def test_skiprows(self): + "Test row skipping" + control = np.array([1, 2, 3, 5], int) + # + data = StringIO.StringIO('comment\n1,2,3,5\n') + test = np.ndfromtxt(data, dtype=int, delimiter=',', skiprows=1) + assert_equal(test, control) + # + data = StringIO.StringIO('# comment\n1,2,3,5\n') + test = np.loadtxt(data, dtype=int, delimiter=',', skiprows=1) + assert_equal(test, control) + + def test_header(self): + "Test retrieving a header" + data = StringIO.StringIO('gender age weight\nM 64.0 75.0\nF 25.0 60.0') + test = np.ndfromtxt(data, dtype=None, names=True) + control = {'gender': np.array(['M', 'F']), + 'age': np.array([64.0, 25.0]), + 'weight': np.array([75.0, 60.0])} + assert_equal(test['gender'], control['gender']) + assert_equal(test['age'], control['age']) + assert_equal(test['weight'], control['weight']) + + def test_auto_dtype(self): + "Test the automatic definition of the output dtype" + data = StringIO.StringIO('A 64 75.0 3+4j True\nBCD 25 60.0 5+6j False') + test = np.ndfromtxt(data, dtype=None) + control = [np.array(['A', 'BCD']), + np.array([64, 25]), + np.array([75.0, 60.0]), + np.array([3+4j, 5+6j]), + np.array([True, False]),] + assert_equal(test.dtype.names, ['f0','f1','f2','f3','f4']) + for (i, ctrl) in enumerate(control): + assert_equal(test['f%i' % i], ctrl) + + + def test_auto_dtype_uniform(self): + "Tests whether the output dtype can be uniformized" + data = StringIO.StringIO('1 2 3 4\n5 6 7 8\n') + test = np.ndfromtxt(data, dtype=None) + control = np.array([[1,2,3,4],[5,6,7,8]]) + assert_equal(test, control) + + + def test_fancy_dtype(self): + "Check that a nested dtype isn't MIA" + data = StringIO.StringIO('1,2,3.0\n4,5,6.0\n') + fancydtype = np.dtype([('x', int), ('y', [('t', int), ('s', float)])]) + test = np.ndfromtxt(data, dtype=fancydtype, delimiter=',') + control = np.array([(1,(2,3.0)),(4,(5,6.0))], dtype=fancydtype) + assert_equal(test, control) + + + def test_names_overwrite(self): + "Test overwriting the names of the dtype" + descriptor = {'names': ('g','a','w'), + 'formats': ('S1', 'i4', 'f4')} + data = StringIO.StringIO('M 64.0 75.0\nF 25.0 60.0') + names = ('gender','age','weight') + test = np.ndfromtxt(data, dtype=descriptor, names=names) + descriptor['names'] = names + control = np.array([('M', 64.0, 75.0), + ('F', 25.0, 60.0)], dtype=descriptor) + assert_equal(test, control) + + + def test_commented_header(self): + "Check that names can be retrieved even if the line is commented out." + data = StringIO.StringIO(""" +#gender age weight +M 21 72.100000 +F 35 58.330000 +M 33 21.99 + """) + # The # is part of the first name and should be deleted automatically. + test = np.genfromtxt(data, names=True, dtype=None) + ctrl = np.array([('M', 21, 72.1), ('F', 35, 58.33), ('M', 33, 21.99)], + dtype=[('gender','|S1'), ('age', int), ('weight', float)]) + assert_equal(test, ctrl) + # Ditto, but we should get rid of the first element + data = StringIO.StringIO(""" +# gender age weight +M 21 72.100000 +F 35 58.330000 +M 33 21.99 + """) + test = np.genfromtxt(data, names=True, dtype=None) + assert_equal(test, ctrl) + + + def test_autonames_and_usecols(self): + "Tests names and usecols" + data = StringIO.StringIO('A B C D\n aaaa 121 45 9.1') + test = np.ndfromtxt(data, usecols=('A', 'C', 'D'), + names=True, dtype=None) + control = np.array(('aaaa', 45, 9.1), + dtype=[('A', '|S4'), ('C', int), ('D', float)]) + assert_equal(test, control) + + + def test_converters_with_usecols(self): + "Test the combination user-defined converters and usecol" + data = StringIO.StringIO('1,2,3,,5\n6,7,8,9,10\n') + test = np.ndfromtxt(data, dtype=int, delimiter=',', + converters={3:lambda s: int(s or -999)}, + usecols=(1, 3, )) + control = np.array([[2, -999], [7, 9]], int) + assert_equal(test, control) + + def test_converters_with_usecols_and_names(self): + "Tests names and usecols" + data = StringIO.StringIO('A B C D\n aaaa 121 45 9.1') + test = np.ndfromtxt(data, usecols=('A', 'C', 'D'), names=True, + dtype=None, converters={'C':lambda s: 2 * int(s)}) + control = np.array(('aaaa', 90, 9.1), + dtype=[('A', '|S4'), ('C', int), ('D', float)]) + assert_equal(test, control) + + def test_converters_cornercases(self): + "Test the conversion to datetime." + converter = {'date': lambda s: strptime(s, '%Y-%m-%d %H:%M:%SZ')} + data = StringIO.StringIO('2009-02-03 12:00:00Z, 72214.0') + test = np.ndfromtxt(data, delimiter=',', dtype=None, + names=['date','stid'], converters=converter) + control = np.array((datetime(2009,02,03), 72214.), + dtype=[('date', np.object_), ('stid', float)]) + assert_equal(test, control) + + + def test_unused_converter(self): + "Test whether unused converters are forgotten" + data = StringIO.StringIO("1 21\n 3 42\n") + test = np.ndfromtxt(data, usecols=(1,), + converters={0: lambda s: int(s, 16)}) + assert_equal(test, [21, 42]) + # + data.seek(0) + test = np.ndfromtxt(data, usecols=(1,), + converters={1: lambda s: int(s, 16)}) + assert_equal(test, [33, 66]) + + + def test_dtype_with_converters(self): + dstr = "2009; 23; 46" + test = np.ndfromtxt(StringIO.StringIO(dstr,), + delimiter=";", dtype=float, converters={0:str}) + control = np.array([('2009', 23., 46)], + dtype=[('f0','|S4'), ('f1', float), ('f2', float)]) + assert_equal(test, control) + test = np.ndfromtxt(StringIO.StringIO(dstr,), + delimiter=";", dtype=float, converters={0:float}) + control = np.array([2009., 23., 46],) + assert_equal(test, control) + + + def test_dtype_with_object(self): + "Test using an explicit dtype with an object" + from datetime import date + import time + data = """ + 1; 2001-01-01 + 2; 2002-01-31 + """ + ndtype = [('idx', int), ('code', np.object)] + func = lambda s: strptime(s.strip(), "%Y-%m-%d") + converters = {1: func} + test = np.genfromtxt(StringIO.StringIO(data), delimiter=";", dtype=ndtype, + converters=converters) + control = np.array([(1, datetime(2001,1,1)), (2, datetime(2002,1,31))], + dtype=ndtype) + assert_equal(test, control) + # + ndtype = [('nest', [('idx', int), ('code', np.object)])] + try: + test = np.genfromtxt(StringIO.StringIO(data), delimiter=";", + dtype=ndtype, converters=converters) + except NotImplementedError: + pass + else: + errmsg = "Nested dtype involving objects should be supported." + raise AssertionError(errmsg) + + + def test_userconverters_with_explicit_dtype(self): + "Test user_converters w/ explicit (standard) dtype" + data = StringIO.StringIO('skip,skip,2001-01-01,1.0,skip') + test = np.genfromtxt(data, delimiter=",", names=None, dtype=float, + usecols=(2, 3), converters={2: str}) + control = np.array([('2001-01-01', 1.)], + dtype=[('', '|S10'), ('', float)]) + assert_equal(test, control) + + + def test_spacedelimiter(self): + "Test space delimiter" + data = StringIO.StringIO("1 2 3 4 5\n6 7 8 9 10") + test = np.ndfromtxt(data) + control = np.array([[ 1., 2., 3., 4., 5.], + [ 6., 7., 8., 9.,10.]]) + assert_equal(test, control) + + + def test_missing(self): + data = StringIO.StringIO('1,2,3,,5\n') + test = np.ndfromtxt(data, dtype=int, delimiter=',', \ + converters={3:lambda s: int(s or -999)}) + control = np.array([1, 2, 3, -999, 5], int) + assert_equal(test, control) + + + def test_usecols(self): + "Test the selection of columns" + # Select 1 column + control = np.array( [[1, 2], [3, 4]], float) + data = StringIO.StringIO() + np.savetxt(data, control) + data.seek(0) + test = np.ndfromtxt(data, dtype=float, usecols=(1,)) + assert_equal(test, control[:, 1]) + # + control = np.array( [[1, 2, 3], [3, 4, 5]], float) + data = StringIO.StringIO() + np.savetxt(data, control) + data.seek(0) + test = np.ndfromtxt(data, dtype=float, usecols=(1, 2)) + assert_equal(test, control[:, 1:]) + # Testing with arrays instead of tuples. + data.seek(0) + test = np.ndfromtxt(data, dtype=float, usecols=np.array([1, 2])) + assert_equal(test, control[:, 1:]) + # Checking with dtypes defined converters. + data = StringIO.StringIO("""JOE 70.1 25.3\nBOB 60.5 27.9""") + names = ['stid', 'temp'] + dtypes = ['S4', 'f8'] + test = np.ndfromtxt(data, usecols=(0, 2), dtype=zip(names, dtypes)) + assert_equal(test['stid'], ["JOE", "BOB"]) + assert_equal(test['temp'], [25.3, 27.9]) + + + def test_empty_file(self): + "Test that an empty file raises the proper exception" + data = StringIO.StringIO() + assert_raises(IOError, np.ndfromtxt, data) + + + def test_fancy_dtype_alt(self): + "Check that a nested dtype isn't MIA" + data = StringIO.StringIO('1,2,3.0\n4,5,6.0\n') + fancydtype = np.dtype([('x', int), ('y', [('t', int), ('s', float)])]) + test = np.mafromtxt(data, dtype=fancydtype, delimiter=',') + control = ma.array([(1,(2,3.0)),(4,(5,6.0))], dtype=fancydtype) + assert_equal(test, control) + + + def test_withmissing(self): + data = StringIO.StringIO('A,B\n0,1\n2,N/A') + test = np.mafromtxt(data, dtype=None, delimiter=',', missing='N/A', + names=True) + control = ma.array([(0, 1), (2, -1)], + mask=[(False, False), (False, True)], + dtype=[('A', np.int), ('B', np.int)]) + assert_equal(test, control) + assert_equal(test.mask, control.mask) + # + data.seek(0) + test = np.mafromtxt(data, delimiter=',', missing='N/A', names=True) + control = ma.array([(0, 1), (2, -1)], + mask=[[False, False], [False, True]],) + assert_equal(test, control) + assert_equal(test.mask, control.mask) + + + def test_user_missing_values(self): + datastr ="A, B, C\n0, 0., 0j\n1, N/A, 1j\n-9, 2.2, N/A\n3, -99, 3j" + data = StringIO.StringIO(datastr) + basekwargs = dict(dtype=None, delimiter=',', names=True, missing='N/A') + mdtype = [('A', int), ('B', float), ('C', complex)] + # + test = np.mafromtxt(data, **basekwargs) + control = ma.array([( 0, 0.0, 0j), (1, -999, 1j), + ( -9, 2.2, -999j), (3, -99, 3j)], + mask=[(0, 0, 0), (0, 1, 0), (0, 0, 1), (0, 0, 0)], + dtype=mdtype) + assert_equal(test, control) + # + data.seek(0) + test = np.mafromtxt(data, + missing_values={0:-9, 1:-99, 2:-999j}, **basekwargs) + control = ma.array([( 0, 0.0, 0j), (1, -999, 1j), + ( -9, 2.2, -999j), (3, -99, 3j)], + mask=[(0, 0, 0), (0, 1, 0), (1, 0, 1), (0, 1, 0)], + dtype=mdtype) + assert_equal(test, control) + # + data.seek(0) + test = np.mafromtxt(data, + missing_values={0:-9, 'B':-99, 'C':-999j}, + **basekwargs) + control = ma.array([( 0, 0.0, 0j), (1, -999, 1j), + ( -9, 2.2, -999j), (3, -99, 3j)], + mask=[(0, 0, 0), (0, 1, 0), (1, 0, 1), (0, 1, 0)], + dtype=mdtype) + assert_equal(test, control) + + + def test_withmissing_float(self): + data = StringIO.StringIO('A,B\n0,1.5\n2,-999.00') + test = np.mafromtxt(data, dtype=None, delimiter=',', missing='-999.0', + names=True,) + control = ma.array([(0, 1.5), (2, -1.)], + mask=[(False, False), (False, True)], + dtype=[('A', np.int), ('B', np.float)]) + assert_equal(test, control) + assert_equal(test.mask, control.mask) + + + def test_with_masked_column_uniform(self): + "Test masked column" + data = StringIO.StringIO('1 2 3\n4 5 6\n') + test = np.genfromtxt(data, missing='2,5', dtype=None, usemask=True) + control = ma.array([[1, 2, 3], [4, 5, 6]], mask=[[0, 1, 0],[0, 1, 0]]) + assert_equal(test, control) + + def test_with_masked_column_various(self): + "Test masked column" + data = StringIO.StringIO('True 2 3\nFalse 5 6\n') + test = np.genfromtxt(data, missing='2,5', dtype=None, usemask=True) + control = ma.array([(1, 2, 3), (0, 5, 6)], + mask=[(0, 1, 0),(0, 1, 0)], + dtype=[('f0', bool), ('f1', bool), ('f2', int)]) + assert_equal(test, control) + + + def test_recfromtxt(self): + # + data = StringIO.StringIO('A,B\n0,1\n2,3') + test = np.recfromtxt(data, delimiter=',', missing='N/A', names=True) + control = np.array([(0, 1), (2, 3)], + dtype=[('A', np.int), ('B', np.int)]) + self.failUnless(isinstance(test, np.recarray)) + assert_equal(test, control) + # + data = StringIO.StringIO('A,B\n0,1\n2,N/A') + test = np.recfromtxt(data, dtype=None, delimiter=',', missing='N/A', + names=True, usemask=True) + control = ma.array([(0, 1), (2, -1)], + mask=[(False, False), (False, True)], + dtype=[('A', np.int), ('B', np.int)]) + assert_equal(test, control) + assert_equal(test.mask, control.mask) + assert_equal(test.A, [0, 2]) + + + def test_recfromcsv(self): + # + data = StringIO.StringIO('A,B\n0,1\n2,3') + test = np.recfromcsv(data, missing='N/A', + names=True, case_sensitive=True) + control = np.array([(0, 1), (2, 3)], + dtype=[('A', np.int), ('B', np.int)]) + self.failUnless(isinstance(test, np.recarray)) + assert_equal(test, control) + # + data = StringIO.StringIO('A,B\n0,1\n2,N/A') + test = np.recfromcsv(data, dtype=None, missing='N/A', + names=True, case_sensitive=True, usemask=True) + control = ma.array([(0, 1), (2, -1)], + mask=[(False, False), (False, True)], + dtype=[('A', np.int), ('B', np.int)]) + assert_equal(test, control) + assert_equal(test.mask, control.mask) + assert_equal(test.A, [0, 2]) + # + data = StringIO.StringIO('A,B\n0,1\n2,3') + test = np.recfromcsv(data, missing='N/A',) + control = np.array([(0, 1), (2, 3)], + dtype=[('a', np.int), ('b', np.int)]) + self.failUnless(isinstance(test, np.recarray)) + assert_equal(test, control) + + + + if __name__ == "__main__": run_module_suite() diff --git a/numpy/lib/tests/test_recfunctions.py b/numpy/lib/tests/test_recfunctions.py new file mode 100644 index 000000000..424d60ae4 --- /dev/null +++ b/numpy/lib/tests/test_recfunctions.py @@ -0,0 +1,606 @@ +import sys + +import numpy as np +import numpy.ma as ma +from numpy.ma.testutils import * + +from numpy.ma.mrecords import MaskedRecords + +from numpy.lib.recfunctions import * +get_names = np.lib.recfunctions.get_names +get_names_flat = np.lib.recfunctions.get_names_flat +zip_descr = np.lib.recfunctions.zip_descr + +class TestRecFunctions(TestCase): + """ + Misc tests + """ + # + def setUp(self): + x = np.array([1, 2,]) + y = np.array([10, 20, 30]) + z = np.array([('A', 1.), ('B', 2.)], + dtype=[('A', '|S3'), ('B', float)]) + w = np.array([(1, (2, 3.0)), (4, (5, 6.0))], + dtype=[('a', int), ('b', [('ba', float), ('bb', int)])]) + self.data = (w, x, y, z) + + + def test_zip_descr(self): + "Test zip_descr" + (w, x, y, z) = self.data + # Std array + test = zip_descr((x, x), flatten=True) + assert_equal(test, + np.dtype([('', int), ('', int)])) + test = zip_descr((x, x), flatten=False) + assert_equal(test, + np.dtype([('', int), ('', int)])) + # Std & flexible-dtype + test = zip_descr((x, z), flatten=True) + assert_equal(test, + np.dtype([('', int), ('A', '|S3'), ('B', float)])) + test = zip_descr((x, z), flatten=False) + assert_equal(test, + np.dtype([('', int), + ('', [('A', '|S3'), ('B', float)])])) + # Standard & nested dtype + test = zip_descr((x, w), flatten=True) + assert_equal(test, + np.dtype([('', int), + ('a', int), + ('ba', float), ('bb', int)])) + test = zip_descr((x, w), flatten=False) + assert_equal(test, + np.dtype([('', int), + ('', [('a', int), + ('b', [('ba', float), ('bb', int)])])])) + + + def test_drop_fields(self): + "Test drop_fields" + a = np.array([(1, (2, 3.0)), (4, (5, 6.0))], + dtype=[('a', int), ('b', [('ba', float), ('bb', int)])]) + # A basic field + test = drop_fields(a, 'a') + control = np.array([((2, 3.0),), ((5, 6.0),)], + dtype=[('b', [('ba', float), ('bb', int)])]) + assert_equal(test, control) + # Another basic field (but nesting two fields) + test = drop_fields(a, 'b') + control = np.array([(1,), (4,)], dtype=[('a', int)]) + assert_equal(test, control) + # A nested sub-field + test = drop_fields(a, ['ba',]) + control = np.array([(1, (3.0,)), (4, (6.0,))], + dtype=[('a', int), ('b', [('bb', int)])]) + assert_equal(test, control) + # All the nested sub-field from a field: zap that field + test = drop_fields(a, ['ba', 'bb']) + control = np.array([(1,), (4,)], dtype=[('a', int)]) + assert_equal(test, control) + # + test = drop_fields(a, ['a', 'b']) + assert(test is None) + + + def test_rename_fields(self): + "Tests rename fields" + a = np.array([(1, (2, [3.0, 30.])), (4, (5, [6.0, 60.]))], + dtype=[('a', int), + ('b', [('ba', float), ('bb', (float, 2))])]) + test = rename_fields(a, {'a':'A', 'bb':'BB'}) + newdtype = [('A', int), ('b', [('ba', float), ('BB', (float, 2))])] + control = a.view(newdtype) + assert_equal(test.dtype, newdtype) + assert_equal(test, control) + + + def test_get_names(self): + "Tests get_names" + ndtype = np.dtype([('A', '|S3'), ('B', float)]) + test = get_names(ndtype) + assert_equal(test, ('A', 'B')) + # + ndtype = np.dtype([('a', int), ('b', [('ba', float), ('bb', int)])]) + test = get_names(ndtype) + assert_equal(test, ('a', ('b', ('ba', 'bb')))) + + + def test_get_names_flat(self): + "Test get_names_flat" + ndtype = np.dtype([('A', '|S3'), ('B', float)]) + test = get_names_flat(ndtype) + assert_equal(test, ('A', 'B')) + # + ndtype = np.dtype([('a', int), ('b', [('ba', float), ('bb', int)])]) + test = get_names_flat(ndtype) + assert_equal(test, ('a', 'b', 'ba', 'bb')) + + + def test_get_fieldstructure(self): + "Test get_fieldstructure" + # No nested fields + ndtype = np.dtype([('A', '|S3'), ('B', float)]) + test = get_fieldstructure(ndtype) + assert_equal(test, {'A':[], 'B':[]}) + # One 1-nested field + ndtype = np.dtype([('A', int), ('B', [('BA', float), ('BB', '|S1')])]) + test = get_fieldstructure(ndtype) + assert_equal(test, {'A': [], 'B': [], 'BA':['B',], 'BB':['B']}) + # One 2-nested fields + ndtype = np.dtype([('A', int), + ('B', [('BA', int), + ('BB', [('BBA', int), ('BBB', int)])])]) + test = get_fieldstructure(ndtype) + control = {'A': [], 'B': [], 'BA': ['B'], 'BB': ['B'], + 'BBA': ['B', 'BB'], 'BBB': ['B', 'BB']} + assert_equal(test, control) + + + @np.testing.dec.knownfailureif(sys.platform=='win32', "Fail on Win32") + def test_find_duplicates(self): + "Test find_duplicates" + a = ma.array([(2, (2., 'B')), (1, (2., 'B')), (2, (2., 'B')), + (1, (1., 'B')), (2, (2., 'B')), (2, (2., 'C'))], + mask=[(0, (0, 0)), (0, (0, 0)), (0, (0, 0)), + (0, (0, 0)), (1, (0, 0)), (0, (1, 0))], + dtype=[('A', int), ('B', [('BA', float), ('BB', '|S1')])]) + # + test = find_duplicates(a, ignoremask=False, return_index=True) + control = [0, 2] + assert_equal(test[-1], control) + assert_equal(test[0], a[control]) + # + test = find_duplicates(a, key='A', return_index=True) + control = [1, 3, 0, 2, 5] + assert_equal(test[-1], control) + assert_equal(test[0], a[control]) + # + test = find_duplicates(a, key='B', return_index=True) + control = [0, 1, 2, 4] + assert_equal(test[-1], control) + assert_equal(test[0], a[control]) + # + test = find_duplicates(a, key='BA', return_index=True) + control = [0, 1, 2, 4] + assert_equal(test[-1], control) + assert_equal(test[0], a[control]) + # + test = find_duplicates(a, key='BB', return_index=True) + control = [0, 1, 2, 3, 4] + assert_equal(test[-1], control) + assert_equal(test[0], a[control]) + + + @np.testing.dec.knownfailureif(sys.platform=='win32', "Fail on Win32") + def test_find_duplicates_ignoremask(self): + "Test the ignoremask option of find_duplicates" + ndtype = [('a', int)] + a = ma.array([1, 1, 1, 2, 2, 3, 3], + mask=[0, 0, 1, 0, 0, 0, 1]).view(ndtype) + test = find_duplicates(a, ignoremask=True, return_index=True) + control = [0, 1, 3, 4] + assert_equal(test[-1], control) + assert_equal(test[0], a[control]) + # + test = find_duplicates(a, ignoremask=False, return_index=True) + control = [0, 1, 3, 4, 6, 2] + try: + assert_equal(test[-1], control) + except AssertionError: + assert_equal(test[-1], [0, 1, 3, 4, 2, 6]) + assert_equal(test[0], a[control]) + + +class TestRecursiveFillFields(TestCase): + """ + Test recursive_fill_fields. + """ + def test_simple_flexible(self): + "Test recursive_fill_fields on flexible-array" + a = np.array([(1, 10.), (2, 20.)], dtype=[('A', int), ('B', float)]) + b = np.zeros((3,), dtype=a.dtype) + test = recursive_fill_fields(a, b) + control = np.array([(1, 10.), (2, 20.), (0, 0.)], + dtype=[('A', int), ('B', float)]) + assert_equal(test, control) + # + def test_masked_flexible(self): + "Test recursive_fill_fields on masked flexible-array" + a = ma.array([(1, 10.), (2, 20.)], mask=[(0, 1), (1, 0)], + dtype=[('A', int), ('B', float)]) + b = ma.zeros((3,), dtype=a.dtype) + test = recursive_fill_fields(a, b) + control = ma.array([(1, 10.), (2, 20.), (0, 0.)], + mask=[(0, 1), (1, 0), (0, 0)], + dtype=[('A', int), ('B', float)]) + assert_equal(test, control) + # + + + +class TestMergeArrays(TestCase): + """ + Test merge_arrays + """ + def setUp(self): + x = np.array([1, 2,]) + y = np.array([10, 20, 30]) + z = np.array([('A', 1.), ('B', 2.)], dtype=[('A', '|S3'), ('B', float)]) + w = np.array([(1, (2, 3.0)), (4, (5, 6.0))], + dtype=[('a', int), ('b', [('ba', float), ('bb', int)])]) + self.data = (w, x, y, z) + # + def test_solo(self): + "Test merge_arrays on a single array." + (_, x, _, z) = self.data + # + test = merge_arrays(x) + control = np.array([(1,), (2,)], dtype=[('f0', int)]) + assert_equal(test, control) + test = merge_arrays((x,)) + assert_equal(test, control) + # + test = merge_arrays(z, flatten=False) + assert_equal(test, z) + test = merge_arrays(z, flatten=True) + assert_equal(test, z) + # + def test_solo_w_flatten(self): + "Test merge_arrays on a single array w & w/o flattening" + w = self.data[0] + test = merge_arrays(w, flatten=False) + assert_equal(test, w) + # + test = merge_arrays(w, flatten=True) + control = np.array([(1, 2, 3.0), (4, 5, 6.0)], + dtype=[('a', int), ('ba', float), ('bb', int)]) + assert_equal(test, control) + # + def test_standard(self): + "Test standard & standard" + # Test merge arrays + (_, x, y, _) = self.data + test = merge_arrays((x, y), usemask=False) + control = np.array([(1, 10), (2, 20), (-1, 30)], + dtype=[('f0', int), ('f1', int)]) + assert_equal(test, control) + # + test = merge_arrays((x, y), usemask=True) + control = ma.array([(1, 10), (2, 20), (-1, 30)], + mask=[(0, 0), (0, 0), (1, 0)], + dtype=[('f0', int), ('f1', int)]) + assert_equal(test, control) + assert_equal(test.mask, control.mask) + # + def test_flatten(self): + "Test standard & flexible" + (_, x, _, z) = self.data + test = merge_arrays((x, z), flatten=True) + control = np.array([(1, 'A', 1.), (2, 'B', 2.)], + dtype=[('f0', int), ('A', '|S3'), ('B', float)]) + assert_equal(test, control) + # + test = merge_arrays((x, z), flatten=False) + control = np.array([(1, ('A', 1.)), (2, ('B', 2.))], + dtype=[('f0', int), + ('f1', [('A', '|S3'), ('B', float)])]) + assert_equal(test, control) + # + def test_flatten_wflexible(self): + "Test flatten standard & nested" + (w, x, _, _) = self.data + test = merge_arrays((x, w), flatten=True) + control = np.array([(1, 1, 2, 3.0), (2, 4, 5, 6.0)], + dtype=[('f0', int), + ('a', int), ('ba', float), ('bb', int)]) + assert_equal(test, control) + # + test = merge_arrays((x, w), flatten=False) + controldtype = dtype=[('f0', int), + ('f1', [('a', int), + ('b', [('ba', float), ('bb', int)])])] + control = np.array([(1., (1, (2, 3.0))), (2, (4, (5, 6.0)))], + dtype=controldtype) + # + def test_wmasked_arrays(self): + "Test merge_arrays masked arrays" + (_, x, _, _) = self.data + mx = ma.array([1, 2, 3], mask=[1, 0, 0]) + test = merge_arrays((x, mx), usemask=True) + control = ma.array([(1, 1), (2, 2), (-1, 3)], + mask=[(0, 1), (0, 0), (1, 0)], + dtype=[('f0', int), ('f1', int)]) + assert_equal(test, control) + test = merge_arrays((x, mx), usemask=True, asrecarray=True) + assert_equal(test, control) + assert(isinstance(test, MaskedRecords)) + # + def test_w_singlefield(self): + "Test single field" + test = merge_arrays((np.array([1, 2]).view([('a', int)]), + np.array([10., 20., 30.])),) + control = ma.array([(1, 10.), (2, 20.), (-1, 30.)], + mask=[(0, 0), (0, 0), (1, 0)], + dtype=[('a', int), ('f1', float)]) + assert_equal(test, control) + # + def test_w_shorter_flex(self): + "Test merge_arrays w/ a shorter flexndarray." + z = self.data[-1] + test = merge_arrays((z, np.array([10, 20, 30]).view([('C', int)]))) + control = np.array([('A', 1., 10), ('B', 2., 20), ('-1', -1, 20)], + dtype=[('A', '|S3'), ('B', float), ('C', int)]) + + + +class TestAppendFields(TestCase): + """ + Test append_fields + """ + def setUp(self): + x = np.array([1, 2,]) + y = np.array([10, 20, 30]) + z = np.array([('A', 1.), ('B', 2.)], dtype=[('A', '|S3'), ('B', float)]) + w = np.array([(1, (2, 3.0)), (4, (5, 6.0))], + dtype=[('a', int), ('b', [('ba', float), ('bb', int)])]) + self.data = (w, x, y, z) + # + def test_append_single(self): + "Test simple case" + (_, x, _, _) = self.data + test = append_fields(x, 'A', data=[10, 20, 30]) + control = ma.array([(1, 10), (2, 20), (-1, 30)], + mask=[(0, 0), (0, 0), (1, 0)], + dtype=[('f0', int), ('A', int)],) + assert_equal(test, control) + # + def test_append_double(self): + "Test simple case" + (_, x, _, _) = self.data + test = append_fields(x, ('A', 'B'), data=[[10, 20, 30], [100, 200]]) + control = ma.array([(1, 10, 100), (2, 20, 200), (-1, 30, -1)], + mask=[(0, 0, 0), (0, 0, 0), (1, 0, 1)], + dtype=[('f0', int), ('A', int), ('B', int)],) + assert_equal(test, control) + # + def test_append_on_flex(self): + "Test append_fields on flexible type arrays" + z = self.data[-1] + test = append_fields(z, 'C', data=[10, 20, 30]) + control = ma.array([('A', 1., 10), ('B', 2., 20), (-1, -1., 30)], + mask=[(0, 0, 0), (0, 0, 0), (1, 1, 0)], + dtype=[('A', '|S3'), ('B', float), ('C', int)],) + assert_equal(test, control) + # + def test_append_on_nested(self): + "Test append_fields on nested fields" + w = self.data[0] + test = append_fields(w, 'C', data=[10, 20, 30]) + control = ma.array([(1, (2, 3.0), 10), + (4, (5, 6.0), 20), + (-1, (-1, -1.), 30)], + mask=[(0, (0, 0), 0), (0, (0, 0), 0), (1, (1, 1), 0)], + dtype=[('a', int), + ('b', [('ba', float), ('bb', int)]), + ('C', int)],) + assert_equal(test, control) + + + +class TestStackArrays(TestCase): + """ + Test stack_arrays + """ + def setUp(self): + x = np.array([1, 2,]) + y = np.array([10, 20, 30]) + z = np.array([('A', 1.), ('B', 2.)], dtype=[('A', '|S3'), ('B', float)]) + w = np.array([(1, (2, 3.0)), (4, (5, 6.0))], + dtype=[('a', int), ('b', [('ba', float), ('bb', int)])]) + self.data = (w, x, y, z) + # + def test_solo(self): + "Test stack_arrays on single arrays" + (_, x, _, _) = self.data + test = stack_arrays((x,)) + assert_equal(test, x) + self.failUnless(test is x) + # + test = stack_arrays(x) + assert_equal(test, x) + self.failUnless(test is x) + # + def test_unnamed_fields(self): + "Tests combinations of arrays w/o named fields" + (_, x, y, _) = self.data + # + test = stack_arrays((x, x), usemask=False) + control = np.array([1, 2, 1, 2]) + assert_equal(test, control) + # + test = stack_arrays((x, y), usemask=False) + control = np.array([1, 2, 10, 20, 30]) + assert_equal(test, control) + # + test = stack_arrays((y, x), usemask=False) + control = np.array([10, 20, 30, 1, 2]) + assert_equal(test, control) + # + def test_unnamed_and_named_fields(self): + "Test combination of arrays w/ & w/o named fields" + (_, x, _, z) = self.data + # + test = stack_arrays((x, z)) + control = ma.array([(1, -1, -1), (2, -1, -1), + (-1, 'A', 1), (-1, 'B', 2)], + mask=[(0, 1, 1), (0, 1, 1), + (1, 0, 0), (1, 0, 0)], + dtype=[('f0', int), ('A', '|S3'), ('B', float)]) + assert_equal(test, control) + assert_equal(test.mask, control.mask) + # + test = stack_arrays((z, x)) + control = ma.array([('A', 1, -1), ('B', 2, -1), + (-1, -1, 1), (-1, -1, 2),], + mask=[(0, 0, 1), (0, 0, 1), + (1, 1, 0), (1, 1, 0)], + dtype=[('A', '|S3'), ('B', float), ('f2', int)]) + assert_equal(test, control) + assert_equal(test.mask, control.mask) + # + test = stack_arrays((z, z, x)) + control = ma.array([('A', 1, -1), ('B', 2, -1), + ('A', 1, -1), ('B', 2, -1), + (-1, -1, 1), (-1, -1, 2),], + mask=[(0, 0, 1), (0, 0, 1), + (0, 0, 1), (0, 0, 1), + (1, 1, 0), (1, 1, 0)], + dtype=[('A', '|S3'), ('B', float), ('f2', int)]) + assert_equal(test, control) + # + def test_matching_named_fields(self): + "Test combination of arrays w/ matching field names" + (_, x, _, z) = self.data + zz = np.array([('a', 10., 100.), ('b', 20., 200.), ('c', 30., 300.)], + dtype=[('A', '|S3'), ('B', float), ('C', float)]) + test = stack_arrays((z, zz)) + control = ma.array([('A', 1, -1), ('B', 2, -1), + ('a', 10., 100.), ('b', 20., 200.), ('c', 30., 300.)], + dtype=[('A', '|S3'), ('B', float), ('C', float)], + mask=[(0, 0, 1), (0, 0, 1), + (0, 0, 0), (0, 0, 0), (0, 0, 0)]) + assert_equal(test, control) + assert_equal(test.mask, control.mask) + # + test = stack_arrays((z, zz, x)) + ndtype = [('A', '|S3'), ('B', float), ('C', float), ('f3', int)] + control = ma.array([('A', 1, -1, -1), ('B', 2, -1, -1), + ('a', 10., 100., -1), ('b', 20., 200., -1), + ('c', 30., 300., -1), + (-1, -1, -1, 1), (-1, -1, -1, 2)], + dtype=ndtype, + mask=[(0, 0, 1, 1), (0, 0, 1, 1), + (0, 0, 0, 1), (0, 0, 0, 1), (0, 0, 0, 1), + (1, 1, 1, 0), (1, 1, 1, 0)]) + assert_equal(test, control) + assert_equal(test.mask, control.mask) + + + def test_defaults(self): + "Test defaults: no exception raised if keys of defaults are not fields." + (_, _, _, z) = self.data + zz = np.array([('a', 10., 100.), ('b', 20., 200.), ('c', 30., 300.)], + dtype=[('A', '|S3'), ('B', float), ('C', float)]) + defaults = {'A':'???', 'B':-999., 'C':-9999., 'D':-99999.} + test = stack_arrays((z, zz), defaults=defaults) + control = ma.array([('A', 1, -9999.), ('B', 2, -9999.), + ('a', 10., 100.), ('b', 20., 200.), ('c', 30., 300.)], + dtype=[('A', '|S3'), ('B', float), ('C', float)], + mask=[(0, 0, 1), (0, 0, 1), + (0, 0, 0), (0, 0, 0), (0, 0, 0)]) + assert_equal(test, control) + assert_equal(test.data, control.data) + assert_equal(test.mask, control.mask) + + + def test_autoconversion(self): + "Tests autoconversion" + adtype = [('A', int), ('B', bool), ('C', float)] + a = ma.array([(1, 2, 3)], mask=[(0, 1, 0)], dtype=adtype) + bdtype = [('A', int), ('B', float), ('C', float)] + b = ma.array([(4, 5, 6)], dtype=bdtype) + control = ma.array([(1, 2, 3), (4, 5, 6)], mask=[(0, 1, 0), (0, 0, 0)], + dtype=bdtype) + test = stack_arrays((a, b), autoconvert=True) + assert_equal(test, control) + assert_equal(test.mask, control.mask) + try: + test = stack_arrays((a, b), autoconvert=False) + except TypeError: + pass + else: + raise AssertionError + + + def test_checktitles(self): + "Test using titles in the field names" + adtype = [(('a', 'A'), int), (('b', 'B'), bool), (('c', 'C'), float)] + a = ma.array([(1, 2, 3)], mask=[(0, 1, 0)], dtype=adtype) + bdtype = [(('a', 'A'), int), (('b', 'B'), bool), (('c', 'C'), float)] + b = ma.array([(4, 5, 6)], dtype=bdtype) + test = stack_arrays((a, b)) + control = ma.array([(1, 2, 3), (4, 5, 6)], mask=[(0, 1, 0), (0, 0, 0)], + dtype=bdtype) + assert_equal(test, control) + assert_equal(test.mask, control.mask) + + +class TestJoinBy(TestCase): + # + def test_base(self): + "Basic test of join_by" + a = np.array(zip(np.arange(10), np.arange(50, 60), np.arange(100, 110)), + dtype=[('a', int), ('b', int), ('c', int)]) + b = np.array(zip(np.arange(5, 15), np.arange(65, 75), np.arange(100, 110)), + dtype=[('a', int), ('b', int), ('d', int)]) + # + test = join_by('a', a, b, jointype='inner') + control = np.array([(5, 55, 65, 105, 100), (6, 56, 66, 106, 101), + (7, 57, 67, 107, 102), (8, 58, 68, 108, 103), + (9, 59, 69, 109, 104)], + dtype=[('a', int), ('b1', int), ('b2', int), + ('c', int), ('d', int)]) + assert_equal(test, control) + # + test = join_by(('a', 'b'), a, b) + control = np.array([(5, 55, 105, 100), (6, 56, 106, 101), + (7, 57, 107, 102), (8, 58, 108, 103), + (9, 59, 109, 104)], + dtype=[('a', int), ('b', int), + ('c', int), ('d', int)]) + # + test = join_by(('a', 'b'), a, b, 'outer') + control = ma.array([( 0, 50, 100, -1), ( 1, 51, 101, -1), + ( 2, 52, 102, -1), ( 3, 53, 103, -1), + ( 4, 54, 104, -1), ( 5, 55, 105, -1), + ( 5, 65, -1, 100), ( 6, 56, 106, -1), + ( 6, 66, -1, 101), ( 7, 57, 107, -1), + ( 7, 67, -1, 102), ( 8, 58, 108, -1), + ( 8, 68, -1, 103), ( 9, 59, 109, -1), + ( 9, 69, -1, 104), (10, 70, -1, 105), + (11, 71, -1, 106), (12, 72, -1, 107), + (13, 73, -1, 108), (14, 74, -1, 109)], + mask=[( 0, 0, 0, 1), ( 0, 0, 0, 1), + ( 0, 0, 0, 1), ( 0, 0, 0, 1), + ( 0, 0, 0, 1), ( 0, 0, 0, 1), + ( 0, 0, 1, 0), ( 0, 0, 0, 1), + ( 0, 0, 1, 0), ( 0, 0, 0, 1), + ( 0, 0, 1, 0), ( 0, 0, 0, 1), + ( 0, 0, 1, 0), ( 0, 0, 0, 1), + ( 0, 0, 1, 0), ( 0, 0, 1, 0), + ( 0, 0, 1, 0), ( 0, 0, 1, 0), + ( 0, 0, 1, 0), ( 0, 0, 1, 0)], + dtype=[('a', int), ('b', int), + ('c', int), ('d', int)]) + assert_equal(test, control) + # + test = join_by(('a', 'b'), a, b, 'leftouter') + control = ma.array([(0, 50, 100, -1), (1, 51, 101, -1), + (2, 52, 102, -1), (3, 53, 103, -1), + (4, 54, 104, -1), (5, 55, 105, -1), + (6, 56, 106, -1), (7, 57, 107, -1), + (8, 58, 108, -1), (9, 59, 109, -1)], + mask=[(0, 0, 0, 1), (0, 0, 0, 1), + (0, 0, 0, 1), (0, 0, 0, 1), + (0, 0, 0, 1), (0, 0, 0, 1), + (0, 0, 0, 1), (0, 0, 0, 1), + (0, 0, 0, 1), (0, 0, 0, 1)], + dtype=[('a', int), ('b', int), ('c', int), ('d', int)]) + + + + +if __name__ == '__main__': + run_module_suite() diff --git a/numpy/lib/utils.py b/numpy/lib/utils.py index d749f00b6..9717a7a8f 100644 --- a/numpy/lib/utils.py +++ b/numpy/lib/utils.py @@ -699,11 +699,11 @@ def _lookfor_generate_cache(module, import_modules, regenerate): # import sub-packages if import_modules and hasattr(item, '__path__'): - for pth in item.__path__: - for mod_path in os.listdir(pth): - init_py = os.path.join(pth, mod_path, '__init__.py') + for pth in item.__path__: + for mod_path in os.listdir(pth): + init_py = os.path.join(pth, mod_path, '__init__.py') if not os.path.isfile(init_py): - continue + continue if _all is not None and mod_path not in _all: continue try: |