Bottleneck-1.2.1/0000775000175000017500000000000013106356012012630 5ustar kgkg00000000000000Bottleneck-1.2.1/RELEASE.rst0000664000175000017500000002761113106351535014456 0ustar kgkg00000000000000 ============= Release Notes ============= These are the major changes made in each release. For details of the changes see the commit log at http://github.com/kwgoodman/bottleneck Bottleneck 1.2.1 ================ *Release date: 2017-05-15* This release adds support for NumPy's relaxed strides checking and fixes a few bugs. **Bug Fixes** - #156 Installing bottleneck when two versions of NumPy are present - #157 Compiling on Ubuntu 14.04 inside a Windows 7 WMware - #159 Occasional segmentation fault in nanargmin, nanargmax, median, and nanmedian when all of the following conditions are met: axis is None, input array is 2d or greater, and input array is not C contiguous. - #163 Reducing np.array([2**31], dtype=np.int64) overflows on Windows Older versions ============== Release notes from past releases. Bottleneck 1.2.0 ---------------- *Release date: 2016-10-20* This release is a complete rewrite of Bottleneck. **Port to C** - Bottleneck is now written in C - Cython is no longer a dependency - Source tarball size reduced by 80% - Build time reduced by 66% - Install size reduced by 45% **Redesign** - Besides porting to C, much of bottleneck has been redesigned to be simpler and faster. For example, bottleneck now uses its own N-dimensional array iterators, reducing function call overhead. **New features** - The new function bench_detailed runs a detailed performance benchmark on a single bottleneck function. - Bottleneck can be installed on systems that do not yet have NumPy installed. Previously that only worked on some systems. **Beware** - Functions partsort and argpartsort have been renamed to partition and argpartition to match NumPy. Additionally the meaning of the input arguments have changed: bn.partsort(a, n) is now equivalent to bn.partition(a, kth=n-1). Similarly for bn.argpartition. - The keyword for array input has been changed from `arr` to `a` in all functions. It now matches NumPy. **Thanks** - Moritz E. Beber: continuous integration with AppVeyor - Christoph Gohlke: Windows compatibility - Jennifer Olsen: comments and suggestions - A special thanks to the Cython developers. The quickest way to appreciate their work is to remove Cython from your project. It is not easy. Bottleneck 1.1.0 ---------------- *Release date: 2016-06-22* This release makes Bottleneck more robust, releases GIL, adds new functions. **More Robust** - move_median can now handle NaNs and `min_count` parameter - move_std is slower but numerically more stable - Bottleneck no longer crashes on byte-swapped input arrays **Faster** - All Bottleneck functions release the GIL - median is faster if the input array contains NaN - move_median is faster for input arrays that contain lots of NaNs - No speed penalty for median, nanmedian, nanargmin, nanargmax for Fortran ordered input arrays when axis is None - Function call overhead cut in half for reduction along all axes (axis=None) if the input array satisfies at least one of the following properties: 1d, C contiguous, F contiguous - Reduction along all axes (axis=None) is more than twice as fast for long, narrow input arrays such as a (1000000, 2) C contiguous array and a (2, 1000000) F contiguous array **New Functions** - move_var - move_argmin - move_argmax - move_rank - push **Beware** - median now returns NaN for a slice that contains one or more NaNs - Instead of using the distutils default, the '-O2' C compiler flag is forced - move_std output changed when mean is large compared to standard deviation - Fixed: Non-accelerated moving window functions used min_count incorrectly - move_median is a bit slower for float input arrays that do not contain NaN **Thanks** Alphabeticaly by last name - Alessandro Amici worked on setup.py - Pietro Battiston modernized bottleneck installation - Moritz E. Beber set up continuous integration with Travis CI - Jaime Frio improved the numerical stability of move_std - Christoph Gohlke revived Windows compatibility - Jennifer Olsen added NaN support to move_median Bottleneck 1.0.0 ---------------- *Release date: 2015-02-06* This release is a complete rewrite of Bottleneck. **Faster** - "python setup.py build" is 18.7 times faster - Function-call overhead cut in half---a big speed up for small input arrays - Arbitrary ndim input arrays accelerated; previously only 1d, 2d, and 3d - bn.nanrankdata is twice as fast for float input arrays - bn.move_max, bn.move_min are faster for int input arrays - No speed penalty for reducing along all axes when input is Fortran ordered **Smaller** - Compiled binaries 14.1 times smaller - Source tarball 4.7 times smaller - 9.8 times less C code - 4.3 times less Cython code - 3.7 times less Python code **Beware** - Requires numpy 1.9.1 - Single API, e.g.: bn.nansum instead of bn.nansum and nansum_2d_float64_axis0 - On 64-bit systems bn.nansum(int32) returns int32 instead of int64 - bn.nansum now returns 0 for all NaN slices (as does numpy 1.9.1) - Reducing over all axes returns, e.g., 6.0; previously np.float64(6.0) - bn.ss() now has default axis=None instead of axis=0 - bn.nn() is no longer in bottleneck **min_count** - Previous releases had moving window function pairs: move_sum, move_nansum - This release only has half of the pairs: move_sum - Instead a new input parameter, min_count, has been added - min_count=None same as old move_sum; min_count=1 same as old move_nansum - If # non-NaN values in window < min_count, then NaN assigned to the window - Exception: move_median does not take min_count as input **Bug Fixes** - Can now install bottleneck with pip even if numpy is not already installed - bn.move_max, bn.move_min now return float32 for float32 input Bottleneck 0.8.0 ---------------- *Release date: 2014-01-21* This version of Bottleneck requires NumPy 1.8. **Breaks from 0.7.0** - This version of Bottleneck requires NumPy 1.8 - nanargmin and nanargmax behave like the corresponding functions in NumPy 1.8 **Bug fixes** - nanargmax/nanargmin wrong for redundant max/min values in 1d int arrays Bottleneck 0.7.0 ---------------- *Release date: 2013-09-10* **Enhancements** - bn.rankdata() is twice as fast (with input a = np.random.rand(1000000)) - C files now included in github repo; cython not needed to try latest - C files are now generated with Cython 0.19.1 instead of 0.16 - Test bottleneck across multiple python/numpy versions using tox - Source tarball size cut in half **Bug fixes** - #50 move_std, move_nanstd return inappropriate NaNs (sqrt of negative #) - #52 `make test` fails on some computers - #57 scipy optional yet some unit tests depend on scipy - #49, #55 now works on Mac OS X 10.8 using clang compiler - #60 nanstd([1.0], ddof=1) and nanvar([1.0], ddof=1) crash Bottleneck 0.6.0 ---------------- *Release date: 2012-06-04* Thanks to Dougal Sutherland, Bottleneck now runs on Python 3.2. **New functions** - replace(arr, old, new), e.g, replace(arr, np.nan, 0) - nn(arr, arr0, axis) nearest neighbor and its index of 1d arr0 in 2d arr - anynan(arr, axis) faster alternative to np.isnan(arr).any(axis) - allnan(arr, axis) faster alternative to np.isnan(arr).all(axis) **Enhancements** - Python 3.2 support (may work on earlier versions of Python 3) - C files are now generated with Cython 0.16 instead of 0.14.1 - Upgrade numpydoc from 0.3.1 to 0.4 to support Sphinx 1.0.1 **Breaks from 0.5.0** - Support for Python 2.5 dropped - Default axis for benchmark suite is now axis=1 (was 0) **Bug fixes** - #31 Confusing error message in partsort and argpartsort - #32 Update path in MANIFEST.in - #35 Wrong output for very large (2**31) input arrays Bottleneck 0.5.0 ---------------- *Release date: 2011-06-13* The fifth release of bottleneck adds four new functions, comes in a single source distribution instead of separate 32 and 64 bit versions, and contains bug fixes. J. David Lee wrote the C-code implementation of the double heap moving window median. **New functions** - move_median(), moving window median - partsort(), partial sort - argpartsort() - ss(), sum of squares, faster version of scipy.stats.ss **Changes** - Single source distribution instead of separate 32 and 64 bit versions - nanmax and nanmin now follow Numpy 1.6 (not 1.5.1) when input is all NaN **Bug fixes** - #14 Support python 2.5 by importing `with` statement - #22 nanmedian wrong for particular ordering of NaN and non-NaN elements - #26 argpartsort, nanargmin, nanargmax returned wrong dtype on 64-bit Windows - #29 rankdata and nanrankdata crashed on 64-bit Windows Bottleneck 0.4.3 ---------------- *Release date: 2011-03-17* This is a bug fix release. **Bug fixes** - #11 median and nanmedian modified (partial sort) input array - #12 nanmedian wrong when odd number of elements with all but last a NaN **Enhancement** - Lazy import of SciPy (rarely used) speeds Bottleneck import 3x Bottleneck 0.4.2 ---------------- *Release date: 2011-03-08* This is a bug fix release. Same bug fixed in Bottleneck 0.4.1 for nanstd() was fixed for nanvar() in this release. Thanks again to Christoph Gohlke for finding the bug. Bottleneck 0.4.1 ---------------- *Release date: 2011-03-08* This is a bug fix release. The low-level functions nanstd_3d_int32_axis1 and nanstd_3d_int64_axis1, called by bottleneck.nanstd(), wrote beyond the memory owned by the output array if arr.shape[1] == 0 and arr.shape[0] > arr.shape[2], where arr is the input array. Thanks to Christoph Gohlke for finding an example to demonstrate the bug. Bottleneck 0.4.0 ---------------- *Release date: 2011-03-08* The fourth release of Bottleneck contains new functions and bug fixes. Separate source code distributions are now made for 32 bit and 64 bit operating systems. **New functions** - rankdata() - nanrankdata() **Enhancements** - Optionally specify the shapes of the arrays used in benchmark - Can specify which input arrays to fill with one-third NaNs in benchmark **Breaks from 0.3.0** - Removed group_nanmean() function - Bump dependency from NumPy 1.4.1 to NumPy 1.5.1 - C files are now generated with Cython 0.14.1 instead of 0.13 **Bug fixes** - #6 Some functions gave wrong output dtype for some input dtypes on 32 bit OS - #7 Some functions choked on size zero input arrays - #8 Segmentation fault with Cython 0.14.1 (but not 0.13) Bottleneck 0.3.0 ---------------- *Release date: 2010-01-19* The third release of Bottleneck is twice as fast for small input arrays and contains 10 new functions. **Faster** - All functions are faster (less overhead in selector functions) **New functions** - nansum() - move_sum() - move_nansum() - move_mean() - move_std() - move_nanstd() - move_min() - move_nanmin() - move_max() - move_nanmax() **Enhancements** - You can now specify the dtype and axis to use in the benchmark timings - Improved documentation and more unit tests **Breaks from 0.2.0** - Moving window functions now default to axis=-1 instead of axis=0 - Low-level moving window selector functions no longer take window as input **Bug fix** - int input array resulted in call to slow, non-cython version of move_nanmean Bottleneck 0.2.0 ---------------- *Release date: 2010-12-27* The second release of Bottleneck is faster, contains more functions, and supports more dtypes. **Faster** - All functions faster (less overhead) when output is not a scalar - Faster nanmean() for 2d, 3d arrays containing NaNs when axis is not None **New functions** - nanargmin() - nanargmax() - nanmedian() **Enhancements** - Added support for float32 - Fallback to slower, non-Cython functions for unaccelerated ndim/dtype - Scipy is no longer a dependency - Added support for older versions of NumPy (1.4.1) - All functions are now templated for dtype and axis - Added a sandbox for prototyping of new Bottleneck functions - Rewrote benchmarking code Bottleneck 0.1.0 ---------------- *Release date: 2010-12-10* Initial release. The three categories of Bottleneck functions: - Faster replacement for NumPy and SciPy functions - Moving window functions - Group functions that bin calculations by like-labeled elements Bottleneck-1.2.1/setup.py0000664000175000017500000001001713103166630014343 0ustar kgkg00000000000000#!/usr/bin/env python import os import sys try: import setuptools # noqa except ImportError: from ez_setup import use_setuptools use_setuptools() from setuptools import setup, find_packages from setuptools.extension import Extension from setuptools.command.build_ext import build_ext as _build_ext # workaround for installing bottleneck when numpy is not present class build_ext(_build_ext): # taken from: stackoverflow.com/questions/19919905/ # how-to-bootstrap-numpy-installation-in-setup-py#21621689 def finalize_options(self): _build_ext.finalize_options(self) # prevent numpy from thinking it is still in its setup process __builtins__.__NUMPY_SETUP__ = False import numpy # place numpy includes first, see gh #156 self.include_dirs.insert(0, numpy.get_include()) def prepare_modules(): from bottleneck.src.template import make_c_files make_c_files() ext = [Extension("bottleneck.reduce", sources=["bottleneck/src/reduce.c"], extra_compile_args=['-O2'])] ext += [Extension("bottleneck.move", sources=["bottleneck/src/move.c", "bottleneck/src/move_median/move_median.c"], extra_compile_args=['-O2'])] ext += [Extension("bottleneck.nonreduce", sources=["bottleneck/src/nonreduce.c"], extra_compile_args=['-O2'])] ext += [Extension("bottleneck.nonreduce_axis", sources=["bottleneck/src/nonreduce_axis.c"], extra_compile_args=['-O2'])] return ext def get_long_description(): with open('README.rst', 'r') as fid: long_description = fid.read() idx = max(0, long_description.find("Bottleneck is a collection")) long_description = long_description[idx:] return long_description def get_version_str(): ver_file = os.path.join('bottleneck', 'version.py') with open(ver_file, 'r') as fid: version = fid.read() version = version.split("= ") version = version[1].strip() version = version.strip("\"") return version CLASSIFIERS = ["Development Status :: 4 - Beta", "Environment :: Console", "Intended Audience :: Science/Research", "License :: OSI Approved :: BSD License", "Operating System :: OS Independent", "Programming Language :: C", "Programming Language :: Python", "Programming Language :: Python :: 3", "Topic :: Scientific/Engineering"] metadata = dict(name='Bottleneck', maintainer="Keith Goodman", maintainer_email="bottle-neck@googlegroups.com", description="Fast NumPy array functions written in C", long_description=get_long_description(), url="https://github.com/kwgoodman/bottleneck", download_url="http://pypi.python.org/pypi/Bottleneck", license="Simplified BSD", classifiers=CLASSIFIERS, platforms="OS Independent", version=get_version_str(), packages=find_packages(), package_data={'bottleneck': ['LICENSE']}, requires=['numpy'], install_requires=['numpy'], cmdclass={'build_ext': build_ext}, setup_requires=['numpy']) if not(len(sys.argv) >= 2 and ('--help' in sys.argv[1:] or sys.argv[1] in ('--help-commands', 'egg_info', '--version', 'clean', 'build_sphinx'))): # build bottleneck metadata['ext_modules'] = prepare_modules() elif sys.argv[1] == 'build_sphinx': # create intro.rst (from readme file) for sphinx manual readme = 'README.rst' intro = os.path.join('doc', 'source', 'intro.rst') with open(readme, 'r') as infile, open(intro, 'w') as outfile: txt = infile.readlines()[4:] # skip travis, appveyor build status outfile.write(''.join(txt)) setup(**metadata) Bottleneck-1.2.1/PKG-INFO0000664000175000017500000001465013106356012013733 0ustar kgkg00000000000000Metadata-Version: 1.1 Name: Bottleneck Version: 1.2.1 Summary: Fast NumPy array functions written in C Home-page: https://github.com/kwgoodman/bottleneck Author: Keith Goodman Author-email: bottle-neck@googlegroups.com License: Simplified BSD Download-URL: http://pypi.python.org/pypi/Bottleneck Description: Bottleneck is a collection of fast NumPy array functions written in C. Let's give it a try. Create a NumPy array:: >>> import numpy as np >>> a = np.array([1, 2, np.nan, 4, 5]) Find the nanmean:: >>> import bottleneck as bn >>> bn.nanmean(a) 3.0 Moving window mean:: >>> bn.move_mean(a, window=2, min_count=1) array([ 1. , 1.5, 2. , 4. , 4.5]) Benchmark ========= Bottleneck comes with a benchmark suite:: >>> bn.bench() Bottleneck performance benchmark Bottleneck 1.3.0.dev0; Numpy 1.12.1 Speed is NumPy time divided by Bottleneck time NaN means approx one-fifth NaNs; float64 used no NaN no NaN NaN no NaN NaN (100,) (1000,1000)(1000,1000)(1000,1000)(1000,1000) axis=0 axis=0 axis=0 axis=1 axis=1 nansum 67.3 0.3 0.7 2.5 2.4 nanmean 194.8 1.9 2.1 3.4 3.1 nanstd 241.5 1.6 2.1 2.7 2.6 nanvar 229.7 1.7 2.1 2.7 2.5 nanmin 34.1 0.7 1.1 0.8 2.6 nanmax 45.6 0.7 2.7 1.0 3.7 median 111.0 1.3 5.6 1.0 4.8 nanmedian 108.8 5.9 6.7 5.6 6.7 ss 16.3 1.1 1.2 1.6 1.6 nanargmin 89.2 2.9 5.1 2.2 5.6 nanargmax 107.4 3.0 5.4 2.2 5.8 anynan 19.4 0.3 35.0 0.5 29.9 allnan 39.9 146.6 128.3 115.8 75.6 rankdata 55.0 2.6 2.3 2.9 2.8 nanrankdata 59.8 2.8 2.2 3.2 2.5 partition 4.4 1.2 1.6 1.0 1.4 argpartition 3.5 1.1 1.4 1.1 1.6 replace 17.7 1.4 1.4 1.3 1.4 push 3440.0 7.8 9.5 20.0 15.5 move_sum 4743.0 75.7 156.1 195.4 211.1 move_mean 8760.9 116.2 167.4 252.1 258.8 move_std 8979.9 96.1 196.3 144.0 256.3 move_var 11216.8 127.3 243.9 225.9 321.4 move_min 2245.3 20.6 36.7 23.2 42.1 move_max 2223.7 20.5 37.2 24.1 42.4 move_argmin 3664.0 48.2 73.3 40.2 83.9 move_argmax 3916.9 42.0 75.4 41.5 81.2 move_median 2023.3 166.8 173.7 153.8 154.3 move_rank 1208.5 1.9 1.9 2.5 2.8 You can also run a detailed benchmark for a single function using, for example, the command:: >>> bn.bench_detailed("move_median", fraction_nan=0.3) Only arrays with data type (dtype) int32, int64, float32, and float64 are accelerated. All other dtypes result in calls to slower, unaccelerated functions. In the rare case of a byte-swapped input array (e.g. a big-endian array on a little-endian operating system) the function will not be accelerated regardless of dtype. Where ===== =================== ======================================================== download https://pypi.python.org/pypi/Bottleneck docs http://berkeleyanalytics.com/bottleneck code https://github.com/kwgoodman/bottleneck mailing list https://groups.google.com/group/bottle-neck =================== ======================================================== License ======= Bottleneck is distributed under a Simplified BSD license. See the LICENSE file for details. Install ======= Requirements: ======================== ==================================================== Bottleneck Python 2.7, 3.5, 3.6; NumPy 1.12.1 Compile gcc, clang, MinGW or MSVC Unit tests nose ======================== ==================================================== To install Bottleneck on GNU/Linux, Mac OS X, et al.:: $ sudo python setup.py install To install bottleneck on Windows, first install MinGW and add it to your system path. Then install Bottleneck with the commands:: python setup.py install --compiler=mingw32 Alternatively, you can use the Windows binaries created by Christoph Gohlke: http://www.lfd.uci.edu/~gohlke/pythonlibs/#bottleneck Unit tests ========== After you have installed Bottleneck, run the suite of unit tests:: >>> import bottleneck as bn >>> bn.test() Ran 169 tests in 57.205s OK Platform: OS Independent Classifier: Development Status :: 4 - Beta Classifier: Environment :: Console Classifier: Intended Audience :: Science/Research Classifier: License :: OSI Approved :: BSD License Classifier: Operating System :: OS Independent Classifier: Programming Language :: C Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 3 Classifier: Topic :: Scientific/Engineering Requires: numpy Bottleneck-1.2.1/LICENSE0000664000175000017500000000010711747525210013642 0ustar kgkg00000000000000The license file is one level down from this file: bottleneck/LICENSE. Bottleneck-1.2.1/README.rst0000664000175000017500000001213613103166630014324 0ustar kgkg00000000000000.. image:: https://travis-ci.org/kwgoodman/bottleneck.svg?branch=master :target: https://travis-ci.org/kwgoodman/bottleneck .. image:: https://ci.appveyor.com/api/projects/status/github/kwgoodman/bottleneck?svg=true&passingText=passing&failingText=failing&pendingText=pending :target: https://ci.appveyor.com/project/kwgoodman/bottleneck ========== Bottleneck ========== Bottleneck is a collection of fast NumPy array functions written in C. Let's give it a try. Create a NumPy array:: >>> import numpy as np >>> a = np.array([1, 2, np.nan, 4, 5]) Find the nanmean:: >>> import bottleneck as bn >>> bn.nanmean(a) 3.0 Moving window mean:: >>> bn.move_mean(a, window=2, min_count=1) array([ 1. , 1.5, 2. , 4. , 4.5]) Benchmark ========= Bottleneck comes with a benchmark suite:: >>> bn.bench() Bottleneck performance benchmark Bottleneck 1.3.0.dev0; Numpy 1.12.1 Speed is NumPy time divided by Bottleneck time NaN means approx one-fifth NaNs; float64 used no NaN no NaN NaN no NaN NaN (100,) (1000,1000)(1000,1000)(1000,1000)(1000,1000) axis=0 axis=0 axis=0 axis=1 axis=1 nansum 67.3 0.3 0.7 2.5 2.4 nanmean 194.8 1.9 2.1 3.4 3.1 nanstd 241.5 1.6 2.1 2.7 2.6 nanvar 229.7 1.7 2.1 2.7 2.5 nanmin 34.1 0.7 1.1 0.8 2.6 nanmax 45.6 0.7 2.7 1.0 3.7 median 111.0 1.3 5.6 1.0 4.8 nanmedian 108.8 5.9 6.7 5.6 6.7 ss 16.3 1.1 1.2 1.6 1.6 nanargmin 89.2 2.9 5.1 2.2 5.6 nanargmax 107.4 3.0 5.4 2.2 5.8 anynan 19.4 0.3 35.0 0.5 29.9 allnan 39.9 146.6 128.3 115.8 75.6 rankdata 55.0 2.6 2.3 2.9 2.8 nanrankdata 59.8 2.8 2.2 3.2 2.5 partition 4.4 1.2 1.6 1.0 1.4 argpartition 3.5 1.1 1.4 1.1 1.6 replace 17.7 1.4 1.4 1.3 1.4 push 3440.0 7.8 9.5 20.0 15.5 move_sum 4743.0 75.7 156.1 195.4 211.1 move_mean 8760.9 116.2 167.4 252.1 258.8 move_std 8979.9 96.1 196.3 144.0 256.3 move_var 11216.8 127.3 243.9 225.9 321.4 move_min 2245.3 20.6 36.7 23.2 42.1 move_max 2223.7 20.5 37.2 24.1 42.4 move_argmin 3664.0 48.2 73.3 40.2 83.9 move_argmax 3916.9 42.0 75.4 41.5 81.2 move_median 2023.3 166.8 173.7 153.8 154.3 move_rank 1208.5 1.9 1.9 2.5 2.8 You can also run a detailed benchmark for a single function using, for example, the command:: >>> bn.bench_detailed("move_median", fraction_nan=0.3) Only arrays with data type (dtype) int32, int64, float32, and float64 are accelerated. All other dtypes result in calls to slower, unaccelerated functions. In the rare case of a byte-swapped input array (e.g. a big-endian array on a little-endian operating system) the function will not be accelerated regardless of dtype. Where ===== =================== ======================================================== download https://pypi.python.org/pypi/Bottleneck docs http://berkeleyanalytics.com/bottleneck code https://github.com/kwgoodman/bottleneck mailing list https://groups.google.com/group/bottle-neck =================== ======================================================== License ======= Bottleneck is distributed under a Simplified BSD license. See the LICENSE file for details. Install ======= Requirements: ======================== ==================================================== Bottleneck Python 2.7, 3.5, 3.6; NumPy 1.12.1 Compile gcc, clang, MinGW or MSVC Unit tests nose ======================== ==================================================== To install Bottleneck on GNU/Linux, Mac OS X, et al.:: $ sudo python setup.py install To install bottleneck on Windows, first install MinGW and add it to your system path. Then install Bottleneck with the commands:: python setup.py install --compiler=mingw32 Alternatively, you can use the Windows binaries created by Christoph Gohlke: http://www.lfd.uci.edu/~gohlke/pythonlibs/#bottleneck Unit tests ========== After you have installed Bottleneck, run the suite of unit tests:: >>> import bottleneck as bn >>> bn.test() Ran 169 tests in 57.205s OK Bottleneck-1.2.1/setup.cfg0000664000175000017500000000004613106356012014451 0ustar kgkg00000000000000[egg_info] tag_build = tag_date = 0 Bottleneck-1.2.1/tox.ini0000664000175000017500000000102413103166630014142 0ustar kgkg00000000000000# Tox (http://tox.testrun.org/) configuration [tox] envlist = py27_np1121, py35_np1121 [testenv] changedir={envdir} commands={envpython} {toxinidir}/tools/test-installed-bottleneck.py {posargs:} [testenv:py27_np1121] basepython = python2.7 deps = nose numpy==1.12.1 [testenv:py35_np1121] basepython = python3.5 deps = nose numpy==1.12.1 # Not run by default. Use 'tox -e py27_npmaster' to call it [testenv:py27_npmaster] basepython = python2.7 deps = nose https://github.com/numpy/numpy/zipball/master Bottleneck-1.2.1/doc/0000775000175000017500000000000013106356012013375 5ustar kgkg00000000000000Bottleneck-1.2.1/doc/doc_howto0000664000175000017500000000045713103166630015315 0ustar kgkg00000000000000 Upload Sphinx docs to web server ================================ Godaddy doesn't allow rsyc. So to upload Sphinx docs to http://berkeleyanalytics.com/bottleneck: $ make doc Then to upload (it will ask for the password): $ scp -r build/sphinx/html/* username@berkeleyanalytics.com:html/bottleneck/ Bottleneck-1.2.1/doc/source/0000775000175000017500000000000013106356012014675 5ustar kgkg00000000000000Bottleneck-1.2.1/doc/source/reference.rst0000664000175000017500000000766013103166630017400 0ustar kgkg00000000000000================== Function reference ================== Bottleneck provides the following functions: ================================= ============================================================================================== reduce :meth:`nansum `, :meth:`nanmean `, :meth:`nanstd `, :meth:`nanvar `, :meth:`nanmin `, :meth:`nanmax `, :meth:`median `, :meth:`nanmedian `, :meth:`ss `, :meth:`nanargmin `, :meth:`nanargmax `, :meth:`anynan `, :meth:`allnan ` non-reduce :meth:`replace ` non-reduce with axis :meth:`rankdata `, :meth:`nanrankdata `, :meth:`partition `, :meth:`argpartition `, :meth:`push ` moving window :meth:`move_sum `, :meth:`move_mean `, :meth:`move_std `, :meth:`move_var `, :meth:`move_min `, :meth:`move_max `, :meth:`move_argmin `, :meth:`move_argmax `, :meth:`move_median `, :meth:`move_rank ` ================================= ============================================================================================== Reduce ------ Functions the reduce the input array along the specified axis. ------------ .. autofunction:: bottleneck.nansum ------------ .. autofunction:: bottleneck.nanmean ------------ .. autofunction:: bottleneck.nanstd ------------ .. autofunction:: bottleneck.nanvar ------------ .. autofunction:: bottleneck.nanmin ------------ .. autofunction:: bottleneck.nanmax ------------ .. autofunction:: bottleneck.median ------------ .. autofunction:: bottleneck.nanmedian ------------ .. autofunction:: bottleneck.ss ------------ .. autofunction:: bottleneck.nanargmin ------------ .. autofunction:: bottleneck.nanargmax ------------ .. autofunction:: bottleneck.anynan ------------ .. autofunction:: bottleneck.allnan Non-reduce ---------- Functions that do not reduce the input array and do not take `axis` as input. ------------ .. autofunction:: bottleneck.replace Non-reduce with axis -------------------- Functions that do not reduce the input array but operate along a specified axis. ------------ .. autofunction:: bottleneck.rankdata ------------ .. autofunction:: bottleneck.nanrankdata ------------ .. autofunction:: bottleneck.partition ------------ .. autofunction:: bottleneck.argpartition ------------ .. autofunction:: bottleneck.push Moving window functions ----------------------- Functions that operate along a (1d) moving window. ------------ .. autofunction:: bottleneck.move_sum ------------ .. autofunction:: bottleneck.move_mean ------------ .. autofunction:: bottleneck.move_std ------------ .. autofunction:: bottleneck.move_var ------------ .. autofunction:: bottleneck.move_min ------------ .. autofunction:: bottleneck.move_max ------------ .. autofunction:: bottleneck.move_argmin ------------ .. autofunction:: bottleneck.move_argmax ------------ .. autofunction:: bottleneck.move_median ------------ .. autofunction:: bottleneck.move_rank Bottleneck-1.2.1/doc/source/license.rst0000664000175000017500000000004612464476771017076 0ustar kgkg00000000000000.. include:: ../../bottleneck/LICENSE Bottleneck-1.2.1/doc/source/.gitignore0000664000175000017500000000001213103166630016660 0ustar kgkg00000000000000intro.rst Bottleneck-1.2.1/doc/source/intro.rst0000664000175000017500000001140413106353763016574 0ustar kgkg00000000000000========== Bottleneck ========== Bottleneck is a collection of fast NumPy array functions written in C. Let's give it a try. Create a NumPy array:: >>> import numpy as np >>> a = np.array([1, 2, np.nan, 4, 5]) Find the nanmean:: >>> import bottleneck as bn >>> bn.nanmean(a) 3.0 Moving window mean:: >>> bn.move_mean(a, window=2, min_count=1) array([ 1. , 1.5, 2. , 4. , 4.5]) Benchmark ========= Bottleneck comes with a benchmark suite:: >>> bn.bench() Bottleneck performance benchmark Bottleneck 1.3.0.dev0; Numpy 1.12.1 Speed is NumPy time divided by Bottleneck time NaN means approx one-fifth NaNs; float64 used no NaN no NaN NaN no NaN NaN (100,) (1000,1000)(1000,1000)(1000,1000)(1000,1000) axis=0 axis=0 axis=0 axis=1 axis=1 nansum 67.3 0.3 0.7 2.5 2.4 nanmean 194.8 1.9 2.1 3.4 3.1 nanstd 241.5 1.6 2.1 2.7 2.6 nanvar 229.7 1.7 2.1 2.7 2.5 nanmin 34.1 0.7 1.1 0.8 2.6 nanmax 45.6 0.7 2.7 1.0 3.7 median 111.0 1.3 5.6 1.0 4.8 nanmedian 108.8 5.9 6.7 5.6 6.7 ss 16.3 1.1 1.2 1.6 1.6 nanargmin 89.2 2.9 5.1 2.2 5.6 nanargmax 107.4 3.0 5.4 2.2 5.8 anynan 19.4 0.3 35.0 0.5 29.9 allnan 39.9 146.6 128.3 115.8 75.6 rankdata 55.0 2.6 2.3 2.9 2.8 nanrankdata 59.8 2.8 2.2 3.2 2.5 partition 4.4 1.2 1.6 1.0 1.4 argpartition 3.5 1.1 1.4 1.1 1.6 replace 17.7 1.4 1.4 1.3 1.4 push 3440.0 7.8 9.5 20.0 15.5 move_sum 4743.0 75.7 156.1 195.4 211.1 move_mean 8760.9 116.2 167.4 252.1 258.8 move_std 8979.9 96.1 196.3 144.0 256.3 move_var 11216.8 127.3 243.9 225.9 321.4 move_min 2245.3 20.6 36.7 23.2 42.1 move_max 2223.7 20.5 37.2 24.1 42.4 move_argmin 3664.0 48.2 73.3 40.2 83.9 move_argmax 3916.9 42.0 75.4 41.5 81.2 move_median 2023.3 166.8 173.7 153.8 154.3 move_rank 1208.5 1.9 1.9 2.5 2.8 You can also run a detailed benchmark for a single function using, for example, the command:: >>> bn.bench_detailed("move_median", fraction_nan=0.3) Only arrays with data type (dtype) int32, int64, float32, and float64 are accelerated. All other dtypes result in calls to slower, unaccelerated functions. In the rare case of a byte-swapped input array (e.g. a big-endian array on a little-endian operating system) the function will not be accelerated regardless of dtype. Where ===== =================== ======================================================== download https://pypi.python.org/pypi/Bottleneck docs http://berkeleyanalytics.com/bottleneck code https://github.com/kwgoodman/bottleneck mailing list https://groups.google.com/group/bottle-neck =================== ======================================================== License ======= Bottleneck is distributed under a Simplified BSD license. See the LICENSE file for details. Install ======= Requirements: ======================== ==================================================== Bottleneck Python 2.7, 3.5, 3.6; NumPy 1.12.1 Compile gcc, clang, MinGW or MSVC Unit tests nose ======================== ==================================================== To install Bottleneck on GNU/Linux, Mac OS X, et al.:: $ sudo python setup.py install To install bottleneck on Windows, first install MinGW and add it to your system path. Then install Bottleneck with the commands:: python setup.py install --compiler=mingw32 Alternatively, you can use the Windows binaries created by Christoph Gohlke: http://www.lfd.uci.edu/~gohlke/pythonlibs/#bottleneck Unit tests ========== After you have installed Bottleneck, run the suite of unit tests:: >>> import bottleneck as bn >>> bn.test() Ran 169 tests in 57.205s OK Bottleneck-1.2.1/doc/source/release.rst0000664000175000017500000000003712464476771017074 0ustar kgkg00000000000000.. include:: ../../RELEASE.rst Bottleneck-1.2.1/doc/source/conf.py0000664000175000017500000001560713103166630016207 0ustar kgkg00000000000000# -*- coding: utf-8 -*- # # la documentation build configuration file, created by # sphinx-quickstart on Thu Jan 14 16:31:34 2010. # # This file is execfile()d with the current directory set to its containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. import sys, os # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. #sys.path.append(os.path.abspath('.')) sys.path.insert(0, os.path.abspath('../sphinxext')) # -- General configuration ----------------------------------------------------- # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = ['sphinx.ext.autodoc','numpydoc'] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. #source_encoding = 'utf-8' # The master toctree document. master_doc = 'index' # General information about the project. project = u'Bottleneck' copyright = u'2010-2017 Keith Goodman' # Grab version from bottleneck/version.py ver_file = os.path.join('..', '..', 'bottleneck', 'version.py') fid = file(ver_file, 'r') VER = fid.read() fid.close() VER = VER.split("= ") VER = VER[1].strip() VER = VER.strip('"') # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. version = VER # The full version, including alpha/beta/rc tags. release = VER # JP: added from sphinxdocs #autosummary_generate = True # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. #language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. #today_fmt = '%B %d, %Y' # List of documents that shouldn't be included in the build. #unused_docs = [] # List of directories, relative to source directory, that shouldn't be searched # for source files. exclude_trees = [] # The reST default role (used for this markup: `text`) to use for all documents. #default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. #modindex_common_prefix = [] # -- Options for HTML output --------------------------------------------------- # The theme to use for HTML and HTML Help pages. Major themes that come with # Sphinx are currently 'default' and 'sphinxdoc'. html_theme = 'default' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. html_theme_options = { "headtextcolor": "#333333", "sidebarbgcolor": "#dddddd", "footerbgcolor": "#cccccc", "footertextcolor": "black", "headbgcolor": "#cccccc", "sidebartextcolor": "#333333", "sidebarlinkcolor": "default", "relbarbgcolor": "#cccccc", "relbartextcolor": "default", "relbarlinkcolor": "default", "codebgcolor": "#ffffff", "textcolor": "#333333", "bgcolor": "#f5f5f5" } # Add any paths that contain custom themes here, relative to this directory. #html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". #html_title = None # A shorter title for the navigation bar. Default is the same as html_title. #html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. html_logo = '../image/icon.png' # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. #html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". #html_static_path = ['_static'] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. #html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. #html_use_modindex = True # If false, no index is generated. #html_use_index = True # If true, the index is split into individual pages for each letter. #html_split_index = False # If true, links to the reST sources are added to the pages. #html_show_sourcelink = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. #html_use_opensearch = '' # If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = '' # Output file base name for HTML help builder. htmlhelp_basename = 'bottleneckdoc' # -- Options for LaTeX output -------------------------------------------------- # The paper size ('letter' or 'a4'). #latex_paper_size = 'letter' # The font size ('10pt', '11pt' or '12pt'). #latex_font_size = '10pt' # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ ('index', 'bottleneck.tex', u'bottleneck Documentation', u'Keith Goodman', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. #latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. #latex_use_parts = False # Additional stuff for the LaTeX preamble. #latex_preamble = '' # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_use_modindex = True Bottleneck-1.2.1/doc/source/index.rst0000664000175000017500000000036513103166630016544 0ustar kgkg00000000000000========== Bottleneck ========== Fast NumPy array functions written in C. .. toctree:: :maxdepth: 2 intro reference release license Indices and tables ================== * :ref:`genindex` * :ref:`modindex` * :ref:`search` Bottleneck-1.2.1/doc/image/0000775000175000017500000000000013106356012014457 5ustar kgkg00000000000000Bottleneck-1.2.1/doc/image/icon.png0000664000175000017500000000113212464476771016137 0ustar kgkg00000000000000‰PNG  IHDR@@ЊiqоsRGBЎЮщbKGDџџџ НЇ“ pHYs  šœtIMEкочŒtEXtCommentCreated with GIMPWЕIDATxкэšлnФ DзˆџџхщSЖQšРcbРВhC`XєкpЅ76ТR#цбrд„СѕwŒj#žчw ш%vїHžщє-д@Q@oL?ѕ %ЃB’ymm+Q%JT[[М˜ц6Рп(і0%ZJ=@]&=-І€QхH‚`ŠЏГж6jšRцсЭQцlгъ({”s‹e­Š$О‰*Аїr7k­*чс }"dYЫFу№§Л‚ZНxuМ(ш ŸЛЈ”тˆёк} џ9ИъщGЌ0Ы7І Аƒ+и7@ЇšMZ8'7Ыh…ŸфiUфуЃКЄ7œwkg'ЌsOџZД&<Ќ”Ž}|/ПXXdkЧjўоыAВ8b`вD йоУгb(—Ё@`зѕйl  _Э”IENDЎB`‚Bottleneck-1.2.1/doc/image/icon.xcf0000664000175000017500000000675412464476771016152 0ustar kgkg00000000000000gimp xcf file@@BBG gimp-commentCreated with GIMPgimp-image-grid(style solid) (fgcolor (color-rgba 0.000000 0.000000 0.000000 1.000000)) (bgcolor (color-rgba 1.000000 1.000000 1.000000 1.000000)) (xspacing 10.000000) (yspacing 10.000000) (spacing-unit inches) (xoffset 0.000000) (yoffset 0.000000) (offset-unit inches) Ў@@ Backgroundџ     ]@@q@@Wџўџ<џ џ(џќџџ џўџџўџ%џ§џџ џўџџўџ%џћџџџ џўџџўџ%џўџџўџ џўџџўџ$џўџџўџ џўџџўџ%џўџџўџ џќџџ%џўџџўџ џ#џ џўџџ!џџўџ џўџџ џўџџўџ џўџџўџџўџџўџ џўџџџџ џўџџ џўџџўџџќџџ7џyџ!џ џџџўџџ џўџ џўџџўџџ џўџ џўџџўџџ џўџ џўџџўџџ џўџ џўџџўџџџўџ џўџџўџџџўџ џџўџџџ џўџџўџџџўџ џўџџўџџџўџ џўџџўџџџўџ џўџџўџџџўџџўџџўџџџўџџўџџџўџџџџєџ ќџџџџџўџ џўџџџџўџ џўџџ џџџўџ џўџџџўџџўџ џўџџџўџџўџ џўџџџџўџ џўџџџўџџўџ џџџўџџўџ џўџџџўџџўџ џўџџџџўџ џўџџџџўџ џўџџџџ џўџџџўџџўџ џўџџ џ џўџ џўџџџўџџџџ џ SџWџўџ<џ џ(џќџџ џўџџўџ%џ§џџ џўџџўџ%џћџџџ џўџџўџ%џўџџўџ џўџџўџ$џўџџўџ џўџџўџ%џўџџўџ џќџџ%џўџџўџ џ#џ џўџџ!џџўџ џўџџ џўџџўџ џўџџўџџўџџўџ џўџџџџ џўџџ џўџџўџџќџџ7џyџ!џ џ џўџџ џ џўџџ џ џўџџ џ џўџџ џ џўџџџ џўџџџ џўџџџ џўџџџ џўџџџ џўџџџ џўџџџ џўџџџ џџ џџєџ ќџџџџџџџџџ џџџџџўџџџџўџџџџџџџўџџџџўџџџџўџџџџџџџџџџџџџўџџџ џ џџџџџ џ SџWџўџ<џ џ(џќџџ џўџџўџ%џ§џџ џўџџўџ%џћџџџ џўџџўџ%џўџџўџ џўџџўџ$џўџџўџ џўџџўџ%џўџџўџ џќџџ%џўџџўџ џ#џ џўџџ!џџўџ џўџџ џўџџўџ џўџџўџџўџџўџ џўџџџџ џўџџ џўџџўџџќџџ7џyџ!џ џ џўџџўџ џўџџ џўџџўџ џўџџ џўџџўџ џўџџ џўџџўџ џўџџ џўџџўџ џўџџ џўџџўџ џўџџ џўџџўџ џўџџ џўџџўџ џўџџ џўџџўџ џўџџ џўџџўџ џўџџ џўџџўџ џџ џўџџ џўџџ џўџ џўџџ џџєџ ќџџџџўџ џћџџџџџўџ џўџ џџџўџ џўџ џџџџўџ џўџџўџџџўџ џўџџўџџџўџ џўџџџџўџ џўџџўџџџ џўџџўџџџўџ џўџџўџџџўџ џўџџџџўџ џўџџџџўџ џўџџџџўџџўџ џўџџџўџџўџ џ џџўџџўџџџўџџџ џ SџWўџ<џ џ(ќџџ ўџўџ%џ§џ ўџўџ%ћџџ ўџўџ%ўџўџ ўџўџ$ўџўџ ўџўџ%ўџўџ ќџџџ%ўџўџ џ#џ ўџџ!џўџ ўџџ ўџўџ ўџўџўџўџ ўџџџџ ўџџ ўџўџќџџџ7џyџ! џ џўџ џ џўџ џ џўџ џ џўџ џ џўџџ џўџџ џўџџ џўџџ џўџџ џўџџ џўџџ џўџџ џџ џџџє џќџџџџџџџџџ џџџџўџџџўџџџџџџўџџџўџџџўџџџџџџџџџџџџўџџџ џџџџџ џ џSBottleneck-1.2.1/doc/image/icon14.png0000664000175000017500000000103012464476771016301 0ustar kgkg00000000000000‰PNG  IHDRH-бsRGBЎЮщbKGDџџџ НЇ“ pHYs  šœtIMEк .Ÿ`tEXtCommentCreated with GIMPWsIDAT(ЯаПkSaЦёЯЭЦ‰) "С ет$( ‚tqtьь.ъъ_ "t‡€›ш_рRдRЈХA ЖŠXhH5 MН…ŠЖ*I_‡мШEЋƒgyЯ9яљђœч№gT1ŽLR—p{гC™РЃ ’: ‘УШЂ‡§I/‹C8‚ЪпР=XЦуx€cu"ФиNG8мO'ЯђтэчмНpƒЦO™КеЗёK1J<ЁВкjЇЛѕ?У(хЮUЉџОjд>щѕѓѕ|d (ЇVЭ&W{9ЅRЦ|;cv&2§ XzљE5з"ОЇаИЉ<Эцв.фˆ з…ѓcœŽиžcyRИMЃЩё˜•@ы2зцЊq м„@xH‹KУМNZ+'iя№ Џ6й:ѕё8ЃvŠjЈWX}C/ЧZ‡ц<Гфѓ,Ц|]уm‡Oэ4ј"™UЎf)>cЋЩы]Oащ2ѓАсџу"ј z7nщН.;ЉIENDЎB`‚Bottleneck-1.2.1/Makefile0000664000175000017500000000252613103166630014277 0ustar kgkg00000000000000# Bottleneck Makefile PYTHON=python srcdir := bottleneck/ help: @echo "Available tasks:" @echo "help --> This help page" @echo "all --> clean, build, flake8, test" @echo "build --> Build the Python C extensions" @echo "clean --> Remove all the build files for a fresh start" @echo "test --> Run unit tests" @echo "flake8 --> Check for pep8 errors" @echo "readme --> Update benchmark results in README.rst" @echo "bench --> Run performance benchmark" @echo "detail --> Detailed benchmarks for all functions" @echo "sdist --> Make source distribution" @echo "doc --> Build Sphinx manual" all: clean build test flake8 build: ${PYTHON} setup.py build_ext --inplace test: ${PYTHON} -c "import bottleneck;bottleneck.test()" flake8: flake8 --exclude=doc,.tox . readme: PYTHONPATH=`pwd`:PYTHONPATH ${PYTHON} tools/update_readme.py bench: ${PYTHON} -c "import bottleneck; bottleneck.bench()" detail: ${PYTHON} -c "import bottleneck; bottleneck.bench_detailed('all')" sdist: rm -f MANIFEST ${PYTHON} setup.py sdist git status # doc directory exists so use phony .PHONY: doc doc: clean build rm -rf build/sphinx ${PYTHON} setup.py build_sphinx clean: rm -rf build dist Bottleneck.egg-info find . -name \*.pyc -delete rm -rf ${srcdir}/*.html ${srcdir}/build rm -rf ${srcdir}/*.c rm -rf ${srcdir}/*.so Bottleneck-1.2.1/MANIFEST.in0000664000175000017500000000050513103166630014370 0ustar kgkg00000000000000include README.rst RELEASE.rst include LICENSE bottleneck/LICENSE include Makefile tox.ini recursive-include bottleneck/src *.c *.h exclude bottleneck/src/reduce.c exclude bottleneck/src/move.c exclude bottleneck/src/nonreduce.c exclude bottleneck/src/nonreduce_axis.c recursive-include doc * recursive-exclude doc/build * Bottleneck-1.2.1/bottleneck/0000775000175000017500000000000013106356012014762 5ustar kgkg00000000000000Bottleneck-1.2.1/bottleneck/src/0000775000175000017500000000000013106356012015551 5ustar kgkg00000000000000Bottleneck-1.2.1/bottleneck/src/move_median/0000775000175000017500000000000013106356012020034 5ustar kgkg00000000000000Bottleneck-1.2.1/bottleneck/src/move_median/move_median.c0000664000175000017500000005337513103166630022502 0ustar kgkg00000000000000/* Copyright (c) 2011 J. David Lee. All rights reserved. Released under a Simplified BSD license Adapted, expanded, and added NaN handling for Bottleneck: Copyright 2016 Keith Goodman Released under the Bottleneck license */ #include "move_median.h" #define min(a, b) (((a) < (b)) ? (a) : (b)) #define SWAP_NODES(heap, idx1, node1, idx2, node2) \ heap[idx1] = node2; \ heap[idx2] = node1; \ node1->idx = idx2; \ node2->idx = idx1; \ idx1 = idx2 /* ----------------------------------------------------------------------------- Prototypes ----------------------------------------------------------------------------- */ /* helper functions */ static MM_INLINE ai_t mm_get_median(mm_handle *mm); static MM_INLINE void heapify_small_node(mm_handle *mm, idx_t idx); static MM_INLINE void heapify_large_node(mm_handle *mm, idx_t idx); static MM_INLINE idx_t mm_get_smallest_child(mm_node **heap, idx_t window, idx_t idx, mm_node **child); static MM_INLINE idx_t mm_get_largest_child(mm_node **heap, idx_t window, idx_t idx, mm_node **child); static MM_INLINE void mm_move_up_small(mm_node **heap, idx_t idx, mm_node *node, idx_t p_idx, mm_node *parent); static MM_INLINE void mm_move_down_small(mm_node **heap, idx_t window, idx_t idx, mm_node *node); static MM_INLINE void mm_move_down_large(mm_node **heap, idx_t idx, mm_node *node, idx_t p_idx, mm_node *parent); static MM_INLINE void mm_move_up_large(mm_node **heap, idx_t window, idx_t idx, mm_node *node); static MM_INLINE void mm_swap_heap_heads(mm_node **s_heap, idx_t n_s, mm_node **l_heap, idx_t n_l, mm_node *s_node, mm_node *l_node); /* ----------------------------------------------------------------------------- Top-level non-nan functions ----------------------------------------------------------------------------- */ /* At the start of bn.move_median two heaps are created. One heap contains the * small values (a max heap); the other heap contains the large values (a min * heap). The handle, containing information about the heaps, is returned. */ mm_handle * mm_new(const idx_t window, idx_t min_count) { mm_handle *mm = malloc(sizeof(mm_handle)); mm->nodes = malloc(window * sizeof(mm_node*)); mm->node_data = malloc(window * sizeof(mm_node)); mm->s_heap = mm->nodes; mm->l_heap = &mm->nodes[window / 2 + window % 2]; mm->window = window; mm->odd = window % 2; mm->min_count = min_count; mm_reset(mm); return mm; } /* Insert a new value, ai, into one of the heaps. Use this function when * the heaps contain less than window-1 nodes. Returns the median value. * Once there are window-1 nodes in the heap, switch to using mm_update. */ ai_t mm_update_init(mm_handle *mm, ai_t ai) { mm_node *node; idx_t n_s = mm->n_s; idx_t n_l = mm->n_l; node = &mm->node_data[n_s + n_l]; node->ai = ai; if (n_s == 0) { /* the first node to appear in a heap */ mm->s_heap[0] = node; node->region = SH; node->idx = 0; mm->oldest = node; /* only need to set the oldest node once */ mm->n_s = 1; mm->s_first_leaf = 0; } else { /* at least one node already exists in the heaps */ mm->newest->next = node; if (n_s > n_l) { /* add new node to large heap */ mm->l_heap[n_l] = node; node->region = LH; node->idx = n_l; ++mm->n_l; mm->l_first_leaf = FIRST_LEAF(mm->n_l); heapify_large_node(mm, n_l); } else { /* add new node to small heap */ mm->s_heap[n_s] = node; node->region = SH; node->idx = n_s; ++mm->n_s; mm->s_first_leaf = FIRST_LEAF(mm->n_s); heapify_small_node(mm, n_s); } } mm->newest = node; return mm_get_median(mm); } /* Insert a new value, ai, into the double heap structure. Use this function * when the double heap contains at least window-1 nodes. Returns the median * value. If there are less than window-1 nodes in the heap, use * mm_update_init. */ ai_t mm_update(mm_handle *mm, ai_t ai) { /* node is oldest node with ai of newest node */ mm_node *node = mm->oldest; node->ai = ai; /* update oldest, newest */ mm->oldest = mm->oldest->next; mm->newest->next = node; mm->newest = node; /* adjust position of new node in heap if needed */ if (node->region == SH) { heapify_small_node(mm, node->idx); } else { heapify_large_node(mm, node->idx); } /* return the median */ if (mm->odd) return mm->s_heap[0]->ai; else return (mm->s_heap[0]->ai + mm->l_heap[0]->ai) / 2.0; } /* ----------------------------------------------------------------------------- Top-level nan functions ----------------------------------------------------------------------------- */ /* At the start of bn.move_median two heaps and a nan array are created. One * heap contains the small values (a max heap); the other heap contains the * large values (a min heap); the nan array contains the NaNs. The handle, * containing information about the heaps and the nan array is returned. */ mm_handle * mm_new_nan(const idx_t window, idx_t min_count) { mm_handle *mm = malloc(sizeof(mm_handle)); mm->nodes = malloc(2 * window * sizeof(mm_node*)); mm->node_data = malloc(window * sizeof(mm_node)); mm->s_heap = mm->nodes; mm->l_heap = &mm->nodes[window / 2 + window % 2]; mm->n_array = &mm->nodes[window]; mm->window = window; mm->min_count = min_count; mm_reset(mm); return mm; } /* Insert a new value, ai, into one of the heaps or the nan array. Use this * function when there are less than window-1 nodes. Returns the median * value. Once there are window-1 nodes in the heap, switch to using * mm_update_nan. */ ai_t mm_update_init_nan(mm_handle *mm, ai_t ai) { mm_node *node; idx_t n_s = mm->n_s; idx_t n_l = mm->n_l; idx_t n_n = mm->n_n; node = &mm->node_data[n_s + n_l + n_n]; node->ai = ai; if (ai != ai) { mm->n_array[n_n] = node; node->region = NA; node->idx = n_n; if (n_s + n_l + n_n == 0) { /* only need to set the oldest node once */ mm->oldest = node; } else { mm->newest->next = node; } ++mm->n_n; } else { if (n_s == 0) { /* the first node to appear in a heap */ mm->s_heap[0] = node; node->region = SH; node->idx = 0; if (n_s + n_l + n_n == 0) { /* only need to set the oldest node once */ mm->oldest = node; } else { mm->newest->next = node; } mm->n_s = 1; mm->s_first_leaf = 0; } else { /* at least one node already exists in the heaps */ mm->newest->next = node; if (n_s > n_l) { /* add new node to large heap */ mm->l_heap[n_l] = node; node->region = LH; node->idx = n_l; ++mm->n_l; mm->l_first_leaf = FIRST_LEAF(mm->n_l); heapify_large_node(mm, n_l); } else { /* add new node to small heap */ mm->s_heap[n_s] = node; node->region = SH; node->idx = n_s; ++mm->n_s; mm->s_first_leaf = FIRST_LEAF(mm->n_s); heapify_small_node(mm, n_s); } } } mm->newest = node; return mm_get_median(mm); } /* Insert a new value, ai, into one of the heaps or the nan array. Use this * function when there are at least window-1 nodes. Returns the median value. * If there are less than window-1 nodes, use mm_update_init_nan. */ ai_t mm_update_nan(mm_handle *mm, ai_t ai) { idx_t n_s, n_l, n_n; mm_node **l_heap; mm_node **s_heap; mm_node **n_array; mm_node *node2; /* node is oldest node with ai of newest node */ mm_node *node = mm->oldest; idx_t idx = node->idx; node->ai = ai; /* update oldest, newest */ mm->oldest = mm->oldest->next; mm->newest->next = node; mm->newest = node; l_heap = mm->l_heap; s_heap = mm->s_heap; n_array = mm->n_array; n_s = mm->n_s; n_l = mm->n_l; n_n = mm->n_n; if (ai != ai) { if (node->region == SH) { /* Oldest node is in the small heap and needs to be moved * to the nan array. Resulting hole in the small heap will be * filled with the rightmost leaf of the last row of the small * heap. */ /* insert node into nan array */ node->region = NA; node->idx = n_n; n_array[n_n] = node; ++mm->n_n; /* plug small heap hole */ --mm->n_s; if (mm->n_s == 0) { mm->s_first_leaf = 0; if (n_l > 0) { /* move head node from the large heap to the small heap */ node2 = mm->l_heap[0]; node2->region = SH; s_heap[0] = node2; mm->n_s = 1; mm->s_first_leaf = 0; /* plug hole in large heap */ node2= mm->l_heap[mm->n_l - 1]; node2->idx = 0; l_heap[0] = node2; --mm->n_l; if (mm->n_l == 0) mm->l_first_leaf = 0; else mm->l_first_leaf = FIRST_LEAF(mm->n_l); heapify_large_node(mm, 0); } } else { if (idx != n_s - 1) { s_heap[idx] = s_heap[n_s - 1]; s_heap[idx]->idx = idx; heapify_small_node(mm, idx); } if (mm->n_s < mm->n_l) { /* move head node from the large heap to the small heap */ node2 = mm->l_heap[0]; node2->idx = mm->n_s; node2->region = SH; s_heap[mm->n_s] = node2; ++mm->n_s; mm->l_first_leaf = FIRST_LEAF(mm->n_s); heapify_small_node(mm, node2->idx); /* plug hole in large heap */ node2= mm->l_heap[mm->n_l - 1]; node2->idx = 0; l_heap[0] = node2; --mm->n_l; if (mm->n_l == 0) mm->l_first_leaf = 0; else mm->l_first_leaf = FIRST_LEAF(mm->n_l); heapify_large_node(mm, 0); } else { mm->s_first_leaf = FIRST_LEAF(mm->n_s); heapify_small_node(mm, idx); } } } else if (node->region == LH) { /* Oldest node is in the large heap and needs to be moved * to the nan array. Resulting hole in the large heap will be * filled with the rightmost leaf of the last row of the large * heap. */ /* insert node into nan array */ node->region = NA; node->idx = n_n; n_array[n_n] = node; ++mm->n_n; /* plug large heap hole */ if (idx != n_l - 1) { l_heap[idx] = l_heap[n_l - 1]; l_heap[idx]->idx = idx; heapify_large_node(mm, idx); } --mm->n_l; if (mm->n_l == 0) mm->l_first_leaf = 0; else mm->l_first_leaf = FIRST_LEAF(mm->n_l); if (mm->n_l < mm->n_s - 1) { /* move head node from the small heap to the large heap */ node2 = mm->s_heap[0]; node2->idx = mm->n_l; node2->region = LH; l_heap[mm->n_l] = node2; ++mm->n_l; mm->l_first_leaf = FIRST_LEAF(mm->n_l); heapify_large_node(mm, node2->idx); /* plug hole in small heap */ if (n_s != 1) { node2 = mm->s_heap[mm->n_s - 1]; node2->idx = 0; s_heap[0] = node2; } --mm->n_s; if (mm->n_s == 0) mm->s_first_leaf = 0; else mm->s_first_leaf = FIRST_LEAF(mm->n_s); heapify_small_node(mm, 0); } /* reorder large heap if needed */ heapify_large_node(mm, idx); } else if (node->region == NA) { /* insert node into nan heap */ n_array[idx] = node; } } else { if (node->region == SH) heapify_small_node(mm, idx); else if (node->region == LH) heapify_large_node(mm, idx); else { /* ai is not NaN but oldest node is in nan array */ if (n_s > n_l) { /* insert into large heap */ node->region = LH; node->idx = n_l; l_heap[n_l] = node; ++mm->n_l; mm->l_first_leaf = FIRST_LEAF(mm->n_l); heapify_large_node(mm, n_l); } else { /* insert into small heap */ node->region = SH; node->idx = n_s; s_heap[n_s] = node; ++mm->n_s; mm->s_first_leaf = FIRST_LEAF(mm->n_s); heapify_small_node(mm, n_s); } /* plug nan array hole */ if (idx != n_n - 1) { n_array[idx] = n_array[n_n - 1]; n_array[idx]->idx = idx; } --mm->n_n; } } return mm_get_median(mm); } /* ----------------------------------------------------------------------------- Top-level functions common to nan and non-nan cases ----------------------------------------------------------------------------- */ /* At the end of each slice the double heap and nan array are reset (mm_reset) * to prepare for the next slice. In the 2d input array case (with axis=1), * each slice is a row of the input array. */ void mm_reset(mm_handle *mm) { mm->n_l = 0; mm->n_s = 0; mm->n_n = 0; mm->s_first_leaf = 0; mm->l_first_leaf = 0; } /* After bn.move_median is done, free the memory */ void mm_free(mm_handle *mm) { free(mm->node_data); free(mm->nodes); free(mm); } /* ----------------------------------------------------------------------------- Utility functions ----------------------------------------------------------------------------- */ /* Return the current median */ static MM_INLINE ai_t mm_get_median(mm_handle *mm) { idx_t n_total = mm->n_l + mm->n_s; if (n_total < mm->min_count) return MM_NAN(); if (min(mm->window, n_total) % 2 == 1) return mm->s_heap[0]->ai; return (mm->s_heap[0]->ai + mm->l_heap[0]->ai) / 2.0; } static MM_INLINE void heapify_small_node(mm_handle *mm, idx_t idx) { idx_t idx2; mm_node *node; mm_node *node2; mm_node **s_heap; mm_node **l_heap; idx_t n_s, n_l; ai_t ai; s_heap = mm->s_heap; l_heap = mm->l_heap; node = s_heap[idx]; n_s = mm->n_s; n_l = mm->n_l; ai = node->ai; /* Internal or leaf node */ if (idx > 0) { idx2 = P_IDX(idx); node2 = s_heap[idx2]; /* Move up */ if (ai > node2->ai) { mm_move_up_small(s_heap, idx, node, idx2, node2); /* Maybe swap between heaps */ node2 = l_heap[0]; if (ai > node2->ai) { mm_swap_heap_heads(s_heap, n_s, l_heap, n_l, node, node2); } } /* Move down */ else if (idx < mm->s_first_leaf) { mm_move_down_small(s_heap, n_s, idx, node); } } /* Head node */ else { node2 = l_heap[0]; if (n_l > 0 && ai > node2->ai) { mm_swap_heap_heads(s_heap, n_s, l_heap, n_l, node, node2); } else { mm_move_down_small(s_heap, n_s, idx, node); } } } static MM_INLINE void heapify_large_node(mm_handle *mm, idx_t idx) { idx_t idx2; mm_node *node; mm_node *node2; mm_node **s_heap; mm_node **l_heap; idx_t n_s, n_l; ai_t ai; s_heap = mm->s_heap; l_heap = mm->l_heap; node = l_heap[idx]; n_s = mm->n_s; n_l = mm->n_l; ai = node->ai; /* Internal or leaf node */ if (idx > 0) { idx2 = P_IDX(idx); node2 = l_heap[idx2]; /* Move down */ if (ai < node2->ai) { mm_move_down_large(l_heap, idx, node, idx2, node2); /* Maybe swap between heaps */ node2 = s_heap[0]; if (ai < node2->ai) { mm_swap_heap_heads(s_heap, n_s, l_heap, n_l, node2, node); } } /* Move up */ else if (idx < mm->l_first_leaf) { mm_move_up_large(l_heap, n_l, idx, node); } } /* Head node */ else { node2 = s_heap[0]; if (n_s > 0 && ai < node2->ai) { mm_swap_heap_heads(s_heap, n_s, l_heap, n_l, node2, node); } else { mm_move_up_large(l_heap, n_l, idx, node); } } } /* Return the index of the smallest child of the node. The pointer * child will also be set. */ static MM_INLINE idx_t mm_get_smallest_child(mm_node **heap, idx_t window, idx_t idx, mm_node **child) { idx_t i0 = FC_IDX(idx); idx_t i1 = i0 + NUM_CHILDREN; i1 = min(i1, window); switch(i1 - i0) { case 8: if (heap[i0 + 7]->ai < heap[idx]->ai) { idx = i0 + 7; } case 7: if (heap[i0 + 6]->ai < heap[idx]->ai) { idx = i0 + 6; } case 6: if (heap[i0 + 5]->ai < heap[idx]->ai) { idx = i0 + 5; } case 5: if (heap[i0 + 4]->ai < heap[idx]->ai) { idx = i0 + 4; } case 4: if (heap[i0 + 3]->ai < heap[idx]->ai) { idx = i0 + 3; } case 3: if (heap[i0 + 2]->ai < heap[idx]->ai) { idx = i0 + 2; } case 2: if (heap[i0 + 1]->ai < heap[idx]->ai) { idx = i0 + 1; } case 1: if (heap[i0 ]->ai < heap[idx]->ai) { idx = i0; } } *child = heap[idx]; return idx; } /* Return the index of the largest child of the node. The pointer * child will also be set. */ static MM_INLINE idx_t mm_get_largest_child(mm_node **heap, idx_t window, idx_t idx, mm_node **child) { idx_t i0 = FC_IDX(idx); idx_t i1 = i0 + NUM_CHILDREN; i1 = min(i1, window); switch(i1 - i0) { case 8: if (heap[i0 + 7]->ai > heap[idx]->ai) { idx = i0 + 7; } case 7: if (heap[i0 + 6]->ai > heap[idx]->ai) { idx = i0 + 6; } case 6: if (heap[i0 + 5]->ai > heap[idx]->ai) { idx = i0 + 5; } case 5: if (heap[i0 + 4]->ai > heap[idx]->ai) { idx = i0 + 4; } case 4: if (heap[i0 + 3]->ai > heap[idx]->ai) { idx = i0 + 3; } case 3: if (heap[i0 + 2]->ai > heap[idx]->ai) { idx = i0 + 2; } case 2: if (heap[i0 + 1]->ai > heap[idx]->ai) { idx = i0 + 1; } case 1: if (heap[i0 ]->ai > heap[idx]->ai) { idx = i0; } } *child = heap[idx]; return idx; } /* Move the given node up through the heap to the appropriate position. */ static MM_INLINE void mm_move_up_small(mm_node **heap, idx_t idx, mm_node *node, idx_t p_idx, mm_node *parent) { do { SWAP_NODES(heap, idx, node, p_idx, parent); if (idx == 0) { break; } p_idx = P_IDX(idx); parent = heap[p_idx]; } while (node->ai > parent->ai); } /* Move the given node down through the heap to the appropriate position. */ static MM_INLINE void mm_move_down_small(mm_node **heap, idx_t window, idx_t idx, mm_node *node) { mm_node *child; ai_t ai = node->ai; idx_t c_idx = mm_get_largest_child(heap, window, idx, &child); while (ai < child->ai) { SWAP_NODES(heap, idx, node, c_idx, child); c_idx = mm_get_largest_child(heap, window, idx, &child); } } /* Move the given node down through the heap to the appropriate position. */ static MM_INLINE void mm_move_down_large(mm_node **heap, idx_t idx, mm_node *node, idx_t p_idx, mm_node *parent) { do { SWAP_NODES(heap, idx, node, p_idx, parent); if (idx == 0) { break; } p_idx = P_IDX(idx); parent = heap[p_idx]; } while (node->ai < parent->ai); } /* Move the given node up through the heap to the appropriate position. */ static MM_INLINE void mm_move_up_large(mm_node **heap, idx_t window, idx_t idx, mm_node *node) { mm_node *child; ai_t ai = node->ai; idx_t c_idx = mm_get_smallest_child(heap, window, idx, &child); while (ai > child->ai) { SWAP_NODES(heap, idx, node, c_idx, child); c_idx = mm_get_smallest_child(heap, window, idx, &child); } } /* Swap the heap heads. */ static MM_INLINE void mm_swap_heap_heads(mm_node **s_heap, idx_t n_s, mm_node **l_heap, idx_t n_l, mm_node *s_node, mm_node *l_node) { s_node->region = LH; l_node->region = SH; s_heap[0] = l_node; l_heap[0] = s_node; mm_move_down_small(s_heap, n_s, 0, l_node); mm_move_up_large(l_heap, n_l, 0, s_node); } Bottleneck-1.2.1/bottleneck/src/move_median/move_median.h0000664000175000017500000000567113103166630022503 0ustar kgkg00000000000000#include #include #include #include #include #include typedef size_t idx_t; typedef double ai_t; #if BINARY_TREE==1 #define NUM_CHILDREN 2 #else /* maximum of 8 due to the manual loop-unrolling used in the code */ #define NUM_CHILDREN 8 #endif /* Find indices of parent and first child */ #define P_IDX(i) ((i) - 1) / NUM_CHILDREN #define FC_IDX(i) NUM_CHILDREN * (i) + 1 /* are we in the small heap (SM), large heap (LH) or NaN array (NA)? */ #define SH 0 #define LH 1 #define NA 2 #define FIRST_LEAF(n) ceil((n - 1) / (double)NUM_CHILDREN) struct _mm_node { int region; /* SH small heap, LH large heap, NA nan array */ ai_t ai; /* The node's value */ idx_t idx; /* The node's index in the heap or nan array */ struct _mm_node *next; /* The next node in order of insertion */ }; typedef struct _mm_node mm_node; struct _mm_handle { idx_t window; /* window size */ int odd; /* is window even (0) or odd (1) */ idx_t min_count; /* Same meaning as in bn.move_median */ idx_t n_s; /* Number of nodes in the small heap */ idx_t n_l; /* Number of nodes in the large heap */ idx_t n_n; /* Number of nodes in the nan array */ mm_node **s_heap; /* The max heap of small ai */ mm_node **l_heap; /* The min heap of large ai */ mm_node **n_array; /* The nan array */ mm_node **nodes; /* s_heap and l_heap point into this array */ mm_node *node_data; /* Pointer to memory location where nodes live */ mm_node *oldest; /* The oldest node */ mm_node *newest; /* The newest node (most recent insert) */ idx_t s_first_leaf; /* All nodes this index or greater are leaf nodes */ idx_t l_first_leaf; /* All nodes this index or greater are leaf nodes */ }; typedef struct _mm_handle mm_handle; /* non-nan functions */ mm_handle *mm_new(const idx_t window, idx_t min_count); ai_t mm_update_init(mm_handle *mm, ai_t ai); ai_t mm_update(mm_handle *mm, ai_t ai); /* nan functions */ mm_handle *mm_new_nan(const idx_t window, idx_t min_count); ai_t mm_update_init_nan(mm_handle *mm, ai_t ai); ai_t mm_update_nan(mm_handle *mm, ai_t ai); /* functions common to non-nan and nan cases */ void mm_reset(mm_handle *mm); void mm_free(mm_handle *mm); /* Copied from Cython ---------------------------------------------------- */ /* inline attribute */ #ifndef MM_INLINE #if defined(__GNUC__) #define MM_INLINE __inline__ #elif defined(_MSC_VER) #define MM_INLINE __inline #elif defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L #define MM_INLINE inline #else #define MM_INLINE #endif #endif /* NaN */ #ifdef NAN #define MM_NAN() ((float) NAN) #else static MM_INLINE float MM_NAN(void) { float value; memset(&value, 0xFF, sizeof(value)); return value; } #endif Bottleneck-1.2.1/bottleneck/src/move_median/move_median_debug.c0000664000175000017500000002071413103166630023637 0ustar kgkg00000000000000#include "move_median.h" ai_t *mm_move_median(ai_t *a, idx_t length, idx_t window, idx_t min_count); int mm_assert_equal(ai_t *actual, ai_t *desired, ai_t *input, idx_t length, char *err_msg); int mm_unit_test(void); void mm_dump(mm_handle *mm); void mm_print_binary_heap(mm_node **heap, idx_t n_array, idx_t oldest_idx, idx_t newest_idx); void mm_check(mm_handle *mm); void mm_print_chain(mm_handle *mm); void mm_print_line(void); void mm_print_node(mm_node *node); int main(void) { return mm_unit_test(); } /* moving window median of 1d arrays returns output array */ ai_t *mm_move_median(ai_t *a, idx_t length, idx_t window, idx_t min_count) { mm_handle *mm; ai_t *out; idx_t i; out = malloc(length * sizeof(ai_t)); mm = mm_new_nan(window, min_count); for (i=0; i < length; i++) { if (i < window) { out[i] = mm_update_init_nan(mm, a[i]); } else { out[i] = mm_update_nan(mm, a[i]); } if (i == window) { mm_print_line(); printf("window complete; switch to mm_update\n"); } mm_print_line(); printf("inserting ai = %f\n", a[i]); mm_print_chain(mm); mm_dump(mm); printf("\nmedian = %f\n\n", out[i]); mm_check(mm); } mm_free(mm); return out; } /* assert that two arrays are equal */ int mm_assert_equal(ai_t *actual, ai_t *desired, ai_t *input, idx_t length, char *err_msg) { idx_t i; int failed = 0; mm_print_line(); printf("%s\n", err_msg); mm_print_line(); printf("input actual desired\n"); for (i=0; i < length; i++) { if (isnan(actual[i]) && isnan(desired[i])) { printf("%9f %9f %9f\n", input[i], actual[i], desired[i]); } else if (actual[i] != desired[i]) { failed = 1; printf("%9f %9f %9f BUG\n", input[i], actual[i], desired[i]); } else printf("%9f %9f %9f\n", input[i], actual[i], desired[i]); } return failed; } int mm_unit_test(void) { ai_t arr_input[] = {0, 3, 7, NAN, 1, 5, 8, 9, 2, NAN}; ai_t desired[] = {0 , 1.5, 3, 5, 4, 3, 5, 8, 8, 5.5}; ai_t *actual; int window = 3; int min_count = 1; int length; char *err_msg; int failed; length = sizeof(arr_input) / sizeof(*arr_input); err_msg = malloc(1024 * sizeof *err_msg); sprintf(err_msg, "move_median failed with window=%d, min_count=%d", window, min_count); actual = mm_move_median(arr_input, length, window, min_count); failed = mm_assert_equal(actual, desired, arr_input, length, err_msg); free(actual); free(err_msg); return failed; } void mm_print_node(mm_node *node) { printf("\n\n%d small\n", node->region); printf("%d idx\n", node->idx); printf("%f ai\n", node->ai); printf("%p next\n\n", node->next); } void mm_print_chain(mm_handle *mm) { idx_t i; mm_node *node; printf("\nchain\n"); node = mm->oldest; printf("\t%6.2f region %d idx %d addr %p\n", node->ai, node->region, node->idx, node); for (i=1; i < mm->n_s + mm->n_l + mm->n_n; i++) { node = node->next; printf("\t%6.2f region %d idx %d addr %p\n", node->ai, node->region, node->idx, node); } } void mm_check(mm_handle *mm) { int ndiff; idx_t i; mm_node *child; mm_node *parent; // small heap for (i=0; in_s; i++) { assert(mm->s_heap[i]->idx == i); assert(mm->s_heap[i]->ai == mm->s_heap[i]->ai); if (i > 0) { child = mm->s_heap[i]; parent = mm->s_heap[P_IDX(child->idx)]; assert(child->ai <= parent->ai); } } // large heap for (i=0; in_l; i++) { assert(mm->l_heap[i]->idx == i); assert(mm->l_heap[i]->ai == mm->l_heap[i]->ai); if (i > 0) { child = mm->l_heap[i]; parent = mm->l_heap[P_IDX(child->idx)]; assert(child->ai >= parent->ai); } } // nan array for (i=0; in_n; i++) { assert(mm->n_array[i]->idx == i); assert(mm->n_array[i]->ai != mm->n_array[i]->ai); } // handle assert(mm->window >= mm->n_s + mm->n_l + mm->n_n); assert(mm->min_count <= mm->window); if (mm->n_s == 0) { assert(mm->s_first_leaf == 0); } else { assert(mm->s_first_leaf == FIRST_LEAF(mm->n_s)); } if (mm->n_l == 0) { assert(mm->l_first_leaf == 0); } else { assert(mm->l_first_leaf == FIRST_LEAF(mm->n_l)); } ndiff = (int)mm->n_s - (int)mm->n_l; if (ndiff < 0) { ndiff *= -1; } assert(ndiff <= 1); if (mm->n_s > 0 && mm->n_l > 0) { assert(mm->s_heap[0]->ai <= mm->l_heap[0]->ai); } } /* Print the two heaps to the screen */ void mm_dump(mm_handle *mm) { int i; idx_t idx; if (!mm) { printf("mm is empty"); return; } printf("\nhandle\n"); printf("\t%2d window\n", mm->window); printf("\t%2d n_s\n", mm->n_s); printf("\t%2d n_l\n", mm->n_l); printf("\t%2d n_n\n", mm->n_n); printf("\t%2d min_count\n", mm->min_count); printf("\t%2d s_first_leaf\n", mm->s_first_leaf); printf("\t%2d l_first_leaf\n", mm->l_first_leaf); if (NUM_CHILDREN == 2) { // binary heap int idx0; int idx1; printf("\nsmall heap\n"); idx0 = -1; if (mm->oldest->region == SH) { idx0 = mm->oldest->idx; } idx1 = -1; if (mm->newest->region == SH) { idx1 = mm->newest->idx; } mm_print_binary_heap(mm->s_heap, mm->n_s, idx0, idx1); printf("\nlarge heap\n"); idx0 = -1; if (mm->oldest->region == LH) { idx0 = mm->oldest->idx; } idx1 = -1; if (mm->newest->region == LH) { idx1 = mm->newest->idx; } mm_print_binary_heap(mm->l_heap, mm->n_l, idx0, idx1); printf("\nnan array\n"); idx0 = -1; if (mm->oldest->region == NA) { idx0 = mm->oldest->idx; } idx1 = -1; if (mm->newest->region == NA) { idx1 = mm->newest->idx; } for(i = 0; i < (int)mm->n_n; ++i) { idx = mm->n_array[i]->idx; if (i == idx0 && i == idx1) { printf("\t%i >%f<\n", idx, mm->n_array[i]->ai); } else if (i == idx0) { printf("\t%i >%f\n", idx, mm->n_array[i]->ai); } else if (i == idx1) { printf("\t%i %f<\n", idx, mm->n_array[i]->ai); } else { printf("\t%i %f\n", idx, mm->n_array[i]->ai); } } } else { // not a binary heap if (mm->oldest) printf("\n\nFirst: %f\n", (double)mm->oldest->ai); if (mm->newest) printf("Last: %f\n", (double)mm->newest->ai); printf("\n\nSmall heap:\n"); for(i = 0; i < (int)mm->n_s; ++i) { printf("%i %f\n", (int)mm->s_heap[i]->idx, mm->s_heap[i]->ai); } printf("\n\nLarge heap:\n"); for(i = 0; i < (int)mm->n_l; ++i) { printf("%i %f\n", (int)mm->l_heap[i]->idx, mm->l_heap[i]->ai); } printf("\n\nNaN heap:\n"); for(i = 0; i < (int)mm->n_n; ++i) { printf("%i %f\n", (int)mm->n_array[i]->idx, mm->n_array[i]->ai); } } } /* Code to print a binary tree from http://stackoverflow.com/a/13755783 * Code modified for bottleneck's needs. */ void mm_print_binary_heap(mm_node **heap, idx_t n_array, idx_t oldest_idx, idx_t newest_idx) { const int line_width = 77; int print_pos[n_array]; int i, j, k, pos, x=1, level=0; print_pos[0] = 0; for(i=0,j=1; i<(int)n_array; i++,j++) { pos = print_pos[(i-1)/2]; pos += (i%2?-1:1)*(line_width/(pow(2,level+1))+1); for (k=0; k%.2f", heap[i]->ai); } else if (i == (int)newest_idx) { printf("%.2f<", heap[i]->ai); } else { printf("%.2f", heap[i]->ai); } print_pos[i] = x = pos+1; if (j==pow(2,level)) { printf("\n"); level++; x = 1; j = 0; } } } void mm_print_line(void) { int i, width = 70; for (i=0; i < width; i++) printf("-"); printf("\n"); } Bottleneck-1.2.1/bottleneck/src/template.py0000664000175000017500000001173513103166630017747 0ustar kgkg00000000000000import os import re import ast def make_c_files(): modules = ['reduce', 'move', 'nonreduce', 'nonreduce_axis'] dirpath = os.path.dirname(__file__) for module in modules: filepath = os.path.join(dirpath, module + '_template.c') with open(filepath, 'r') as f: src_str = f.read() src_str = template(src_str) filepath = os.path.join(dirpath, module + '.c') with open(filepath, 'w') as f: f.write(src_str) def template(src_str): src_list = src_str.splitlines() src_list = repeat_templating(src_list) src_list = dtype_templating(src_list) src_list = string_templating(src_list) src_str = '\n'.join(src_list) src_str = re.sub(r'\n\s*\n\s*\n', r'\n\n', src_str) return src_str # repeat -------------------------------------------------------------------- REPEAT_BEGIN = r'^/\*\s*repeat\s*=\s*' REPEAT_END = r'^/\*\s*repeat end' COMMENT_END = r'.*\*\/.*' def repeat_templating(lines): index = 0 while True: idx0, idx1 = next_block(lines, index, REPEAT_BEGIN, REPEAT_END) if idx0 is None: break func_list = lines[idx0:idx1] func_list = expand_functions_repeat(func_list) # the +1 below is to skip the /* repeat end */ line lines = lines[:idx0] + func_list + lines[idx1+1:] index = idx0 return lines def expand_functions_repeat(lines): idx = first_occurence(COMMENT_END, lines) repeat_dict = repeat_info(lines[:idx + 1]) lines = lines[idx + 1:] func_str = '\n'.join(lines) func_list = expand_repeat(func_str, repeat_dict) return func_list def repeat_info(lines): line = ''.join(lines) repeat = re.findall(r'\{.*\}', line) repeat_dict = ast.literal_eval(repeat[0]) return repeat_dict def expand_repeat(func_str, repeat_dict): nrepeats = [len(repeat_dict[key]) for key in repeat_dict] if len(set(nrepeats)) != 1: raise ValueError("All repeat lists must be the same length") nrepeat = nrepeats[0] func_list = [] for i in range(nrepeat): f = func_str[:] for key in repeat_dict: f = f.replace(key, repeat_dict[key][i]) func_list.append('\n' + f) func_list = (''.join(func_list)).splitlines() return func_list # dtype --------------------------------------------------------------------- DTYPE_BEGIN = r'^/\*\s*dtype\s*=\s*' DTYPE_END = r'^/\*\s*dtype end' def dtype_templating(lines): index = 0 while True: idx0, idx1 = next_block(lines, index, DTYPE_BEGIN, DTYPE_END) if idx0 is None: break func_list = lines[idx0:idx1] func_list = expand_functions_dtype(func_list) # the +1 below is to skip the /* dtype end */ line lines = lines[:idx0] + func_list + lines[idx1+1:] index = idx0 return lines def expand_functions_dtype(lines): idx = first_occurence(COMMENT_END, lines) dtypes = dtype_info(lines[:idx + 1]) lines = lines[idx + 1:] func_str = '\n'.join(lines) func_list = expand_dtypes(func_str, dtypes) return func_list def dtype_info(lines): line = ''.join(lines) dtypes = re.findall(r'\[.*\]', line) if len(dtypes) != 1: raise ValueError("expecting exactly one dtype specification") dtypes = ast.literal_eval(dtypes[0]) return dtypes def expand_dtypes(func_str, dtypes): if 'DTYPE' not in func_str: raise ValueError("cannot find dtype marker") func_list = [] for dtype in dtypes: f = func_str[:] for i, dt in enumerate(dtype): f = f.replace('DTYPE%d' % i, dt) if i > 0: f = f + '\n' func_list.append('\n\n' + f) return func_list # multiline strings --------------------------------------------------------- STRING_BEGIN = r'.*MULTILINE STRING BEGIN.*' STRING_END = r'.*MULTILINE STRING END.*' def string_templating(lines): index = 0 while True: idx0, idx1 = next_block(lines, index, STRING_BEGIN, STRING_END) if idx0 is None: break str_list = lines[idx0+1:idx1] str_list = quote_string(str_list) lines = lines[:idx0] + str_list + lines[idx1+1:] index = idx0 return lines def quote_string(lines): for i in range(len(lines)): lines[i] = "\"" + lines[i] + r"\n" + "\"" lines[-1] = lines[-1] + ";" return lines # utility ------------------------------------------------------------------- def first_occurence(pattern, lines): for i in range(len(lines)): if re.match(pattern, lines[i]): return i raise ValueError("`pattern` not found") def next_block(lines, index, begine_pattern, end_pattern): idx = None for i in range(index, len(lines)): line = lines[i] if re.match(begine_pattern, line): idx = i elif re.match(end_pattern, line): if idx is None: raise ValueError("found end of function before beginning") return idx, i return None, None Bottleneck-1.2.1/bottleneck/src/bottleneck.h0000664000175000017500000001273213103166630020063 0ustar kgkg00000000000000#ifndef BOTTLENECK_H #define BOTTLENECK_H #include #define NPY_NO_DEPRECATED_API NPY_1_11_API_VERSION #include /* THREADS=1 releases the GIL but increases function call * overhead. THREADS=0 does not release the GIL but keeps * function call overhead low. Curly brackets are for C89 * support. */ #define THREADS 1 #if THREADS #define BN_BEGIN_ALLOW_THREADS Py_BEGIN_ALLOW_THREADS { #define BN_END_ALLOW_THREADS ;} Py_END_ALLOW_THREADS #else #define BN_BEGIN_ALLOW_THREADS { #define BN_END_ALLOW_THREADS } #endif /* for ease of dtype templating */ #define NPY_float64 NPY_FLOAT64 #define NPY_float32 NPY_FLOAT32 #define NPY_int64 NPY_INT64 #define NPY_int32 NPY_INT32 #define NPY_intp NPY_INTP #define NPY_MAX_int64 NPY_MAX_INT64 #define NPY_MAX_int32 NPY_MAX_INT32 #define NPY_MIN_int64 NPY_MIN_INT64 #define NPY_MIN_int32 NPY_MIN_INT32 #if PY_MAJOR_VERSION >= 3 #define PyString_FromString PyBytes_FromString #define PyInt_AsLong PyLong_AsLong #define PyString_InternFromString PyUnicode_InternFromString #endif #define VARKEY METH_VARARGS | METH_KEYWORDS #define error_converting(x) (((x) == -1) && PyErr_Occurred()) #define VALUE_ERR(text) PyErr_SetString(PyExc_ValueError, text) #define TYPE_ERR(text) PyErr_SetString(PyExc_TypeError, text) #define MEMORY_ERR(text) PyErr_SetString(PyExc_MemoryError, text) #define RUNTIME_ERR(text) PyErr_SetString(PyExc_RuntimeError, text) /* `inline` copied from NumPy. */ #if defined(_MSC_VER) #define BN_INLINE __inline #elif defined(__GNUC__) #if defined(__STRICT_ANSI__) #define BN_INLINE __inline__ #else #define BN_INLINE inline #endif #else #define BN_INLINE #endif /* * NAN and INFINITY like macros (same behavior as glibc for NAN, same as C99 * for INFINITY). Copied from NumPy. */ BN_INLINE static float __bn_inff(void) { const union { npy_uint32 __i; float __f;} __bint = {0x7f800000UL}; return __bint.__f; } BN_INLINE static float __bn_nanf(void) { const union { npy_uint32 __i; float __f;} __bint = {0x7fc00000UL}; return __bint.__f; } #define BN_INFINITYF __bn_inff() #define BN_NANF __bn_nanf() #define BN_INFINITY ((npy_double)BN_INFINITYF) #define BN_NAN ((npy_double)BN_NANF) #define C_CONTIGUOUS(a) PyArray_CHKFLAGS(a, NPY_ARRAY_C_CONTIGUOUS) #define F_CONTIGUOUS(a) PyArray_CHKFLAGS(a, NPY_ARRAY_F_CONTIGUOUS) #define IS_CONTIGUOUS(a) (C_CONTIGUOUS(a) || F_CONTIGUOUS(a)) /* WIRTH ----------------------------------------------------------------- */ /* WIRTH macro based on: Fast median search: an ANSI C implementation Nicolas Devillard - ndevilla AT free DOT fr July 1998 which, in turn, took the algorithm from Wirth, Niklaus Algorithms + data structures = programs, p. 366 Englewood Cliffs: Prentice-Hall, 1976 Adapted for Bottleneck: (C) 2016 Keith Goodman */ #define WIRTH(dtype) \ x = B(dtype, k); \ i = l; \ j = r; \ do { \ while (B(dtype, i) < x) i++; \ while (x < B(dtype, j)) j--; \ if (i <= j) { \ npy_##dtype atmp = B(dtype, i); \ B(dtype, i) = B(dtype, j); \ B(dtype, j) = atmp; \ i++; \ j--; \ } \ } while (i <= j); \ if (j < k) l = i; \ if (k < i) r = j; /* partition ------------------------------------------------------------- */ #define PARTITION(dtype) \ while (l < r) { \ npy_##dtype x; \ npy_##dtype al = B(dtype, l); \ npy_##dtype ak = B(dtype, k); \ npy_##dtype ar = B(dtype, r); \ if (al > ak) { \ if (ak < ar) { \ if (al < ar) { \ B(dtype, k) = al; \ B(dtype, l) = ak; \ } \ else { \ B(dtype, k) = ar; \ B(dtype, r) = ak; \ } \ } \ } \ else { \ if (ak > ar) { \ if (al > ar) { \ B(dtype, k) = al; \ B(dtype, l) = ak; \ } \ else { \ B(dtype, k) = ar; \ B(dtype, r) = ak; \ } \ } \ } \ WIRTH(dtype) \ } /* slow ------------------------------------------------------------------ */ static PyObject *slow_module = NULL; static PyObject * slow(char *name, PyObject *args, PyObject *kwds) { PyObject *func = NULL; PyObject *out = NULL; if (slow_module == NULL) { /* bottleneck.slow has not been imported during the current * python session. Only import it once per session to save time */ slow_module = PyImport_ImportModule("bottleneck.slow"); if (slow_module == NULL) { PyErr_SetString(PyExc_RuntimeError, "Cannot import bottleneck.slow"); return NULL; } } func = PyObject_GetAttrString(slow_module, name); if (func == NULL) { PyErr_Format(PyExc_RuntimeError, "Cannot import %s from bottleneck.slow", name); return NULL; } if (PyCallable_Check(func)) { out = PyObject_Call(func, args, kwds); if (out == NULL) { Py_XDECREF(func); return NULL; } } else { Py_XDECREF(func); PyErr_Format(PyExc_RuntimeError, "bottleneck.slow.%s is not callable", name); return NULL; } Py_XDECREF(func); return out; } #endif /* BOTTLENECK_H */ Bottleneck-1.2.1/bottleneck/src/move_template.c0000664000175000017500000012630113106351704020564 0ustar kgkg00000000000000#include "bottleneck.h" #include "iterators.h" #include "move_median/move_median.h" /* move_min, move_max, move_argmin, and move_argmax are based on the minimum on a sliding window algorithm by Richard Harter http://www.richardhartersworld.com/cri/2001/slidingmin.html Copyright Richard Harter 2009 Released under a Simplified BSD license Adapted, expanded, and added NaN handling for Bottleneck: Copyright 2010, 2014, 2015, 2016 Keith Goodman Released under the Bottleneck license */ /* macros ---------------------------------------------------------------- */ #define INIT(dtype) \ PyObject *y = PyArray_EMPTY(PyArray_NDIM(a), PyArray_SHAPE(a), dtype, 0); \ iter2 it; \ init_iter2(&it, a, y, axis); /* low-level functions such as move_sum_float64 */ #define MOVE(name, dtype) \ static PyObject * \ name##_##dtype(PyArrayObject *a, \ int window, \ int min_count, \ int axis, \ int ddof) /* top-level functions such as move_sum */ #define MOVE_MAIN(name, ddof) \ static PyObject * \ name(PyObject *self, PyObject *args, PyObject *kwds) \ { \ return mover(#name, \ args, \ kwds, \ name##_float64, \ name##_float32, \ name##_int64, \ name##_int32, \ ddof); \ } /* typedefs and prototypes ----------------------------------------------- */ /* used by move_min and move_max */ struct _pairs { double value; int death; }; typedef struct _pairs pairs; /* function pointer for functions passed to mover */ typedef PyObject *(*move_t)(PyArrayObject *, int, int, int, int); static PyObject * mover(char *name, PyObject *args, PyObject *kwds, move_t, move_t, move_t, move_t, int has_ddof); /* move_sum -------------------------------------------------------------- */ /* dtype = [['float64'], ['float32']] */ MOVE(move_sum, DTYPE0) { Py_ssize_t count; npy_DTYPE0 asum, ai, aold; INIT(NPY_DTYPE0) BN_BEGIN_ALLOW_THREADS WHILE { asum = count = 0; WHILE0 { ai = AI(DTYPE0); if (ai == ai) { asum += ai; count += 1; } YI(DTYPE0) = BN_NAN; } WHILE1 { ai = AI(DTYPE0); if (ai == ai) { asum += ai; count += 1; } YI(DTYPE0) = count >= min_count ? asum : BN_NAN; } WHILE2 { ai = AI(DTYPE0); aold = AOLD(DTYPE0); if (ai == ai) { if (aold == aold) { asum += ai - aold; } else { asum += ai; count++; } } else { if (aold == aold) { asum -= aold; count--; } } YI(DTYPE0) = count >= min_count ? asum : BN_NAN; } NEXT2 } BN_END_ALLOW_THREADS return y; } /* dtype end */ /* dtype = [['int64', 'float64'], ['int32', 'float64']] */ MOVE(move_sum, DTYPE0) { npy_DTYPE1 asum; INIT(NPY_DTYPE1) BN_BEGIN_ALLOW_THREADS WHILE { asum = 0; WHILE0 { asum += AI(DTYPE0); YI(DTYPE1) = BN_NAN; } WHILE1 { asum += AI(DTYPE0); YI(DTYPE1) = asum; } WHILE2 { asum += AI(DTYPE0) - AOLD(DTYPE0); YI(DTYPE1) = asum; } NEXT2 } BN_END_ALLOW_THREADS return y; } /* dtype end */ MOVE_MAIN(move_sum, 0) /* move_mean -------------------------------------------------------------- */ /* dtype = [['float64'], ['float32']] */ MOVE(move_mean, DTYPE0) { Py_ssize_t count; npy_DTYPE0 asum, ai, aold, count_inv; INIT(NPY_DTYPE0) BN_BEGIN_ALLOW_THREADS WHILE { asum = count = 0; WHILE0 { ai = AI(DTYPE0); if (ai == ai) { asum += ai; count += 1; } YI(DTYPE0) = BN_NAN; } WHILE1 { ai = AI(DTYPE0); if (ai == ai) { asum += ai; count += 1; } YI(DTYPE0) = count >= min_count ? asum / count : BN_NAN; } count_inv = 1.0 / count; WHILE2 { ai = AI(DTYPE0); aold = AOLD(DTYPE0); if (ai == ai) { if (aold == aold) { asum += ai - aold; } else { asum += ai; count++; count_inv = 1.0 / count; } } else { if (aold == aold) { asum -= aold; count--; count_inv = 1.0 / count; } } YI(DTYPE0) = count >= min_count ? asum * count_inv : BN_NAN; } NEXT2 } BN_END_ALLOW_THREADS return y; } /* dtype end */ /* dtype = [['int64', 'float64'], ['int32', 'float64']] */ MOVE(move_mean, DTYPE0) { npy_DTYPE1 asum, window_inv = 1.0 / window; INIT(NPY_DTYPE1) BN_BEGIN_ALLOW_THREADS WHILE { asum = 0; WHILE0 { asum += AI(DTYPE0); YI(DTYPE1) = BN_NAN; } WHILE1 { asum += AI(DTYPE0); *(npy_DTYPE1*)(it.py + it.i * it.ystride) = (npy_DTYPE1)asum / (it.i + 1); it.i++; } WHILE2 { asum += AI(DTYPE0) - AOLD(DTYPE0); YI(DTYPE1) = (npy_DTYPE1)asum * window_inv; } NEXT2 } BN_END_ALLOW_THREADS return y; } /* dtype end */ MOVE_MAIN(move_mean, 0) /* move_std, move_var ---------------------------------------------------- */ /* repeat = {'NAME': ['move_std', 'move_var'], 'FUNC': ['sqrt', '']} */ /* dtype = [['float64'], ['float32']] */ MOVE(NAME, DTYPE0) { Py_ssize_t count; npy_DTYPE0 delta, amean, assqdm, ai, aold, yi, count_inv, ddof_inv; INIT(NPY_DTYPE0) BN_BEGIN_ALLOW_THREADS WHILE { amean = assqdm = count = 0; WHILE0 { ai = AI(DTYPE0); if (ai == ai) { count += 1; delta = ai - amean; amean += delta / count; assqdm += delta * (ai - amean); } YI(DTYPE0) = BN_NAN; } WHILE1 { ai = AI(DTYPE0); if (ai == ai) { count += 1; delta = ai - amean; amean += delta / count; assqdm += delta * (ai - amean); } if (count >= min_count) { if (assqdm < 0) { assqdm = 0; } yi = FUNC(assqdm / (count - ddof)); } else { yi = BN_NAN; } YI(DTYPE0) = yi; } count_inv = 1.0 / count; ddof_inv = 1.0 / (count - ddof); WHILE2 { ai = AI(DTYPE0); aold = AOLD(DTYPE0); if (ai == ai) { if (aold == aold) { delta = ai - aold; aold -= amean; amean += delta * count_inv; ai -= amean; assqdm += (ai + aold) * delta; } else { count++; count_inv = 1.0 / count; ddof_inv = 1.0 / (count - ddof); delta = ai - amean; amean += delta * count_inv; assqdm += delta * (ai - amean); } } else { if (aold == aold) { count--; count_inv = 1.0 / count; ddof_inv = 1.0 / (count - ddof); if (count > 0) { delta = aold - amean; amean -= delta * count_inv; assqdm -= delta * (aold - amean); } else { amean = 0; assqdm = 0; } } } if (count >= min_count) { if (assqdm < 0) { assqdm = 0; } yi = FUNC(assqdm * ddof_inv); } else { yi = BN_NAN; } YI(DTYPE0) = yi; } NEXT2 } BN_END_ALLOW_THREADS return y; } /* dtype end */ /* dtype = [['int64', 'float64'], ['int32', 'float64']] */ MOVE(NAME, DTYPE0) { int winddof = window - ddof; npy_DTYPE1 delta, amean, assqdm, yi, ai, aold; npy_DTYPE1 window_inv = 1.0 / window, winddof_inv = 1.0 / winddof; INIT(NPY_DTYPE1) BN_BEGIN_ALLOW_THREADS WHILE { amean = assqdm = 0; WHILE0 { ai = AI(DTYPE0); delta = ai - amean; amean += delta / (INDEX + 1); assqdm += delta * (ai - amean); YI(DTYPE1) = BN_NAN; } WHILE1 { ai = AI(DTYPE0); delta = ai - amean; amean += delta / (INDEX + 1); assqdm += delta * (ai - amean); yi = FUNC(assqdm / (INDEX + 1 - ddof)); YI(DTYPE1) = yi; } WHILE2 { ai = AI(DTYPE0); aold = AOLD(DTYPE0); delta = ai - aold; aold -= amean; amean += delta * window_inv; ai -= amean; assqdm += (ai + aold) * delta; if (assqdm < 0) { assqdm = 0; } YI(DTYPE1) = FUNC(assqdm * winddof_inv); } NEXT2 } BN_END_ALLOW_THREADS return y; } /* dtype end */ MOVE_MAIN(NAME, 1) /* repeat end */ /* move_min, move_max, move_argmin, move_argmax -------------------------- */ /* repeat = {'MACRO_FLOAT': ['MOVE_NANMIN', 'MOVE_NANMAX'], 'MACRO_INT': ['MOVE_MIN', 'MOVE_MAX'], 'COMPARE': ['<=', '>='], 'FLIP': ['>=', '<='], 'BIG_FLOAT': ['BN_INFINITY', '-BN_INFINITY']} */ #define MACRO_FLOAT(dtype, yi, code) \ ai = AI(dtype); \ if (ai == ai) count++; else ai = BIG_FLOAT; \ code; \ if (ai COMPARE extreme_pair->value) { \ extreme_pair->value = ai; \ extreme_pair->death = INDEX + window; \ last = extreme_pair; \ } \ else { \ while (last->value FLIP ai) { \ if (last == ring) last = end; \ last--; \ } \ last++; \ if (last == end) last = ring; \ last->value = ai; \ last->death = INDEX + window; \ } \ yi_tmp = yi; /* yi might contain i and YI contains i++ */ \ YI(dtype) = yi_tmp; #define MACRO_INT(a_dtype, y_dtype, yi, code) \ ai = AI(a_dtype); \ code; \ if (ai COMPARE extreme_pair->value) { \ extreme_pair->value = ai; \ extreme_pair->death = INDEX + window; \ last = extreme_pair; \ } \ else { \ while (last->value FLIP ai) { \ if (last == ring) last = end; \ last--; \ } \ last++; \ if (last == end) last = ring; \ last->value = ai; \ last->death = INDEX + window; \ } \ yi_tmp = yi; \ YI(y_dtype) = yi_tmp; /* repeat end */ /* repeat = { 'NAME': ['move_min', 'move_max', 'move_argmin', 'move_argmax'], 'MACRO_FLOAT': ['MOVE_NANMIN', 'MOVE_NANMAX', 'MOVE_NANMIN', 'MOVE_NANMAX'], 'MACRO_INT': ['MOVE_MIN', 'MOVE_MAX', 'MOVE_MIN', 'MOVE_MAX'], 'COMPARE': ['<=', '>=', '<=', '>='], 'BIG_FLOAT': ['BN_INFINITY', '-BN_INFINITY', 'BN_INFINITY', '-BN_INFINITY'], 'BIG_INT': ['NPY_MAX_DTYPE0', 'NPY_MIN_DTYPE0', 'NPY_MAX_DTYPE0', 'NPY_MIN_DTYPE0'], 'VALUE': ['extreme_pair->value', 'extreme_pair->value', 'INDEX-extreme_pair->death+window', 'INDEX-extreme_pair->death+window'] } */ /* dtype = [['float64'], ['float32']] */ MOVE(NAME, DTYPE0) { npy_DTYPE0 ai, aold, yi_tmp; Py_ssize_t count; pairs *extreme_pair; pairs *end; pairs *last; pairs *ring = (pairs *)malloc(window * sizeof(pairs)); INIT(NPY_DTYPE0) BN_BEGIN_ALLOW_THREADS WHILE { count = 0; end = ring + window; last = ring; extreme_pair = ring; ai = A0(DTYPE0); extreme_pair->value = ai == ai ? ai : BIG_FLOAT; extreme_pair->death = window; WHILE0 { MACRO_FLOAT(DTYPE0, BN_NAN, ) } WHILE1 { MACRO_FLOAT(DTYPE0, count >= min_count ? VALUE : BN_NAN, ) } WHILE2 { MACRO_FLOAT(DTYPE0, count >= min_count ? VALUE : BN_NAN, aold = AOLD(DTYPE0); if (aold == aold) count--; if (extreme_pair->death == INDEX) { extreme_pair++; if (extreme_pair >= end) extreme_pair = ring; }) } NEXT2 } free(ring); BN_END_ALLOW_THREADS return y; } /* dtype end */ /* dtype = [['int64', 'float64'], ['int32', 'float64']] */ MOVE(NAME, DTYPE0) { npy_DTYPE0 ai; npy_DTYPE1 yi_tmp; pairs *extreme_pair; pairs *end; pairs *last; pairs *ring = (pairs *)malloc(window * sizeof(pairs)); INIT(NPY_DTYPE1) BN_BEGIN_ALLOW_THREADS WHILE { end = ring + window; last = ring; extreme_pair = ring; ai = A0(DTYPE0); extreme_pair->value = ai; extreme_pair->death = window; WHILE0 { MACRO_INT(DTYPE0, DTYPE1, BN_NAN, ) } WHILE1 { MACRO_INT(DTYPE0, DTYPE1, VALUE, ) } WHILE2 { MACRO_INT(DTYPE0, DTYPE1, VALUE, if (extreme_pair->death == INDEX) { extreme_pair++; if (extreme_pair >= end) extreme_pair = ring; }) } NEXT2 } free(ring); BN_END_ALLOW_THREADS return y; } /* dtype end */ MOVE_MAIN(NAME, 0) /* repeat end */ /* move_median ----------------------------------------------------------- */ /* dtype = [['float64'], ['float32']] */ MOVE(move_median, DTYPE0) { npy_DTYPE0 ai; mm_handle *mm = mm_new_nan(window, min_count); INIT(NPY_DTYPE0) if (window == 1) { mm_free(mm); return PyArray_Copy(a); } if (mm == NULL) { MEMORY_ERR("Could not allocate memory for move_median"); } BN_BEGIN_ALLOW_THREADS WHILE { WHILE0 { ai = AI(DTYPE0); YI(DTYPE0) = mm_update_init_nan(mm, ai); } WHILE1 { ai = AI(DTYPE0); YI(DTYPE0) = mm_update_init_nan(mm, ai); } WHILE2 { ai = AI(DTYPE0); YI(DTYPE0) = mm_update_nan(mm, ai); } mm_reset(mm); NEXT2 } mm_free(mm); BN_END_ALLOW_THREADS return y; } /* dtype end */ /* dtype = [['int64', 'float64'], ['int32', 'float64']] */ MOVE(move_median, DTYPE0) { npy_DTYPE0 ai; mm_handle *mm = mm_new(window, min_count); INIT(NPY_DTYPE1) if (window == 1) { return PyArray_CastToType(a, PyArray_DescrFromType(NPY_DTYPE1), PyArray_CHKFLAGS(a, NPY_ARRAY_F_CONTIGUOUS)); } if (mm == NULL) { MEMORY_ERR("Could not allocate memory for move_median"); } BN_BEGIN_ALLOW_THREADS WHILE { WHILE0 { ai = AI(DTYPE0); YI(DTYPE1) = mm_update_init(mm, ai); } WHILE1 { ai = AI(DTYPE0); YI(DTYPE1) = mm_update_init(mm, ai); } WHILE2 { ai = AI(DTYPE0); YI(DTYPE1) = mm_update(mm, ai); } mm_reset(mm); NEXT2 } mm_free(mm); BN_END_ALLOW_THREADS return y; } /* dtype end */ MOVE_MAIN(move_median, 0) /* move_rank-------------------------------------------------------------- */ #define MOVE_RANK(dtype0, dtype1, limit) \ Py_ssize_t j; \ npy_##dtype0 ai, aj; \ npy_##dtype1 g, e, n, r; \ ai = AI(dtype0); \ if (ai == ai) { \ g = 0; \ e = 1; \ n = 1; \ r = 0; \ for (j = limit; j < INDEX; j++) { \ aj = AX(dtype0, j); \ if (aj == aj) { \ n++; \ if (ai > aj) g += 2; \ else if (ai == aj) e++; \ } \ } \ if (n < min_count) { \ r = BN_NAN; \ } \ else if (n == 1) { \ r = 0.0; \ } \ else { \ r = 0.5 * (g + e - 1.0); \ r = r / (n - 1.0); \ r = 2.0 * (r - 0.5); \ } \ } \ else { \ r = BN_NAN; \ } \ /* dtype = [['float64', 'float64'], ['float32', 'float32']] */ MOVE(move_rank, DTYPE0) { INIT(NPY_DTYPE1) BN_BEGIN_ALLOW_THREADS WHILE { WHILE0 { YI(DTYPE1) = BN_NAN; } WHILE1 { MOVE_RANK(DTYPE0, DTYPE1, 0) YI(DTYPE1) = r; } WHILE2 { MOVE_RANK(DTYPE0, DTYPE1, INDEX - window + 1) YI(DTYPE1) = r; } NEXT2 } BN_END_ALLOW_THREADS return y; } /* dtype end */ /* dtype = [['int64', 'float64'], ['int32', 'float64']] */ MOVE(move_rank, DTYPE0) { Py_ssize_t j; npy_DTYPE0 ai, aj; npy_DTYPE1 g, e, r, window_inv = 0.5 * 1.0 / (window - 1); INIT(NPY_DTYPE1) BN_BEGIN_ALLOW_THREADS WHILE { WHILE0 { YI(DTYPE1) = BN_NAN; } WHILE1 { ai = AI(DTYPE0); g = 0; e = 1; r = 0; for (j = 0; j < INDEX; j++) { aj = AX(DTYPE0, j); if (ai > aj) g += 2; else if (ai == aj) e++; } if (INDEX < min_count - 1) { r = BN_NAN; } else if (INDEX == 0) { r = 0.0; } else { r = 0.5 * (g + e - 1.0); r = r / INDEX; r = 2.0 * (r - 0.5); } YI(DTYPE1) = r; } WHILE2 { ai = AI(DTYPE0); g = 0; e = 1; r = 0; for (j = INDEX - window + 1; j < INDEX; j++) { aj = AX(DTYPE0, j); if (aj == aj) { if (ai > aj) g += 2; else if (ai == aj) e++; } } if (window == 1) { r = 0.0; } else { r = window_inv * (g + e - 1.0); r = 2.0 * (r - 0.5); } YI(DTYPE1) = r; } NEXT2 } BN_END_ALLOW_THREADS return y; } /* dtype end */ MOVE_MAIN(move_rank, 0) /* python strings -------------------------------------------------------- */ PyObject *pystr_a = NULL; PyObject *pystr_window = NULL; PyObject *pystr_min_count = NULL; PyObject *pystr_axis = NULL; PyObject *pystr_ddof = NULL; static int intern_strings(void) { pystr_a = PyString_InternFromString("a"); pystr_window = PyString_InternFromString("window"); pystr_min_count = PyString_InternFromString("min_count"); pystr_axis = PyString_InternFromString("axis"); pystr_ddof = PyString_InternFromString("ddof"); return pystr_a && pystr_window && pystr_min_count && pystr_axis && pystr_ddof; } /* mover ----------------------------------------------------------------- */ static BN_INLINE int parse_args(PyObject *args, PyObject *kwds, int has_ddof, PyObject **a, PyObject **window, PyObject **min_count, PyObject **axis, PyObject **ddof) { const Py_ssize_t nargs = PyTuple_GET_SIZE(args); const Py_ssize_t nkwds = kwds == NULL ? 0 : PyDict_Size(kwds); if (nkwds) { int nkwds_found = 0; PyObject *tmp; switch (nargs) { case 4: if (has_ddof) { *axis = PyTuple_GET_ITEM(args, 3); } else { TYPE_ERR("wrong number of arguments"); return 0; } case 3: *min_count = PyTuple_GET_ITEM(args, 2); case 2: *window = PyTuple_GET_ITEM(args, 1); case 1: *a = PyTuple_GET_ITEM(args, 0); case 0: break; default: TYPE_ERR("wrong number of arguments"); return 0; } switch (nargs) { case 0: *a = PyDict_GetItem(kwds, pystr_a); if (*a == NULL) { TYPE_ERR("Cannot find `a` keyword input"); return 0; } nkwds_found += 1; case 1: *window = PyDict_GetItem(kwds, pystr_window); if (*window == NULL) { TYPE_ERR("Cannot find `window` keyword input"); return 0; } nkwds_found++; case 2: tmp = PyDict_GetItem(kwds, pystr_min_count); if (tmp != NULL) { *min_count = tmp; nkwds_found++; } case 3: tmp = PyDict_GetItem(kwds, pystr_axis); if (tmp != NULL) { *axis = tmp; nkwds_found++; } case 4: if (has_ddof) { tmp = PyDict_GetItem(kwds, pystr_ddof); if (tmp != NULL) { *ddof = tmp; nkwds_found++; } break; } break; default: TYPE_ERR("wrong number of arguments"); return 0; } if (nkwds_found != nkwds) { TYPE_ERR("wrong number of keyword arguments"); return 0; } if (nargs + nkwds_found > 4 + has_ddof) { TYPE_ERR("too many arguments"); return 0; } } else { switch (nargs) { case 5: if (has_ddof) { *ddof = PyTuple_GET_ITEM(args, 4); } else { TYPE_ERR("wrong number of arguments"); return 0; } case 4: *axis = PyTuple_GET_ITEM(args, 3); case 3: *min_count = PyTuple_GET_ITEM(args, 2); case 2: *window = PyTuple_GET_ITEM(args, 1); *a = PyTuple_GET_ITEM(args, 0); break; default: TYPE_ERR("wrong number of arguments"); return 0; } } return 1; } static PyObject * mover(char *name, PyObject *args, PyObject *kwds, move_t move_float64, move_t move_float32, move_t move_int64, move_t move_int32, int has_ddof) { int mc; int window; int axis; int ddof; int dtype; int ndim; Py_ssize_t length; PyArrayObject *a; PyObject *a_obj = NULL; PyObject *window_obj = NULL; PyObject *min_count_obj = Py_None; PyObject *axis_obj = NULL; PyObject *ddof_obj = NULL; if (!parse_args(args, kwds, has_ddof, &a_obj, &window_obj, &min_count_obj, &axis_obj, &ddof_obj)) { return NULL; } /* convert to array if necessary */ if PyArray_Check(a_obj) { a = (PyArrayObject *)a_obj; } else { a = (PyArrayObject *)PyArray_FROM_O(a_obj); if (a == NULL) { return NULL; } } /* check for byte swapped input array */ if PyArray_ISBYTESWAPPED(a) { return slow(name, args, kwds); } /* window */ window = PyArray_PyIntAsInt(window_obj); if (error_converting(window)) { TYPE_ERR("`window` must be an integer"); return NULL; } /* min_count */ if (min_count_obj == Py_None) { mc = window; } else { mc = PyArray_PyIntAsInt(min_count_obj); if (error_converting(mc)) { TYPE_ERR("`min_count` must be an integer or None"); return NULL; } if (mc > window) { PyErr_Format(PyExc_ValueError, "min_count (%d) cannot be greater than window (%d)", mc, window); return NULL; } else if (mc <= 0) { VALUE_ERR("`min_count` must be greater than zero."); return NULL; } } ndim = PyArray_NDIM(a); /* defend against 0d beings */ if (ndim == 0) { VALUE_ERR("moving window functions require ndim > 0"); return NULL; } /* defend against the axis of negativity */ if (axis_obj == NULL) { axis = ndim - 1; } else { axis = PyArray_PyIntAsInt(axis_obj); if (error_converting(axis)) { TYPE_ERR("`axis` must be an integer"); return NULL; } if (axis < 0) { axis += ndim; if (axis < 0) { PyErr_Format(PyExc_ValueError, "axis(=%d) out of bounds", axis); return NULL; } } else if (axis >= ndim) { PyErr_Format(PyExc_ValueError, "axis(=%d) out of bounds", axis); return NULL; } } /* ddof */ if (ddof_obj == NULL) { ddof = 0; } else { ddof = PyArray_PyIntAsInt(ddof_obj); if (error_converting(ddof)) { TYPE_ERR("`ddof` must be an integer"); return NULL; } } length = PyArray_DIM(a, axis); if ((window < 1) || (window > length)) { PyErr_Format(PyExc_ValueError, "Moving window (=%d) must between 1 and %zu, inclusive", window, length); return NULL; } dtype = PyArray_TYPE(a); if (dtype == NPY_float64) { return move_float64(a, window, mc, axis, ddof); } else if (dtype == NPY_float32) { return move_float32(a, window, mc, axis, ddof); } else if (dtype == NPY_int64) { return move_int64(a, window, mc, axis, ddof); } else if (dtype == NPY_int32) { return move_int32(a, window, mc, axis, ddof); } else { return slow(name, args, kwds); } } /* docstrings ------------------------------------------------------------- */ static char move_doc[] = "Bottleneck moving window functions."; static char move_sum_doc[] = /* MULTILINE STRING BEGIN move_sum(a, window, min_count=None, axis=-1) Moving window sum along the specified axis, optionally ignoring NaNs. This function cannot handle input arrays that contain Inf. When the window contains Inf, the output will correctly be Inf. However, when Inf moves out of the window, the remaining output values in the slice will incorrectly be NaN. Parameters ---------- a : ndarray Input array. If `a` is not an array, a conversion is attempted. window : int The number of elements in the moving window. min_count: {int, None}, optional If the number of non-NaN values in a window is less than `min_count`, then a value of NaN is assigned to the window. By default `min_count` is None, which is equivalent to setting `min_count` equal to `window`. axis : int, optional The axis over which the window is moved. By default the last axis (axis=-1) is used. An axis of None is not allowed. Returns ------- y : ndarray The moving sum of the input array along the specified axis. The output has the same shape as the input. Examples -------- >>> a = np.array([1.0, 2.0, 3.0, np.nan, 5.0]) >>> bn.move_sum(a, window=2) array([ nan, 3., 5., nan, nan]) >>> bn.move_sum(a, window=2, min_count=1) array([ 1., 3., 5., 3., 5.]) MULTILINE STRING END */ static char move_mean_doc[] = /* MULTILINE STRING BEGIN move_mean(a, window, min_count=None, axis=-1) Moving window mean along the specified axis, optionally ignoring NaNs. This function cannot handle input arrays that contain Inf. When the window contains Inf, the output will correctly be Inf. However, when Inf moves out of the window, the remaining output values in the slice will incorrectly be NaN. Parameters ---------- a : ndarray Input array. If `a` is not an array, a conversion is attempted. window : int The number of elements in the moving window. min_count: {int, None}, optional If the number of non-NaN values in a window is less than `min_count`, then a value of NaN is assigned to the window. By default `min_count` is None, which is equivalent to setting `min_count` equal to `window`. axis : int, optional The axis over which the window is moved. By default the last axis (axis=-1) is used. An axis of None is not allowed. Returns ------- y : ndarray The moving mean of the input array along the specified axis. The output has the same shape as the input. Examples -------- >>> a = np.array([1.0, 2.0, 3.0, np.nan, 5.0]) >>> bn.move_mean(a, window=2) array([ nan, 1.5, 2.5, nan, nan]) >>> bn.move_mean(a, window=2, min_count=1) array([ 1. , 1.5, 2.5, 3. , 5. ]) MULTILINE STRING END */ static char move_std_doc[] = /* MULTILINE STRING BEGIN move_std(a, window, min_count=None, axis=-1, ddof=0) Moving window standard deviation along the specified axis, optionally ignoring NaNs. This function cannot handle input arrays that contain Inf. When Inf enters the moving window, the outout becomes NaN and will continue to be NaN for the remainer of the slice. Unlike bn.nanstd, which uses a two-pass algorithm, move_nanstd uses a one-pass algorithm called Welford's method. The algorithm is slow but numerically stable for cases where the mean is large compared to the standard deviation. Parameters ---------- a : ndarray Input array. If `a` is not an array, a conversion is attempted. window : int The number of elements in the moving window. min_count: {int, None}, optional If the number of non-NaN values in a window is less than `min_count`, then a value of NaN is assigned to the window. By default `min_count` is None, which is equivalent to setting `min_count` equal to `window`. axis : int, optional The axis over which the window is moved. By default the last axis (axis=-1) is used. An axis of None is not allowed. ddof : int, optional Means Delta Degrees of Freedom. The divisor used in calculations is ``N - ddof``, where ``N`` represents the number of elements. By default `ddof` is zero. Returns ------- y : ndarray The moving standard deviation of the input array along the specified axis. The output has the same shape as the input. Examples -------- >>> a = np.array([1.0, 2.0, 3.0, np.nan, 5.0]) >>> bn.move_std(a, window=2) array([ nan, 0.5, 0.5, nan, nan]) >>> bn.move_std(a, window=2, min_count=1) array([ 0. , 0.5, 0.5, 0. , 0. ]) MULTILINE STRING END */ static char move_var_doc[] = /* MULTILINE STRING BEGIN move_var(a, window, min_count=None, axis=-1, ddof=0) Moving window variance along the specified axis, optionally ignoring NaNs. This function cannot handle input arrays that contain Inf. When Inf enters the moving window, the outout becomes NaN and will continue to be NaN for the remainer of the slice. Unlike bn.nanvar, which uses a two-pass algorithm, move_nanvar uses a one-pass algorithm called Welford's method. The algorithm is slow but numerically stable for cases where the mean is large compared to the standard deviation. Parameters ---------- a : ndarray Input array. If `a` is not an array, a conversion is attempted. window : int The number of elements in the moving window. min_count: {int, None}, optional If the number of non-NaN values in a window is less than `min_count`, then a value of NaN is assigned to the window. By default `min_count` is None, which is equivalent to setting `min_count` equal to `window`. axis : int, optional The axis over which the window is moved. By default the last axis (axis=-1) is used. An axis of None is not allowed. ddof : int, optional Means Delta Degrees of Freedom. The divisor used in calculations is ``N - ddof``, where ``N`` represents the number of elements. By default `ddof` is zero. Returns ------- y : ndarray The moving variance of the input array along the specified axis. The output has the same shape as the input. Examples -------- >>> a = np.array([1.0, 2.0, 3.0, np.nan, 5.0]) >>> bn.move_var(a, window=2) array([ nan, 0.25, 0.25, nan, nan]) >>> bn.move_var(a, window=2, min_count=1) array([ 0. , 0.25, 0.25, 0. , 0. ]) MULTILINE STRING END */ static char move_min_doc[] = /* MULTILINE STRING BEGIN move_min(a, window, min_count=None, axis=-1) Moving window minimum along the specified axis, optionally ignoring NaNs. float64 output is returned for all input data types. Parameters ---------- a : ndarray Input array. If `a` is not an array, a conversion is attempted. window : int The number of elements in the moving window. min_count: {int, None}, optional If the number of non-NaN values in a window is less than `min_count`, then a value of NaN is assigned to the window. By default `min_count` is None, which is equivalent to setting `min_count` equal to `window`. axis : int, optional The axis over which the window is moved. By default the last axis (axis=-1) is used. An axis of None is not allowed. Returns ------- y : ndarray The moving minimum of the input array along the specified axis. The output has the same shape as the input. The dtype of the output is always float64. Examples -------- >>> a = np.array([1.0, 2.0, 3.0, np.nan, 5.0]) >>> bn.move_min(a, window=2) array([ nan, 1., 2., nan, nan]) >>> bn.move_min(a, window=2, min_count=1) array([ 1., 1., 2., 3., 5.]) MULTILINE STRING END */ static char move_max_doc[] = /* MULTILINE STRING BEGIN move_max(a, window, min_count=None, axis=-1) Moving window maximum along the specified axis, optionally ignoring NaNs. float64 output is returned for all input data types. Parameters ---------- a : ndarray Input array. If `a` is not an array, a conversion is attempted. window : int The number of elements in the moving window. min_count: {int, None}, optional If the number of non-NaN values in a window is less than `min_count`, then a value of NaN is assigned to the window. By default `min_count` is None, which is equivalent to setting `min_count` equal to `window`. axis : int, optional The axis over which the window is moved. By default the last axis (axis=-1) is used. An axis of None is not allowed. Returns ------- y : ndarray The moving maximum of the input array along the specified axis. The output has the same shape as the input. The dtype of the output is always float64. Examples -------- >>> a = np.array([1.0, 2.0, 3.0, np.nan, 5.0]) >>> bn.move_max(a, window=2) array([ nan, 2., 3., nan, nan]) >>> bn.move_max(a, window=2, min_count=1) array([ 1., 2., 3., 3., 5.]) MULTILINE STRING END */ static char move_argmin_doc[] = /* MULTILINE STRING BEGIN move_argmin(a, window, min_count=None, axis=-1) Moving window index of minimum along the specified axis, optionally ignoring NaNs. Index 0 is at the rightmost edge of the window. For example, if the array is monotonically decreasing (increasing) along the specified axis then the output array will contain zeros (window-1). If there is a tie in input values within a window, then the rightmost index is returned. float64 output is returned for all input data types. Parameters ---------- a : ndarray Input array. If `a` is not an array, a conversion is attempted. window : int The number of elements in the moving window. min_count: {int, None}, optional If the number of non-NaN values in a window is less than `min_count`, then a value of NaN is assigned to the window. By default `min_count` is None, which is equivalent to setting `min_count` equal to `window`. axis : int, optional The axis over which the window is moved. By default the last axis (axis=-1) is used. An axis of None is not allowed. Returns ------- y : ndarray The moving index of minimum values of the input array along the specified axis. The output has the same shape as the input. The dtype of the output is always float64. Examples -------- >>> a = np.array([1.0, 2.0, 3.0, 4.0, 5.0]) >>> bn.move_argmin(a, window=2) array([ nan, 1., 1., 1., 1.]) >>> a = np.array([5.0, 4.0, 3.0, 2.0, 1.0]) >>> bn.move_argmin(a, window=2) array([ nan, 0., 0., 0., 0.]) >>> a = np.array([2.0, 3.0, 4.0, 1.0, 7.0, 5.0, 6.0]) >>> bn.move_argmin(a, window=3) array([ nan, nan, 2., 0., 1., 2., 1.]) MULTILINE STRING END */ static char move_argmax_doc[] = /* MULTILINE STRING BEGIN move_argmax(a, window, min_count=None, axis=-1) Moving window index of maximum along the specified axis, optionally ignoring NaNs. Index 0 is at the rightmost edge of the window. For example, if the array is monotonically increasing (decreasing) along the specified axis then the output array will contain zeros (window-1). If there is a tie in input values within a window, then the rightmost index is returned. float64 output is returned for all input data types. Parameters ---------- a : ndarray Input array. If `a` is not an array, a conversion is attempted. window : int The number of elements in the moving window. min_count: {int, None}, optional If the number of non-NaN values in a window is less than `min_count`, then a value of NaN is assigned to the window. By default `min_count` is None, which is equivalent to setting `min_count` equal to `window`. axis : int, optional The axis over which the window is moved. By default the last axis (axis=-1) is used. An axis of None is not allowed. Returns ------- y : ndarray The moving index of maximum values of the input array along the specified axis. The output has the same shape as the input. The dtype of the output is always float64. Examples -------- >>> a = np.array([1.0, 2.0, 3.0, 4.0, 5.0]) >>> bn.move_argmax(a, window=2) array([ nan, 0., 0., 0., 0.]) >>> a = np.array([5.0, 4.0, 3.0, 2.0, 1.0]) >>> bn.move_argmax(a, window=2) array([ nan, 1., 1., 1., 1.]) >>> a = np.array([2.0, 3.0, 4.0, 1.0, 7.0, 5.0, 6.0]) >>> bn.move_argmax(a, window=3) array([ nan, nan, 0., 1., 0., 1., 2.]) MULTILINE STRING END */ static char move_median_doc[] = /* MULTILINE STRING BEGIN move_median(a, window, min_count=None, axis=-1) Moving window median along the specified axis, optionally ignoring NaNs. float64 output is returned for all input data types. Parameters ---------- a : ndarray Input array. If `a` is not an array, a conversion is attempted. window : int The number of elements in the moving window. min_count: {int, None}, optional If the number of non-NaN values in a window is less than `min_count`, then a value of NaN is assigned to the window. By default `min_count` is None, which is equivalent to setting `min_count` equal to `window`. axis : int, optional The axis over which the window is moved. By default the last axis (axis=-1) is used. An axis of None is not allowed. Returns ------- y : ndarray The moving median of the input array along the specified axis. The output has the same shape as the input. Examples -------- >>> a = np.array([1.0, 2.0, 3.0, 4.0]) >>> bn.move_median(a, window=2) array([ nan, 1.5, 2.5, 3.5]) >>> bn.move_median(a, window=2, min_count=1) array([ 1. , 1.5, 2.5, 3.5]) MULTILINE STRING END */ static char move_rank_doc[] = /* MULTILINE STRING BEGIN move_rank(a, window, min_count=None, axis=-1) Moving window ranking along the specified axis, optionally ignoring NaNs. The output is normalized to be between -1 and 1. For example, with a window width of 3 (and with no ties), the possible output values are -1, 0, 1. Ties are broken by averaging the rankings. See the examples below. The runtime depends almost linearly on `window`. The more NaNs there are in the input array, the shorter the runtime. Parameters ---------- a : ndarray Input array. If `a` is not an array, a conversion is attempted. window : int The number of elements in the moving window. min_count: {int, None}, optional If the number of non-NaN values in a window is less than `min_count`, then a value of NaN is assigned to the window. By default `min_count` is None, which is equivalent to setting `min_count` equal to `window`. axis : int, optional The axis over which the window is moved. By default the last axis (axis=-1) is used. An axis of None is not allowed. Returns ------- y : ndarray The moving ranking along the specified axis. The output has the same shape as the input. For integer input arrays, the dtype of the output is float64. Examples -------- With window=3 and no ties, there are 3 possible output values, i.e. [-1., 0., 1.]: >>> a = np.array([1, 2, 3, 9, 8, 7, 5, 6, 4]) >>> bn.move_rank(a, window=3) array([ nan, nan, 1., 1., 0., -1., -1., 0., -1.]) Ties are broken by averaging the rankings of the tied elements: >>> a = np.array([1, 2, 3, 3, 3, 4]) >>> bn.move_rank(a, window=3) array([ nan, nan, 1. , 0.5, 0. , 1. ]) In an increasing sequence, the moving window ranking is always equal to 1: >>> a = np.array([1, 2, 3, 4, 5]) >>> bn.move_rank(a, window=2) array([ nan, 1., 1., 1., 1.]) MULTILINE STRING END */ /* python wrapper -------------------------------------------------------- */ static PyMethodDef move_methods[] = { {"move_sum", (PyCFunction)move_sum, VARKEY, move_sum_doc}, {"move_mean", (PyCFunction)move_mean, VARKEY, move_mean_doc}, {"move_std", (PyCFunction)move_std, VARKEY, move_std_doc}, {"move_var", (PyCFunction)move_var, VARKEY, move_var_doc}, {"move_min", (PyCFunction)move_min, VARKEY, move_min_doc}, {"move_max", (PyCFunction)move_max, VARKEY, move_max_doc}, {"move_argmin", (PyCFunction)move_argmin, VARKEY, move_argmin_doc}, {"move_argmax", (PyCFunction)move_argmax, VARKEY, move_argmax_doc}, {"move_median", (PyCFunction)move_median, VARKEY, move_median_doc}, {"move_rank", (PyCFunction)move_rank, VARKEY, move_rank_doc}, {NULL, NULL, 0, NULL} }; #if PY_MAJOR_VERSION >= 3 static struct PyModuleDef move_def = { PyModuleDef_HEAD_INIT, "move", move_doc, -1, move_methods }; #endif PyMODINIT_FUNC #if PY_MAJOR_VERSION >= 3 #define RETVAL m PyInit_move(void) #else #define RETVAL initmove(void) #endif { #if PY_MAJOR_VERSION >=3 PyObject *m = PyModule_Create(&move_def); #else PyObject *m = Py_InitModule3("move", move_methods, move_doc); #endif if (m == NULL) return RETVAL; import_array(); if (!intern_strings()) { return RETVAL; } return RETVAL; } Bottleneck-1.2.1/bottleneck/src/nonreduce_axis_template.c0000664000175000017500000007154213103166630022631 0ustar kgkg00000000000000#include "bottleneck.h" #include "iterators.h" /* function signatures --------------------------------------------------- */ /* low-level functions such as move_sum_float64 */ #define NRA(name, dtype) \ static PyObject * \ name##_##dtype(PyArrayObject *a, int axis, int n) /* top-level functions such as move_sum */ #define NRA_MAIN(name, parse) \ static PyObject * \ name(PyObject *self, PyObject *args, PyObject *kwds) \ { \ return nonreducer_axis(#name, \ args, \ kwds, \ name##_float64, \ name##_float32, \ name##_int64, \ name##_int32, \ parse); \ } /* typedefs and prototypes ----------------------------------------------- */ /* how should input be parsed? */ typedef enum {PARSE_PARTITION, PARSE_RANKDATA, PARSE_PUSH} parse_type; /* function pointer for functions passed to nonreducer_axis */ typedef PyObject *(*nra_t)(PyArrayObject *, int, int); static PyObject * nonreducer_axis(char *name, PyObject *args, PyObject *kwds, nra_t, nra_t, nra_t, nra_t, parse_type); /* partition ------------------------------------------------------------- */ #define B(dtype, i) AX(dtype, i) /* used by PARTITION */ /* dtype = [['float64'], ['float32'], ['int64'], ['int32']] */ NRA(partition, DTYPE0) { npy_intp i; npy_intp j, l, r, k; iter it; a = (PyArrayObject *)PyArray_NewCopy(a, NPY_ANYORDER); init_iter_one(&it, a, axis); if (LENGTH == 0) return (PyObject *)a; if (n < 0 || n > LENGTH - 1) { PyErr_Format(PyExc_ValueError, "`n` (=%d) must be between 0 and %zd, inclusive.", n, LENGTH - 1); return NULL; } BN_BEGIN_ALLOW_THREADS k = n; WHILE { l = 0; r = LENGTH - 1; PARTITION(DTYPE0) NEXT } BN_END_ALLOW_THREADS return (PyObject *)a; } /* dtype end */ NRA_MAIN(partition, PARSE_PARTITION) /* argpartition ----------------------------------------------------------- */ #define BUFFER_NEW(dtype) dtype *B = malloc(LENGTH * sizeof(dtype)); #define BUFFER_DELETE free(B); #define ARGWIRTH(dtype0, dtype1) \ x = B[k]; \ i = l; \ j = r; \ do { \ while (B[i] < x) i++; \ while (x < B[j]) j--; \ if (i <= j) { \ npy_##dtype0 atmp = B[i]; \ B[i] = B[j]; \ B[j] = atmp; \ ytmp = YX(dtype1, i); \ YX(dtype1, i) = YX(dtype1, j); \ YX(dtype1, j) = ytmp; \ i++; \ j--; \ } \ } while (i <= j); \ if (j < k) l = i; \ if (k < i) r = j; #define ARGPARTITION(dtype0, dtype1) \ while (l < r) { \ npy_##dtype0 x; \ npy_##dtype0 al = B[l]; \ npy_##dtype0 ak = B[k]; \ npy_##dtype0 ar = B[r]; \ npy_##dtype1 ytmp; \ if (al > ak) { \ if (ak < ar) { \ if (al < ar) { \ B[k] = al; \ B[l] = ak; \ ytmp = YX(dtype1, k); \ YX(dtype1, k) = YX(dtype1, l); \ YX(dtype1, l) = ytmp; \ } \ else { \ B[k] = ar; \ B[r] = ak; \ ytmp = YX(dtype1, k); \ YX(dtype1, k) = YX(dtype1, r); \ YX(dtype1, r) = ytmp; \ } \ } \ } \ else { \ if (ak > ar) { \ if (al > ar) { \ B[k] = al; \ B[l] = ak; \ ytmp = YX(dtype1, k); \ YX(dtype1, k) = YX(dtype1, l); \ YX(dtype1, l) = ytmp; \ } \ else { \ B[k] = ar; \ B[r] = ak; \ ytmp = YX(dtype1, k); \ YX(dtype1, k) = YX(dtype1, r); \ YX(dtype1, r) = ytmp; \ } \ } \ } \ ARGWIRTH(dtype0, dtype1) \ } #define ARGPARTSORT(dtype0, dtype1) \ for (i = 0; i < LENGTH; i++) { \ B[i] = AX(dtype0, i); \ YX(dtype1, i) = i; \ } \ l = 0; \ r = LENGTH - 1; \ ARGPARTITION(dtype0, dtype1) /* dtype = [['float64', 'intp'], ['float32', 'intp'], ['int64', 'intp'], ['int32', 'intp']] */ NRA(argpartition, DTYPE0) { npy_intp i; PyObject *y = PyArray_EMPTY(PyArray_NDIM(a), PyArray_SHAPE(a), NPY_DTYPE1, 0); iter2 it; init_iter2(&it, a, y, axis); if (LENGTH == 0) return y; if (n < 0 || n > LENGTH - 1) { PyErr_Format(PyExc_ValueError, "`n` (=%d) must be between 0 and %zd, inclusive.", n, LENGTH - 1); return NULL; } BN_BEGIN_ALLOW_THREADS BUFFER_NEW(npy_DTYPE0) npy_intp j, l, r, k; k = n; WHILE { l = 0; r = LENGTH - 1; ARGPARTSORT(DTYPE0, DTYPE1) NEXT2 } BUFFER_DELETE BN_END_ALLOW_THREADS return y; } /* dtype end */ NRA_MAIN(argpartition, PARSE_PARTITION) /* rankdata -------------------------------------------------------------- */ /* dtype = [['float64', 'float64', 'intp'], ['float32', 'float64', 'intp'], ['int64', 'float64', 'intp'], ['int32', 'float64', 'intp']] */ NRA(rankdata, DTYPE0) { Py_ssize_t j=0, k, idx, dupcount=0, i; npy_DTYPE1 old, new, averank, sumranks = 0; PyObject *z = PyArray_ArgSort(a, axis, NPY_QUICKSORT); PyObject *y = PyArray_EMPTY(PyArray_NDIM(a), PyArray_SHAPE(a), NPY_DTYPE1, 0); iter3 it; init_iter3(&it, a, y, z, axis); BN_BEGIN_ALLOW_THREADS if (LENGTH == 0) { Py_ssize_t size = PyArray_SIZE((PyArrayObject *)y); npy_DTYPE1 *py = (npy_DTYPE1 *)PyArray_DATA(a); for (i = 0; i < size; i++) YPP = BN_NAN; } else { WHILE { idx = ZX(DTYPE2, 0); old = AX(DTYPE0, idx); sumranks = 0; dupcount = 0; for (i = 0; i < LENGTH - 1; i++) { sumranks += i; dupcount++; k = i + 1; idx = ZX(DTYPE2, k); new = AX(DTYPE0, idx); if (old != new) { averank = sumranks / dupcount + 1; for (j = k - dupcount; j < k; j++) { idx = ZX(DTYPE2, j); YX(DTYPE1, idx) = averank; } sumranks = 0; dupcount = 0; } old = new; } sumranks += (LENGTH - 1); dupcount++; averank = sumranks / dupcount + 1; for (j = LENGTH - dupcount; j < LENGTH; j++) { idx = ZX(DTYPE2, j); YX(DTYPE1, idx) = averank; } NEXT3 } } BN_END_ALLOW_THREADS Py_DECREF(z); return y; } /* dtype end */ NRA_MAIN(rankdata, PARSE_RANKDATA) /* nanrankdata ----------------------------------------------------------- */ /* dtype = [['float64', 'float64', 'intp'], ['float32', 'float64', 'intp']] */ NRA(nanrankdata, DTYPE0) { Py_ssize_t j=0, k, idx, dupcount=0, i; npy_DTYPE1 old, new, averank, sumranks = 0; PyObject *z = PyArray_ArgSort(a, axis, NPY_QUICKSORT); PyObject *y = PyArray_EMPTY(PyArray_NDIM(a), PyArray_SHAPE(a), NPY_DTYPE1, 0); iter3 it; init_iter3(&it, a, y, z, axis); BN_BEGIN_ALLOW_THREADS if (LENGTH == 0) { Py_ssize_t size = PyArray_SIZE((PyArrayObject *)y); npy_DTYPE1 *py = (npy_DTYPE1 *)PyArray_DATA(a); for (i = 0; i < size; i++) YPP = BN_NAN; } else { WHILE { idx = ZX(DTYPE2, 0); old = AX(DTYPE0, idx); sumranks = 0; dupcount = 0; for (i = 0; i < LENGTH - 1; i++) { sumranks += i; dupcount++; k = i + 1; idx = ZX(DTYPE2, k); new = AX(DTYPE0, idx); if (old != new) { if (old == old) { averank = sumranks / dupcount + 1; for (j = k - dupcount; j < k; j++) { idx = ZX(DTYPE2, j); YX(DTYPE1, idx) = averank; } } else { idx = ZX(DTYPE2, i); YX(DTYPE1, idx) = BN_NAN; } sumranks = 0; dupcount = 0; } old = new; } sumranks += (LENGTH - 1); dupcount++; averank = sumranks / dupcount + 1; if (old == old) { for (j = LENGTH - dupcount; j < LENGTH; j++) { idx = ZX(DTYPE2, j); YX(DTYPE1, idx) = averank; } } else { idx = ZX(DTYPE2, LENGTH - 1); YX(DTYPE1, idx) = BN_NAN; } NEXT3 } } BN_END_ALLOW_THREADS Py_DECREF(z); return y; } /* dtype end */ static PyObject * nanrankdata(PyObject *self, PyObject *args, PyObject *kwds) { return nonreducer_axis("nanrankdata", args, kwds, nanrankdata_float64, nanrankdata_float32, rankdata_int64, rankdata_int32, PARSE_RANKDATA); } /* push ------------------------------------------------------------------ */ /* dtype = [['float64'], ['float32']] */ NRA(push, DTYPE0) { npy_intp index; npy_DTYPE0 ai, ai_last, n_float; PyObject *y = PyArray_Copy(a); iter it; init_iter_one(&it, (PyArrayObject *)y, axis); if (LENGTH == 0 || NDIM == 0) { return y; } n_float = n < 0 ? BN_INFINITY : (npy_DTYPE0)n; BN_BEGIN_ALLOW_THREADS WHILE { index = 0; ai_last = BN_NAN; FOR { ai = AI(DTYPE0); if (ai == ai) { ai_last = ai; index = INDEX; } else { if (INDEX - index <= n_float) { AI(DTYPE0) = ai_last; } } } NEXT } BN_END_ALLOW_THREADS return y; } /* dtype end */ /* dtype = [['int64'], ['int32']] */ NRA(push, DTYPE0) { PyObject *y = PyArray_Copy(a); return y; } /* dtype end */ NRA_MAIN(push, PARSE_PUSH) /* python strings -------------------------------------------------------- */ PyObject *pystr_a = NULL; PyObject *pystr_n = NULL; PyObject *pystr_kth = NULL; PyObject *pystr_axis = NULL; static int intern_strings(void) { pystr_a = PyString_InternFromString("a"); pystr_n = PyString_InternFromString("n"); pystr_kth = PyString_InternFromString("kth"); pystr_axis = PyString_InternFromString("axis"); return pystr_a && pystr_n && pystr_axis; } /* nonreducer_axis ------------------------------------------------------- */ static BN_INLINE int parse_partition(PyObject *args, PyObject *kwds, PyObject **a, PyObject **n, PyObject **axis) { const Py_ssize_t nargs = PyTuple_GET_SIZE(args); const Py_ssize_t nkwds = kwds == NULL ? 0 : PyDict_Size(kwds); if (nkwds) { int nkwds_found = 0; PyObject *tmp; switch (nargs) { case 2: *n = PyTuple_GET_ITEM(args, 1); case 1: *a = PyTuple_GET_ITEM(args, 0); case 0: break; default: TYPE_ERR("wrong number of arguments"); return 0; } switch (nargs) { case 0: *a = PyDict_GetItem(kwds, pystr_a); if (*a == NULL) { TYPE_ERR("Cannot find `a` keyword input"); return 0; } nkwds_found += 1; case 1: *n = PyDict_GetItem(kwds, pystr_kth); if (*n == NULL) { TYPE_ERR("Cannot find `kth` keyword input"); return 0; } nkwds_found++; case 2: tmp = PyDict_GetItem(kwds, pystr_axis); if (tmp != NULL) { *axis = tmp; nkwds_found++; } break; default: TYPE_ERR("wrong number of arguments"); return 0; } if (nkwds_found != nkwds) { TYPE_ERR("wrong number of keyword arguments"); return 0; } if (nargs + nkwds_found > 3) { TYPE_ERR("too many arguments"); return 0; } } else { switch (nargs) { case 3: *axis = PyTuple_GET_ITEM(args, 2); case 2: *n = PyTuple_GET_ITEM(args, 1); *a = PyTuple_GET_ITEM(args, 0); break; default: TYPE_ERR("wrong number of arguments"); return 0; } } return 1; } static BN_INLINE int parse_rankdata(PyObject *args, PyObject *kwds, PyObject **a, PyObject **axis) { const Py_ssize_t nargs = PyTuple_GET_SIZE(args); const Py_ssize_t nkwds = kwds == NULL ? 0 : PyDict_Size(kwds); if (nkwds) { int nkwds_found = 0; PyObject *tmp; switch (nargs) { case 1: *a = PyTuple_GET_ITEM(args, 0); case 0: break; default: TYPE_ERR("wrong number of arguments"); return 0; } switch (nargs) { case 0: *a = PyDict_GetItem(kwds, pystr_a); if (*a == NULL) { TYPE_ERR("Cannot find `a` keyword input"); return 0; } nkwds_found += 1; case 1: tmp = PyDict_GetItem(kwds, pystr_axis); if (tmp != NULL) { *axis = tmp; nkwds_found++; } break; default: TYPE_ERR("wrong number of arguments"); return 0; } if (nkwds_found != nkwds) { TYPE_ERR("wrong number of keyword arguments"); return 0; } if (nargs + nkwds_found > 2) { TYPE_ERR("too many arguments"); return 0; } } else { switch (nargs) { case 2: *axis = PyTuple_GET_ITEM(args, 1); case 1: *a = PyTuple_GET_ITEM(args, 0); break; default: TYPE_ERR("wrong number of arguments"); return 0; } } return 1; } static BN_INLINE int parse_push(PyObject *args, PyObject *kwds, PyObject **a, PyObject **n, PyObject **axis) { const Py_ssize_t nargs = PyTuple_GET_SIZE(args); const Py_ssize_t nkwds = kwds == NULL ? 0 : PyDict_Size(kwds); if (nkwds) { int nkwds_found = 0; PyObject *tmp; switch (nargs) { case 2: *n = PyTuple_GET_ITEM(args, 1); case 1: *a = PyTuple_GET_ITEM(args, 0); case 0: break; default: TYPE_ERR("wrong number of arguments"); return 0; } switch (nargs) { case 0: *a = PyDict_GetItem(kwds, pystr_a); if (*a == NULL) { TYPE_ERR("Cannot find `a` keyword input"); return 0; } nkwds_found += 1; case 1: tmp = PyDict_GetItem(kwds, pystr_n); if (tmp != NULL) { *n = tmp; nkwds_found++; } case 2: tmp = PyDict_GetItem(kwds, pystr_axis); if (tmp != NULL) { *axis = tmp; nkwds_found++; } break; default: TYPE_ERR("wrong number of arguments"); return 0; } if (nkwds_found != nkwds) { TYPE_ERR("wrong number of keyword arguments"); return 0; } if (nargs + nkwds_found > 3) { TYPE_ERR("too many arguments"); return 0; } } else { switch (nargs) { case 3: *axis = PyTuple_GET_ITEM(args, 2); case 2: *n = PyTuple_GET_ITEM(args, 1); case 1: *a = PyTuple_GET_ITEM(args, 0); break; default: TYPE_ERR("wrong number of arguments"); return 0; } } return 1; } static PyObject * nonreducer_axis(char *name, PyObject *args, PyObject *kwds, nra_t nra_float64, nra_t nra_float32, nra_t nra_int64, nra_t nra_int32, parse_type parse) { int n; int axis; int dtype; PyArrayObject *a; PyObject *a_obj = NULL; PyObject *n_obj = NULL; PyObject *axis_obj = NULL; if (parse == PARSE_PARTITION) { if (!parse_partition(args, kwds, &a_obj, &n_obj, &axis_obj)) { return NULL; } } else if (parse == PARSE_RANKDATA) { if (!parse_rankdata(args, kwds, &a_obj, &axis_obj)) { return NULL; } } else if (parse == PARSE_PUSH) { if (!parse_push(args, kwds, &a_obj, &n_obj, &axis_obj)) { return NULL; } } else { RUNTIME_ERR("Unknown parse type; please report error."); } /* convert to array if necessary */ if PyArray_Check(a_obj) { a = (PyArrayObject *)a_obj; } else { a = (PyArrayObject *)PyArray_FROM_O(a_obj); if (a == NULL) { return NULL; } } /* check for byte swapped input array */ if PyArray_ISBYTESWAPPED(a) { return slow(name, args, kwds); } /* defend against the axis of negativity */ if (axis_obj == NULL) { if (parse == PARSE_PARTITION || parse == PARSE_PUSH) { axis = PyArray_NDIM(a) - 1; if (axis < 0) { PyErr_Format(PyExc_ValueError, "axis(=%d) out of bounds", axis); return NULL; } } else { if (PyArray_NDIM(a) != 1) { a = (PyArrayObject *)PyArray_Ravel(a, NPY_CORDER); } axis = 0; } } else if (axis_obj == Py_None) { if (parse == PARSE_PUSH) { VALUE_ERR("`axis` cannot be None"); return NULL; } if (PyArray_NDIM(a) != 1) { a = (PyArrayObject *)PyArray_Ravel(a, NPY_CORDER); } axis = 0; } else { axis = PyArray_PyIntAsInt(axis_obj); if (error_converting(axis)) { TYPE_ERR("`axis` must be an integer"); return NULL; } if (axis < 0) { axis += PyArray_NDIM(a); if (axis < 0) { PyErr_Format(PyExc_ValueError, "axis(=%d) out of bounds", axis); return NULL; } } else if (axis >= PyArray_NDIM(a)) { PyErr_Format(PyExc_ValueError, "axis(=%d) out of bounds", axis); return NULL; } } /* ddof */ if (n_obj == NULL) { n = -1; } else { n = PyArray_PyIntAsInt(n_obj); if (error_converting(n)) { TYPE_ERR("`n` must be an integer"); return NULL; } if (n < 0 && parse == PARSE_PUSH) { VALUE_ERR("`n` must be nonnegative"); return NULL; } } dtype = PyArray_TYPE(a); if (dtype == NPY_float64) return nra_float64(a, axis, n); else if (dtype == NPY_float32) return nra_float32(a, axis, n); else if (dtype == NPY_int64) return nra_int64(a, axis, n); else if (dtype == NPY_int32) return nra_int32(a, axis, n); else return slow(name, args, kwds); } /* docstrings ------------------------------------------------------------- */ static char nra_doc[] = "Bottleneck non-reducing functions that operate along an axis."; static char partition_doc[] = /* MULTILINE STRING BEGIN partition(a, kth, axis=-1) Partition array elements along given axis. A 1d array B is partitioned at array index `kth` if three conditions are met: (1) B[kth] is in its sorted position, (2) all elements to the left of `kth` are less than or equal to B[kth], and (3) all elements to the right of `kth` are greater than or equal to B[kth]. Note that the array elements in conditions (2) and (3) are in general unordered. Shuffling the input array may change the output. The only guarantee is given by the three conditions above. This functions is not protected against NaN. Therefore, you may get unexpected results if the input contains NaN. Parameters ---------- a : array_like Input array. If `a` is not an array, a conversion is attempted. kth : int The value of the element at index `kth` will be in its sorted position. Smaller (larger) or equal values will be to the left (right) of index `kth`. axis : {int, None}, optional Axis along which the partition is performed. The default (axis=-1) is to partition along the last axis. Returns ------- y : ndarray A partitioned copy of the input array with the same shape and type of `a`. See Also -------- bottleneck.argpartition: Indices that would partition an array Notes ----- Unexpected results may occur if the input array contains NaN. Examples -------- Create a numpy array: >>> a = np.array([1, 0, 3, 4, 2]) Partition array so that the first 3 elements (indices 0, 1, 2) are the smallest 3 elements (note, as in this example, that the smallest 3 elements may not be sorted): >>> bn.partition(a, kth=2) array([1, 0, 2, 4, 3]) Now Partition array so that the last 2 elements are the largest 2 elements: >>> bn.partition(a, kth=3) array([1, 0, 2, 3, 4]) MULTILINE STRING END */ static char argpartition_doc[] = /* MULTILINE STRING BEGIN argpartition(a, kth, axis=-1) Return indices that would partition array along the given axis. A 1d array B is partitioned at array index `kth` if three conditions are met: (1) B[kth] is in its sorted position, (2) all elements to the left of `kth` are less than or equal to B[kth], and (3) all elements to the right of `kth` are greater than or equal to B[kth]. Note that the array elements in conditions (2) and (3) are in general unordered. Shuffling the input array may change the output. The only guarantee is given by the three conditions above. This functions is not protected against NaN. Therefore, you may get unexpected results if the input contains NaN. Parameters ---------- a : array_like Input array. If `a` is not an array, a conversion is attempted. kth : int The value of the element at index `kth` will be in its sorted position. Smaller (larger) or equal values will be to the left (right) of index `kth`. axis : {int, None}, optional Axis along which the partition is performed. The default (axis=-1) is to partition along the last axis. Returns ------- y : ndarray An array the same shape as the input array containing the indices that partition `a`. The dtype of the indices is numpy.intp. See Also -------- bottleneck.partition: Partition array elements along given axis. Notes ----- Unexpected results may occur if the input array contains NaN. Examples -------- Create a numpy array: >>> a = np.array([10, 0, 30, 40, 20]) Find the indices that partition the array so that the first 3 elements are the smallest 3 elements: >>> index = bn.argpartition(a, kth=2) >>> index array([0, 1, 4, 3, 2]) Let's use the indices to partition the array (note, as in this example, that the smallest 3 elements may not be in order): >>> a[index] array([10, 0, 20, 40, 30]) MULTILINE STRING END */ static char rankdata_doc[] = /* MULTILINE STRING BEGIN rankdata(a, axis=None) Ranks the data, dealing with ties appropriately. Equal values are assigned a rank that is the average of the ranks that would have been otherwise assigned to all of the values within that set. Ranks begin at 1, not 0. Parameters ---------- a : array_like Input array. If `a` is not an array, a conversion is attempted. axis : {int, None}, optional Axis along which the elements of the array are ranked. The default (axis=None) is to rank the elements of the flattened array. Returns ------- y : ndarray An array with the same shape as `a`. The dtype is 'float64'. See also -------- bottleneck.nanrankdata: Ranks the data dealing with ties and NaNs. Examples -------- >>> bn.rankdata([0, 2, 2, 3]) array([ 1. , 2.5, 2.5, 4. ]) >>> bn.rankdata([[0, 2], [2, 3]]) array([ 1. , 2.5, 2.5, 4. ]) >>> bn.rankdata([[0, 2], [2, 3]], axis=0) array([[ 1., 1.], [ 2., 2.]]) >>> bn.rankdata([[0, 2], [2, 3]], axis=1) array([[ 1., 2.], [ 1., 2.]]) MULTILINE STRING END */ static char nanrankdata_doc[] = /* MULTILINE STRING BEGIN nanrankdata(a, axis=None) Ranks the data, dealing with ties and NaNs appropriately. Equal values are assigned a rank that is the average of the ranks that would have been otherwise assigned to all of the values within that set. Ranks begin at 1, not 0. NaNs in the input array are returned as NaNs. Parameters ---------- a : array_like Input array. If `a` is not an array, a conversion is attempted. axis : {int, None}, optional Axis along which the elements of the array are ranked. The default (axis=None) is to rank the elements of the flattened array. Returns ------- y : ndarray An array with the same shape as `a`. The dtype is 'float64'. See also -------- bottleneck.rankdata: Ranks the data, dealing with ties and appropriately. Examples -------- >>> bn.nanrankdata([np.nan, 2, 2, 3]) array([ nan, 1.5, 1.5, 3. ]) >>> bn.nanrankdata([[np.nan, 2], [2, 3]]) array([ nan, 1.5, 1.5, 3. ]) >>> bn.nanrankdata([[np.nan, 2], [2, 3]], axis=0) array([[ nan, 1.], [ 1., 2.]]) >>> bn.nanrankdata([[np.nan, 2], [2, 3]], axis=1) array([[ nan, 1.], [ 1., 2.]]) MULTILINE STRING END */ static char push_doc[] = /* MULTILINE STRING BEGIN push(a, n=None, axis=-1) Fill missing values (NaNs) with most recent non-missing values. Filling proceeds along the specified axis from small index values to large index values. Parameters ---------- a : array_like Input array. If `a` is not an array, a conversion is attempted. n : {int, None}, optional How far to push values. If the most recent non-NaN array element is more than `n` index positions away, than a NaN is returned. The default (n = None) is to push the entire length of the slice. If `n` is an integer it must be nonnegative. axis : int, optional Axis along which the elements of the array are pushed. The default (axis=-1) is to push along the last axis of the input array. Returns ------- y : ndarray An array with the same shape and dtype as `a`. See also -------- bottleneck.replace: Replace specified value of an array with new value. Examples -------- >>> a = np.array([5, np.nan, np.nan, 6, np.nan]) >>> bn.push(a) array([ 5., 5., 5., 6., 6.]) >>> bn.push(a, n=1) array([ 5., 5., nan, 6., 6.]) >>> bn.push(a, n=2) array([ 5., 5., 5., 6., 6.]) MULTILINE STRING END */ /* python wrapper -------------------------------------------------------- */ static PyMethodDef nra_methods[] = { {"partition", (PyCFunction)partition, VARKEY, partition_doc}, {"argpartition", (PyCFunction)argpartition, VARKEY, argpartition_doc}, {"rankdata", (PyCFunction)rankdata, VARKEY, rankdata_doc}, {"nanrankdata", (PyCFunction)nanrankdata, VARKEY, nanrankdata_doc}, {"push", (PyCFunction)push, VARKEY, push_doc}, {NULL, NULL, 0, NULL} }; #if PY_MAJOR_VERSION >= 3 static struct PyModuleDef nra_def = { PyModuleDef_HEAD_INIT, "nonreduce_axis", nra_doc, -1, nra_methods }; #endif PyMODINIT_FUNC #if PY_MAJOR_VERSION >= 3 #define RETVAL m PyInit_nonreduce_axis(void) #else #define RETVAL initnonreduce_axis(void) #endif { #if PY_MAJOR_VERSION >=3 PyObject *m = PyModule_Create(&nra_def); #else PyObject *m = Py_InitModule3("nonreduce_axis", nra_methods, nra_doc); #endif if (m == NULL) return RETVAL; import_array(); if (!intern_strings()) { return RETVAL; } return RETVAL; } Bottleneck-1.2.1/bottleneck/src/__init__.py0000664000175000017500000000000013103166047017654 0ustar kgkg00000000000000Bottleneck-1.2.1/bottleneck/src/iterators.h0000664000175000017500000002462613103166630017752 0ustar kgkg00000000000000#include /* Bottleneck iterators are based on ideas from NumPy's PyArray_IterAllButAxis and PyArray_ITER_NEXT. */ /* one input array ------------------------------------------------------- */ /* these iterators are used mainly by reduce functions such as nansum */ struct _iter { int ndim_m2; /* ndim - 2 */ int axis; /* axis to not iterate over */ Py_ssize_t length; /* a.shape[axis] */ Py_ssize_t astride; /* a.strides[axis] */ npy_intp i; /* integer used by some macros */ npy_intp its; /* number of iterations completed */ npy_intp nits; /* number of iterations iterator plans to make */ npy_intp indices[NPY_MAXDIMS]; /* current location of iterator */ npy_intp astrides[NPY_MAXDIMS]; /* a.strides, a.strides[axis] removed */ npy_intp shape[NPY_MAXDIMS]; /* a.shape, a.shape[axis] removed */ char *pa; /* pointer to data corresponding to indices */ PyArrayObject *a_ravel; /* NULL or pointer to ravelled input array */ }; typedef struct _iter iter; static BN_INLINE void init_iter_one(iter *it, PyArrayObject *a, int axis) { int i, j = 0; const int ndim = PyArray_NDIM(a); const npy_intp *shape = PyArray_SHAPE(a); const npy_intp *strides = PyArray_STRIDES(a); it->axis = axis; it->its = 0; it->nits = 1; it->pa = PyArray_BYTES(a); it->ndim_m2 = -1; it->length = 1; it->astride = 0; if (ndim != 0) { it->ndim_m2 = ndim - 2; for (i = 0; i < ndim; i++) { if (i == axis) { it->astride = strides[i]; it->length = shape[i]; } else { it->indices[j] = 0; it->astrides[j] = strides[i]; it->shape[j] = shape[i]; it->nits *= shape[i]; j++; } } } } /* * If both ravel != 0 and it.a_ravel != NULL then you are responsible for * calling Py_DECREF(it.a_ravel) after you are done with the iterator. * See nanargmin for an example. */ static BN_INLINE void init_iter_all(iter *it, PyArrayObject *a, int ravel, int anyorder) { int i, j = 0; const int ndim = PyArray_NDIM(a); const npy_intp *shape = PyArray_SHAPE(a); const npy_intp *strides = PyArray_STRIDES(a); it->axis = 0; it->its = 0; it->nits = 1; it->a_ravel = NULL; if (ndim == 1) { it->ndim_m2 = -1; it->length = shape[0]; it->astride = strides[0]; } else if (ndim == 0) { it->ndim_m2 = -1; it->length = 1; it->astride = 0; } /* The &&! in the next two else ifs is to deal with relaxed * stride checking introduced in numpy 1.12.0; see gh #161 */ else if (C_CONTIGUOUS(a) && !F_CONTIGUOUS(a)) { it->ndim_m2 = -1; it->axis = ndim - 1; it->length = PyArray_SIZE(a); it->astride = strides[ndim - 1]; } else if (F_CONTIGUOUS(a) && !C_CONTIGUOUS(a)) { if (anyorder || !ravel) { it->ndim_m2 = -1; it->length = PyArray_SIZE(a); it->astride = strides[0]; } else { it->ndim_m2 = -1; if (anyorder) { a = (PyArrayObject *)PyArray_Ravel(a, NPY_ANYORDER); } else { a = (PyArrayObject *)PyArray_Ravel(a, NPY_CORDER); } it->a_ravel = a; it->length = PyArray_DIM(a, 0); it->astride = PyArray_STRIDE(a, 0); } } else if (ravel) { it->ndim_m2 = -1; if (anyorder) { a = (PyArrayObject *)PyArray_Ravel(a, NPY_ANYORDER); } else { a = (PyArrayObject *)PyArray_Ravel(a, NPY_CORDER); } it->a_ravel = a; it->length = PyArray_DIM(a, 0); it->astride = PyArray_STRIDE(a, 0); } else { it->ndim_m2 = ndim - 2; it->astride = strides[0]; for (i = 1; i < ndim; i++) { if (strides[i] < it->astride) { it->astride = strides[i]; it->axis = i; } } it->length = shape[it->axis]; for (i = 0; i < ndim; i++) { if (i != it->axis) { it->indices[j] = 0; it->astrides[j] = strides[i]; it->shape[j] = shape[i]; it->nits *= shape[i]; j++; } } } it->pa = PyArray_BYTES(a); } #define NEXT \ for (it.i = it.ndim_m2; it.i > -1; it.i--) { \ if (it.indices[it.i] < it.shape[it.i] - 1) { \ it.pa += it.astrides[it.i]; \ it.indices[it.i]++; \ break; \ } \ it.pa -= it.indices[it.i] * it.astrides[it.i]; \ it.indices[it.i] = 0; \ } \ it.its++; /* two input arrays ------------------------------------------------------ */ /* this iterator is used mainly by moving window functions such as move_sum */ struct _iter2 { int ndim_m2; int axis; Py_ssize_t length; Py_ssize_t astride; Py_ssize_t ystride; npy_intp i; npy_intp its; npy_intp nits; npy_intp indices[NPY_MAXDIMS]; npy_intp astrides[NPY_MAXDIMS]; npy_intp ystrides[NPY_MAXDIMS]; npy_intp shape[NPY_MAXDIMS]; char *pa; char *py; }; typedef struct _iter2 iter2; static BN_INLINE void init_iter2(iter2 *it, PyArrayObject *a, PyObject *y, int axis) { int i, j = 0; const int ndim = PyArray_NDIM(a); const npy_intp *shape = PyArray_SHAPE(a); const npy_intp *astrides = PyArray_STRIDES(a); const npy_intp *ystrides = PyArray_STRIDES((PyArrayObject *)y); /* to avoid compiler warning of uninitialized variables */ it->length = 0; it->astride = 0; it->ystride = 0; it->ndim_m2 = ndim - 2; it->axis = axis; it->its = 0; it->nits = 1; it->pa = PyArray_BYTES(a); it->py = PyArray_BYTES((PyArrayObject *)y); for (i = 0; i < ndim; i++) { if (i == axis) { it->astride = astrides[i]; it->ystride = ystrides[i]; it->length = shape[i]; } else { it->indices[j] = 0; it->astrides[j] = astrides[i]; it->ystrides[j] = ystrides[i]; it->shape[j] = shape[i]; it->nits *= shape[i]; j++; } } } #define NEXT2 \ for (it.i = it.ndim_m2; it.i > -1; it.i--) { \ if (it.indices[it.i] < it.shape[it.i] - 1) { \ it.pa += it.astrides[it.i]; \ it.py += it.ystrides[it.i]; \ it.indices[it.i]++; \ break; \ } \ it.pa -= it.indices[it.i] * it.astrides[it.i]; \ it.py -= it.indices[it.i] * it.ystrides[it.i]; \ it.indices[it.i] = 0; \ } \ it.its++; /* three input arrays ---------------------------------------------------- */ /* this iterator is used mainly by rankdata and nanrankdata */ struct _iter3 { int ndim_m2; int axis; Py_ssize_t length; Py_ssize_t astride; Py_ssize_t ystride; Py_ssize_t zstride; npy_intp i; npy_intp its; npy_intp nits; npy_intp indices[NPY_MAXDIMS]; npy_intp astrides[NPY_MAXDIMS]; npy_intp ystrides[NPY_MAXDIMS]; npy_intp zstrides[NPY_MAXDIMS]; npy_intp shape[NPY_MAXDIMS]; char *pa; char *py; char *pz; }; typedef struct _iter3 iter3; static BN_INLINE void init_iter3(iter3 *it, PyArrayObject *a, PyObject *y, PyObject *z, int axis) { int i, j = 0; const int ndim = PyArray_NDIM(a); const npy_intp *shape = PyArray_SHAPE(a); const npy_intp *astrides = PyArray_STRIDES(a); const npy_intp *ystrides = PyArray_STRIDES((PyArrayObject *)y); const npy_intp *zstrides = PyArray_STRIDES((PyArrayObject *)z); /* to avoid compiler warning of uninitialized variables */ it->length = 0; it->astride = 0; it->ystride = 0; it->zstride = 0; it->ndim_m2 = ndim - 2; it->axis = axis; it->its = 0; it->nits = 1; it->pa = PyArray_BYTES(a); it->py = PyArray_BYTES((PyArrayObject *)y); it->pz = PyArray_BYTES((PyArrayObject *)z); for (i = 0; i < ndim; i++) { if (i == axis) { it->astride = astrides[i]; it->ystride = ystrides[i]; it->zstride = zstrides[i]; it->length = shape[i]; } else { it->indices[j] = 0; it->astrides[j] = astrides[i]; it->ystrides[j] = ystrides[i]; it->zstrides[j] = zstrides[i]; it->shape[j] = shape[i]; it->nits *= shape[i]; j++; } } } #define NEXT3 \ for (it.i = it.ndim_m2; it.i > -1; it.i--) { \ if (it.indices[it.i] < it.shape[it.i] - 1) { \ it.pa += it.astrides[it.i]; \ it.py += it.ystrides[it.i]; \ it.pz += it.zstrides[it.i]; \ it.indices[it.i]++; \ break; \ } \ it.pa -= it.indices[it.i] * it.astrides[it.i]; \ it.py -= it.indices[it.i] * it.ystrides[it.i]; \ it.pz -= it.indices[it.i] * it.zstrides[it.i]; \ it.indices[it.i] = 0; \ } \ it.its++; /* macros used with iterators -------------------------------------------- */ /* most of these macros assume iterator is named `it` */ #define NDIM it.ndim_m2 + 2 #define SHAPE it.shape #define SIZE it.nits * it.length #define LENGTH it.length #define INDEX it.i #define WHILE while (it.its < it.nits) #define WHILE0 it.i = 0; while (it.i < min_count - 1) #define WHILE1 while (it.i < window) #define WHILE2 while (it.i < it.length) #define FOR for (it.i = 0; it.i < it.length; it.i++) #define FOR_REVERSE for (it.i = it.length - 1; it.i > -1; it.i--) #define RESET it.its = 0; #define A0(dtype) *(npy_##dtype *)(it.pa) #define AI(dtype) *(npy_##dtype *)(it.pa + it.i * it.astride) #define AX(dtype, x) *(npy_##dtype *)(it.pa + (x) * it.astride) #define AOLD(dtype) *(npy_##dtype *)(it.pa + (it.i - window) * it.astride) #define YPP *py++ #define YI(dtype) *(npy_##dtype *)(it.py + it.i++ * it.ystride) #define YX(dtype, x) *(npy_##dtype *)(it.py + (x) * it.ystride) #define ZX(dtype, x) *(npy_##dtype *)(it.pz + (x) * it.zstride) #define FILL_Y(value) \ int i; \ Py_ssize_t size = PyArray_SIZE((PyArrayObject *)y); \ for (i = 0; i < size; i++) YPP = value; Bottleneck-1.2.1/bottleneck/src/nonreduce_template.c0000664000175000017500000002124313103166630021576 0ustar kgkg00000000000000#include "bottleneck.h" #include "iterators.h" /* typedefs and prototypes ----------------------------------------------- */ /* function pointer for functions passed to nonreducer */ typedef PyObject *(*nr_t)(PyArrayObject *, double, double); static PyObject * nonreducer(char *name, PyObject *args, PyObject *kwds, nr_t, nr_t, nr_t, nr_t, int inplace); /* replace --------------------------------------------------------------- */ /* dtype = [['float64'], ['float32']] */ static PyObject * replace_DTYPE0(PyArrayObject *a, double old, double new) { npy_DTYPE0 ai; iter it; init_iter_all(&it, a, 0, 1); BN_BEGIN_ALLOW_THREADS if (old == old) { WHILE { FOR { if (AI(DTYPE0) == old) AI(DTYPE0) = new; } NEXT } } else { WHILE { FOR { ai = AI(DTYPE0); if (ai != ai) AI(DTYPE0) = new; } NEXT } } BN_END_ALLOW_THREADS Py_INCREF(a); return (PyObject *)a; } /* dtype end */ /* dtype = [['int64'], ['int32']] */ static PyObject * replace_DTYPE0(PyArrayObject *a, double old, double new) { npy_DTYPE0 oldint, newint; iter it; init_iter_all(&it, a, 0, 1); if (old == old) { oldint = (npy_DTYPE0)old; newint = (npy_DTYPE0)new; if (oldint != old) { VALUE_ERR("Cannot safely cast `old` to int"); return NULL; } if (newint != new) { VALUE_ERR("Cannot safely cast `new` to int"); return NULL; } BN_BEGIN_ALLOW_THREADS WHILE { FOR { if (AI(DTYPE0) == oldint) AI(DTYPE0) = newint; } NEXT } BN_END_ALLOW_THREADS } Py_INCREF(a); return (PyObject *)a; } /* dtype end */ static PyObject * replace(PyObject *self, PyObject *args, PyObject *kwds) { return nonreducer("replace", args, kwds, replace_float64, replace_float32, replace_int64, replace_int32, 1); } /* python strings -------------------------------------------------------- */ PyObject *pystr_a = NULL; PyObject *pystr_old = NULL; PyObject *pystr_new = NULL; static int intern_strings(void) { pystr_a = PyString_InternFromString("a"); pystr_old = PyString_InternFromString("old"); pystr_new = PyString_InternFromString("new"); return pystr_a && pystr_old && pystr_new; } /* nonreduce ------------------------------------------------------------- */ static BN_INLINE int parse_args(PyObject *args, PyObject *kwds, PyObject **a, PyObject **old, PyObject **new) { const Py_ssize_t nargs = PyTuple_GET_SIZE(args); const Py_ssize_t nkwds = kwds == NULL ? 0 : PyDict_Size(kwds); if (nkwds) { int nkwds_found = 0; switch (nargs) { case 2: *old = PyTuple_GET_ITEM(args, 1); case 1: *a = PyTuple_GET_ITEM(args, 0); case 0: break; default: TYPE_ERR("wrong number of arguments 1"); return 0; } switch (nargs) { case 0: *a = PyDict_GetItem(kwds, pystr_a); if (*a == NULL) { TYPE_ERR("Cannot find `a` keyword input"); return 0; } nkwds_found += 1; case 1: *old = PyDict_GetItem(kwds, pystr_old); if (*old == NULL) { TYPE_ERR("Cannot find `old` keyword input"); return 0; } nkwds_found += 1; case 2: *new = PyDict_GetItem(kwds, pystr_new); if (*new == NULL) { TYPE_ERR("Cannot find `new` keyword input"); return 0; } nkwds_found += 1; break; default: TYPE_ERR("wrong number of arguments 2"); return 0; } if (nkwds_found != nkwds) { TYPE_ERR("wrong number of keyword arguments 3"); return 0; } if (nargs + nkwds_found > 3) { TYPE_ERR("too many arguments"); return 0; } } else { switch (nargs) { case 3: *a = PyTuple_GET_ITEM(args, 0); *old = PyTuple_GET_ITEM(args, 1); *new = PyTuple_GET_ITEM(args, 2); break; default: TYPE_ERR("wrong number of arguments 4"); return 0; } } return 1; } static PyObject * nonreducer(char *name, PyObject *args, PyObject *kwds, nr_t nr_float64, nr_t nr_float32, nr_t nr_int64, nr_t nr_int32, int inplace) { int dtype; double old, new; PyArrayObject *a; PyObject *a_obj = NULL; PyObject *old_obj = NULL; PyObject *new_obj = NULL; if (!parse_args(args, kwds, &a_obj, &old_obj, &new_obj)) return NULL; /* convert to array if necessary */ if PyArray_Check(a_obj) { a = (PyArrayObject *)a_obj; } else { if (inplace) { TYPE_ERR("works in place so input must be an array, " "not (e.g.) a list"); return NULL; } a = (PyArrayObject *)PyArray_FROM_O(a_obj); if (a == NULL) { return NULL; } } /* check for byte swapped input array */ if PyArray_ISBYTESWAPPED(a) { return slow(name, args, kwds); } /* old */ if (old_obj == NULL) { RUNTIME_ERR("`old_obj` should never be NULL; please report this bug."); return NULL; } else { old = PyFloat_AsDouble(old_obj); if (error_converting(old)) { TYPE_ERR("`old` must be a number"); return NULL; } } /* new */ if (new_obj == NULL) { RUNTIME_ERR("`new_obj` should never be NULL; please report this bug."); return NULL; } else { new = PyFloat_AsDouble(new_obj); if (error_converting(new)) { TYPE_ERR("`new` must be a number"); return NULL; } } dtype = PyArray_TYPE(a); if (dtype == NPY_float64) return nr_float64(a, old, new); else if (dtype == NPY_float32) return nr_float32(a, old, new); else if (dtype == NPY_int64) return nr_int64(a, old, new); else if (dtype == NPY_int32) return nr_int32(a, old, new); else return slow(name, args, kwds); } /* docstrings ------------------------------------------------------------- */ static char nonreduce_doc[] = "Bottleneck nonreducing functions."; static char replace_doc[] = /* MULTILINE STRING BEGIN replace(a, old, new) Replace (inplace) given scalar values of an array with new values. The equivalent numpy function: a[a==old] = new Or in the case where old=np.nan: a[np.isnan(old)] = new Parameters ---------- a : numpy.ndarray The input array, which is also the output array since this functions works inplace. old : scalar All elements in `a` with this value will be replaced by `new`. new : scalar All elements in `a` with a value of `old` will be replaced by `new`. Returns ------- Returns a view of the input array after performing the replacements, if any. Examples -------- Replace zero with 3 (note that the input array is modified): >>> a = np.array([1, 2, 0]) >>> bn.replace(a, 0, 3) >>> a array([1, 2, 3]) Replace np.nan with 0: >>> a = np.array([1, 2, np.nan]) >>> bn.replace(a, np.nan, 0) >>> a array([ 1., 2., 0.]) MULTILINE STRING END */ /* python wrapper -------------------------------------------------------- */ static PyMethodDef nonreduce_methods[] = { {"replace", (PyCFunction)replace, VARKEY, replace_doc}, {NULL, NULL, 0, NULL} }; #if PY_MAJOR_VERSION >= 3 static struct PyModuleDef nonreduce_def = { PyModuleDef_HEAD_INIT, "nonreduce", nonreduce_doc, -1, nonreduce_methods }; #endif PyMODINIT_FUNC #if PY_MAJOR_VERSION >= 3 #define RETVAL m PyInit_nonreduce(void) #else #define RETVAL initnonreduce(void) #endif { #if PY_MAJOR_VERSION >=3 PyObject *m = PyModule_Create(&nonreduce_def); #else PyObject *m = Py_InitModule3("nonreduce", nonreduce_methods, nonreduce_doc); #endif if (m == NULL) return RETVAL; import_array(); if (!intern_strings()) { return RETVAL; } return RETVAL; } Bottleneck-1.2.1/bottleneck/src/reduce_template.c0000664000175000017500000013336013103166630021067 0ustar kgkg00000000000000#include "bottleneck.h" #include "iterators.h" /* init macros ----------------------------------------------------------- */ #define INIT_ALL \ iter it; \ init_iter_all(&it, a, 0, 1); #define INIT_ALL_RAVEL \ iter it; \ init_iter_all(&it, a, 1, 0); /* used with INIT_ALL_RAVEL */ #define DECREF_INIT_ALL_RAVEL \ if (it.a_ravel != NULL) { \ Py_DECREF(it.a_ravel); \ } #define INIT_ONE(dtype0, dtype1) \ iter it; \ PyObject *y; \ npy_##dtype1 *py; \ init_iter_one(&it, a, axis); \ y = PyArray_EMPTY(NDIM - 1, SHAPE, NPY_##dtype0, 0); \ py = (npy_##dtype1 *)PyArray_DATA((PyArrayObject *)y); /* function signatures --------------------------------------------------- */ /* low-level functions such as nansum_all_float64 */ #define REDUCE_ALL(name, dtype) \ static PyObject * \ name##_all_##dtype(PyArrayObject *a, int ddof) /* low-level functions such as nansum_one_float64 */ #define REDUCE_ONE(name, dtype) \ static PyObject * \ name##_one_##dtype(PyArrayObject *a, int axis, int ddof) /* top-level functions such as nansum */ #define REDUCE_MAIN(name, has_ddof) \ static PyObject * \ name(PyObject *self, PyObject *args, PyObject *kwds) \ { \ return reducer(#name, \ args, \ kwds, \ name##_all_float64, \ name##_all_float32, \ name##_all_int64, \ name##_all_int32, \ name##_one_float64, \ name##_one_float32, \ name##_one_int64, \ name##_one_int32, \ has_ddof); \ } /* typedefs and prototypes ----------------------------------------------- */ typedef PyObject *(*fall_t)(PyArrayObject *a, int ddof); typedef PyObject *(*fone_t)(PyArrayObject *a, int axis, int ddof); static PyObject * reducer(char *name, PyObject *args, PyObject *kwds, fall_t fall_float64, fall_t fall_float32, fall_t fall_int64, fall_t fall_int32, fone_t fone_float64, fone_t fone_float32, fone_t fone_int64, fone_t fone_int32, int has_ddof); /* nansum ---------------------------------------------------------------- */ /* dtype = [['float64'], ['float32']] */ REDUCE_ALL(nansum, DTYPE0) { npy_DTYPE0 ai, asum = 0; INIT_ALL BN_BEGIN_ALLOW_THREADS WHILE { FOR { ai = AI(DTYPE0); if (ai == ai) asum += ai; } NEXT } BN_END_ALLOW_THREADS return PyFloat_FromDouble(asum); } REDUCE_ONE(nansum, DTYPE0) { npy_DTYPE0 ai, asum; INIT_ONE(DTYPE0, DTYPE0) BN_BEGIN_ALLOW_THREADS if (LENGTH == 0) { FILL_Y(0) } else { WHILE { asum = 0; FOR { ai = AI(DTYPE0); if (ai == ai) asum += ai; } YPP = asum; NEXT } } BN_END_ALLOW_THREADS return y; } /* dtype end */ /* dtype = [['int64'], ['int32']] */ REDUCE_ALL(nansum, DTYPE0) { npy_DTYPE0 asum = 0; INIT_ALL BN_BEGIN_ALLOW_THREADS WHILE { FOR asum += AI(DTYPE0); NEXT } BN_END_ALLOW_THREADS return PyLong_FromLongLong(asum); } REDUCE_ONE(nansum, DTYPE0) { npy_DTYPE0 asum; INIT_ONE(DTYPE0, DTYPE0) BN_BEGIN_ALLOW_THREADS if (LENGTH == 0) { FILL_Y(0) } else { WHILE { asum = 0; FOR asum += AI(DTYPE0); YPP = asum; NEXT } } BN_END_ALLOW_THREADS return y; } /* dtype end */ REDUCE_MAIN(nansum, 0) /* nanmean ---------------------------------------------------------------- */ /* dtype = [['float64'], ['float32']] */ REDUCE_ALL(nanmean, DTYPE0) { Py_ssize_t count = 0; npy_DTYPE0 ai, asum = 0; INIT_ALL BN_BEGIN_ALLOW_THREADS WHILE { FOR { ai = AI(DTYPE0); if (ai == ai) { asum += ai; count += 1; } } NEXT } BN_END_ALLOW_THREADS if (count > 0) { return PyFloat_FromDouble(asum / count); } else { return PyFloat_FromDouble(BN_NAN); } } REDUCE_ONE(nanmean, DTYPE0) { Py_ssize_t count; npy_DTYPE0 ai, asum; INIT_ONE(DTYPE0, DTYPE0) BN_BEGIN_ALLOW_THREADS if (LENGTH == 0) { FILL_Y(BN_NAN) } else { WHILE { asum = count = 0; FOR { ai = AI(DTYPE0); if (ai == ai) { asum += ai; count += 1; } } if (count > 0) { asum /= count; } else { asum = BN_NAN; } YPP = asum; NEXT } } BN_END_ALLOW_THREADS return y; } /* dtype end */ /* dtype = [['int64', 'float64'], ['int32', 'float64']] */ REDUCE_ALL(nanmean, DTYPE0) { Py_ssize_t total_length = 0; npy_DTYPE1 asum = 0; INIT_ALL BN_BEGIN_ALLOW_THREADS WHILE { FOR asum += AI(DTYPE0); total_length += LENGTH; NEXT } BN_END_ALLOW_THREADS if (total_length > 0) { return PyFloat_FromDouble(asum / total_length); } else { return PyFloat_FromDouble(BN_NAN); } } REDUCE_ONE(nanmean, DTYPE0) { npy_DTYPE1 asum; INIT_ONE(DTYPE1, DTYPE1) BN_BEGIN_ALLOW_THREADS if (LENGTH == 0) { FILL_Y(BN_NAN) } else { WHILE { asum = 0; FOR asum += AI(DTYPE0); if (LENGTH > 0) { asum /= LENGTH; } else { asum = BN_NAN; } YPP = asum; NEXT } } BN_END_ALLOW_THREADS return y; } /* dtype end */ REDUCE_MAIN(nanmean, 0) /* nanstd, nanvar- ------------------------------------------------------- */ /* repeat = {'NAME': ['nanstd', 'nanvar'], 'FUNC': ['sqrt', '']} */ /* dtype = [['float64'], ['float32']] */ REDUCE_ALL(NAME, DTYPE0) { Py_ssize_t count = 0; npy_DTYPE0 ai, amean, out, asum = 0; INIT_ALL BN_BEGIN_ALLOW_THREADS WHILE { FOR { ai = AI(DTYPE0); if (ai == ai) { asum += ai; count++; } } NEXT } if (count > ddof) { amean = asum / count; asum = 0; RESET WHILE { FOR { ai = AI(DTYPE0); if (ai == ai) { ai -= amean; asum += ai * ai; } } NEXT } out = FUNC(asum / (count - ddof)); } else { out = BN_NAN; } BN_END_ALLOW_THREADS return PyFloat_FromDouble(out); } REDUCE_ONE(NAME, DTYPE0) { Py_ssize_t count; npy_DTYPE0 ai, asum, amean; INIT_ONE(DTYPE0, DTYPE0) BN_BEGIN_ALLOW_THREADS if (LENGTH == 0) { FILL_Y(BN_NAN) } else { WHILE { asum = count = 0; FOR { ai = AI(DTYPE0); if (ai == ai) { asum += ai; count++; } } if (count > ddof) { amean = asum / count; asum = 0; FOR { ai = AI(DTYPE0); if (ai == ai) { ai -= amean; asum += ai * ai; } } asum = FUNC(asum / (count - ddof)); } else { asum = BN_NAN; } YPP = asum; NEXT } } BN_END_ALLOW_THREADS return y; } /* dtype end */ /* dtype = [['int64', 'float64'], ['int32', 'float64']] */ REDUCE_ALL(NAME, DTYPE0) { npy_DTYPE1 out; Py_ssize_t size = 0; npy_DTYPE1 ai, amean, asum = 0; INIT_ALL BN_BEGIN_ALLOW_THREADS WHILE { FOR asum += AI(DTYPE0); size += LENGTH; NEXT } if (size > ddof) { amean = asum / size; asum = 0; RESET WHILE { FOR { ai = AI(DTYPE0) - amean; asum += ai * ai; } NEXT } out = FUNC(asum / (size - ddof)); } else { out = BN_NAN; } BN_END_ALLOW_THREADS return PyFloat_FromDouble(out); } REDUCE_ONE(NAME, DTYPE0) { npy_DTYPE1 ai, asum, amean, length_inv, length_ddof_inv; INIT_ONE(DTYPE1, DTYPE1) BN_BEGIN_ALLOW_THREADS length_inv = 1.0 / LENGTH; length_ddof_inv = 1.0 / (LENGTH - ddof); if (LENGTH == 0) { FILL_Y(BN_NAN) } else { WHILE { asum = 0; FOR asum += AI(DTYPE0); if (LENGTH > ddof) { amean = asum * length_inv; asum = 0; FOR { ai = AI(DTYPE0) - amean; asum += ai * ai; } asum = FUNC(asum * length_ddof_inv); } else { asum = BN_NAN; } YPP = asum; NEXT } } BN_END_ALLOW_THREADS return y; } /* dtype end */ REDUCE_MAIN(NAME, 1) /* repeat end */ /* nanmin, nanmax -------------------------------------------------------- */ /* repeat = {'NAME': ['nanmin', 'nanmax'], 'COMPARE': ['<=', '>='], 'BIG_FLOAT': ['BN_INFINITY', '-BN_INFINITY'], 'BIG_INT': ['NPY_MAX_DTYPE0', 'NPY_MIN_DTYPE0']} */ /* dtype = [['float64'], ['float32']] */ REDUCE_ALL(NAME, DTYPE0) { npy_DTYPE0 ai, extreme = BIG_FLOAT; int allnan = 1; INIT_ALL if (SIZE == 0) { VALUE_ERR("numpy.NAME raises on a.size==0 and axis=None; " "So Bottleneck too."); return NULL; } BN_BEGIN_ALLOW_THREADS WHILE { FOR { ai = AI(DTYPE0); if (ai COMPARE extreme) { extreme = ai; allnan = 0; } } NEXT } if (allnan) extreme = BN_NAN; BN_END_ALLOW_THREADS return PyFloat_FromDouble(extreme); } REDUCE_ONE(NAME, DTYPE0) { npy_DTYPE0 ai, extreme; int allnan; INIT_ONE(DTYPE0, DTYPE0) if (LENGTH == 0) { VALUE_ERR("numpy.NAME raises on a.shape[axis]==0; " "So Bottleneck too."); return NULL; } BN_BEGIN_ALLOW_THREADS WHILE { extreme = BIG_FLOAT; allnan = 1; FOR { ai = AI(DTYPE0); if (ai COMPARE extreme) { extreme = ai; allnan = 0; } } if (allnan) extreme = BN_NAN; YPP = extreme; NEXT } BN_END_ALLOW_THREADS return y; } /* dtype end */ /* dtype = [['int64'], ['int32']] */ REDUCE_ALL(NAME, DTYPE0) { npy_DTYPE0 ai, extreme = BIG_INT; INIT_ALL if (SIZE == 0) { VALUE_ERR("numpy.NAME raises on a.size==0 and axis=None; " "So Bottleneck too."); return NULL; } BN_BEGIN_ALLOW_THREADS WHILE { FOR { ai = AI(DTYPE0); if (ai COMPARE extreme) extreme = ai; } NEXT } BN_END_ALLOW_THREADS return PyLong_FromLongLong(extreme); } REDUCE_ONE(NAME, DTYPE0) { npy_DTYPE0 ai, extreme; INIT_ONE(DTYPE0, DTYPE0) if (LENGTH == 0) { VALUE_ERR("numpy.NAME raises on a.shape[axis]==0; " "So Bottleneck too."); return NULL; } BN_BEGIN_ALLOW_THREADS WHILE { extreme = BIG_INT; FOR { ai = AI(DTYPE0); if (ai COMPARE extreme) extreme = ai; } YPP = extreme; NEXT } BN_END_ALLOW_THREADS return y; } /* dtype end */ REDUCE_MAIN(NAME, 0) /* repeat end */ /* nanargmin, nanargmax -------------------------------------------------- */ /* repeat = {'NAME': ['nanargmin', 'nanargmax'], 'COMPARE': ['<=', '>='], 'BIG_FLOAT': ['BN_INFINITY', '-BN_INFINITY'], 'BIG_INT': ['NPY_MAX_DTYPE0', 'NPY_MIN_DTYPE0']} */ /* dtype = [['float64'], ['float32']] */ REDUCE_ALL(NAME, DTYPE0) { npy_DTYPE0 ai, extreme = BIG_FLOAT; int allnan = 1; Py_ssize_t idx = 0; INIT_ALL_RAVEL if (SIZE == 0) { DECREF_INIT_ALL_RAVEL VALUE_ERR("numpy.NAME raises on a.size==0 and axis=None; " "So Bottleneck too."); return NULL; } BN_BEGIN_ALLOW_THREADS FOR_REVERSE { ai = AI(DTYPE0); if (ai COMPARE extreme) { extreme = ai; allnan = 0; idx = INDEX; } } BN_END_ALLOW_THREADS DECREF_INIT_ALL_RAVEL if (allnan) { VALUE_ERR("All-NaN slice encountered"); return NULL; } else { return PyLong_FromLongLong(idx); } } REDUCE_ONE(NAME, DTYPE0) { int allnan, err_code = 0; Py_ssize_t idx = 0; npy_DTYPE0 ai, extreme; INIT_ONE(INTP, intp) if (LENGTH == 0) { VALUE_ERR("numpy.NAME raises on a.shape[axis]==0; " "So Bottleneck too."); return NULL; } BN_BEGIN_ALLOW_THREADS WHILE { extreme = BIG_FLOAT; allnan = 1; FOR_REVERSE { ai = AI(DTYPE0); if (ai COMPARE extreme) { extreme = ai; allnan = 0; idx = INDEX; } } if (allnan == 0) { YPP = idx; } else { err_code = 1; } NEXT } BN_END_ALLOW_THREADS if (err_code) { VALUE_ERR("All-NaN slice encountered"); return NULL; } return y; } /* dtype end */ /* dtype = [['int64', 'intp'], ['int32', 'intp']] */ REDUCE_ALL(NAME, DTYPE0) { npy_DTYPE1 idx = 0; npy_DTYPE0 ai, extreme = BIG_INT; INIT_ALL_RAVEL if (SIZE == 0) { DECREF_INIT_ALL_RAVEL VALUE_ERR("numpy.NAME raises on a.size==0 and axis=None; " "So Bottleneck too."); return NULL; } BN_BEGIN_ALLOW_THREADS FOR_REVERSE { ai = AI(DTYPE0); if (ai COMPARE extreme) { extreme = ai; idx = INDEX; } } BN_END_ALLOW_THREADS DECREF_INIT_ALL_RAVEL return PyLong_FromLongLong(idx); } REDUCE_ONE(NAME, DTYPE0) { npy_DTYPE1 idx = 0; npy_DTYPE0 ai, extreme; INIT_ONE(DTYPE1, DTYPE1) if (LENGTH == 0) { VALUE_ERR("numpy.NAME raises on a.shape[axis]==0; " "So Bottleneck too."); return NULL; } BN_BEGIN_ALLOW_THREADS WHILE { extreme = BIG_INT; FOR_REVERSE{ ai = AI(DTYPE0); if (ai COMPARE extreme) { extreme = ai; idx = INDEX; } } YPP = idx; NEXT } BN_END_ALLOW_THREADS return y; } /* dtype end */ REDUCE_MAIN(NAME, 0) /* repeat end */ /* ss ---------------------------------------------------------------- */ /* dtype = [['float64'], ['float32']] */ REDUCE_ALL(ss, DTYPE0) { npy_DTYPE0 ai, asum = 0; INIT_ALL BN_BEGIN_ALLOW_THREADS WHILE { FOR { ai = AI(DTYPE0); asum += ai * ai; } NEXT } BN_END_ALLOW_THREADS return PyFloat_FromDouble(asum); } REDUCE_ONE(ss, DTYPE0) { npy_DTYPE0 ai, asum; INIT_ONE(DTYPE0, DTYPE0) BN_BEGIN_ALLOW_THREADS if (LENGTH == 0) { FILL_Y(0) } else { WHILE { asum = 0; FOR{ ai = AI(DTYPE0); asum += ai * ai; } YPP = asum; NEXT } } BN_END_ALLOW_THREADS return y; } /* dtype end */ /* dtype = [['int64'], ['int32']] */ REDUCE_ALL(ss, DTYPE0) { npy_DTYPE0 ai, asum = 0; INIT_ALL BN_BEGIN_ALLOW_THREADS WHILE { FOR { ai = AI(DTYPE0); asum += ai * ai; } NEXT } BN_END_ALLOW_THREADS return PyLong_FromLongLong(asum); } REDUCE_ONE(ss, DTYPE0) { npy_DTYPE0 ai, asum; INIT_ONE(DTYPE0, DTYPE0) BN_BEGIN_ALLOW_THREADS if (LENGTH == 0) { FILL_Y(0) } else { WHILE { asum = 0; FOR{ ai = AI(DTYPE0); asum += ai * ai; } YPP = asum; NEXT } } BN_END_ALLOW_THREADS return y; } /* dtype end */ REDUCE_MAIN(ss, 0) /* median, nanmedian MACROS ---------------------------------------------- */ #define B(dtype, i) buffer[i] /* used by PARTITION */ #define EVEN_ODD(dtype, N) \ if (N % 2 == 0) { \ npy_##dtype amax = B(dtype, 0); \ for (i = 1; i < k; i++) { \ ai = B(dtype, i); \ if (ai > amax) amax = ai; \ } \ med = 0.5 * (B(dtype, k) + amax); \ } \ else { \ med = B(dtype, k); \ } \ #define MEDIAN(dtype) \ npy_intp j, l, r, k; \ npy_##dtype ai; \ l = 0; \ for (i = 0; i < LENGTH; i++) { \ ai = AX(dtype, i); \ if (ai == ai) { \ B(dtype, l++) = ai; \ } \ } \ if (l != LENGTH) { \ med = BN_NAN; \ goto done; \ } \ k = LENGTH >> 1; \ l = 0; \ r = LENGTH - 1; \ PARTITION(dtype) \ EVEN_ODD(dtype, LENGTH) #define MEDIAN_INT(dtype) \ npy_intp j, l, r, k; \ npy_##dtype ai; \ for (i = 0; i < LENGTH; i++) { \ B(dtype, i) = AX(dtype, i); \ } \ k = LENGTH >> 1; \ l = 0; \ r = LENGTH - 1; \ PARTITION(dtype) \ EVEN_ODD(dtype, LENGTH) #define NANMEDIAN(dtype) \ npy_intp j, l, r, k, n; \ npy_##dtype ai; \ l = 0; \ for (i = 0; i < LENGTH; i++) { \ ai = AX(dtype, i); \ if (ai == ai) { \ B(dtype, l++) = ai; \ } \ } \ n = l; \ k = n >> 1; \ l = 0; \ r = n - 1; \ if (n == 0) { \ med = BN_NAN; \ goto done; \ } \ PARTITION(dtype) \ EVEN_ODD(dtype, n) #define BUFFER_NEW(dtype, length) \ npy_##dtype *buffer = malloc(length * sizeof(npy_##dtype)); #define BUFFER_DELETE free(buffer); /* median, nanmedian ----------------------------------------------------- */ /* repeat = {'NAME': ['median', 'nanmedian'], 'FUNC': ['MEDIAN', 'NANMEDIAN']} */ /* dtype = [['float64', 'float64'], ['float32', 'float32']] */ REDUCE_ALL(NAME, DTYPE0) { npy_intp i; npy_DTYPE1 med; INIT_ALL_RAVEL BN_BEGIN_ALLOW_THREADS BUFFER_NEW(DTYPE0, LENGTH) if (LENGTH == 0) { med = BN_NAN; } else { FUNC(DTYPE0) } done: BUFFER_DELETE BN_END_ALLOW_THREADS DECREF_INIT_ALL_RAVEL return PyFloat_FromDouble(med); } REDUCE_ONE(NAME, DTYPE0) { npy_intp i; npy_DTYPE1 med; INIT_ONE(DTYPE1, DTYPE1) BN_BEGIN_ALLOW_THREADS if (LENGTH == 0) { FILL_Y(BN_NAN) } else { BUFFER_NEW(DTYPE0, LENGTH) WHILE { FUNC(DTYPE0) done: YPP = med; NEXT } BUFFER_DELETE } BN_END_ALLOW_THREADS return y; } /* dtype end */ /* repeat end */ /* dtype = [['int64', 'float64'], ['int32', 'float64']] */ REDUCE_ALL(median, DTYPE0) { npy_intp i; npy_DTYPE1 med; INIT_ALL_RAVEL BN_BEGIN_ALLOW_THREADS if (LENGTH == 0) { med = BN_NAN; } else { BUFFER_NEW(DTYPE0, LENGTH) MEDIAN_INT(DTYPE0) BUFFER_DELETE } BN_END_ALLOW_THREADS DECREF_INIT_ALL_RAVEL return PyFloat_FromDouble(med); } REDUCE_ONE(median, DTYPE0) { npy_intp i; npy_DTYPE1 med; INIT_ONE(DTYPE1, DTYPE1) BN_BEGIN_ALLOW_THREADS if (LENGTH == 0) { FILL_Y(BN_NAN) } else { BUFFER_NEW(DTYPE0, LENGTH) WHILE { MEDIAN_INT(DTYPE0) YPP = med; NEXT } BUFFER_DELETE } BN_END_ALLOW_THREADS return y; } /* dtype end */ REDUCE_MAIN(median, 0) static PyObject * nanmedian(PyObject *self, PyObject *args, PyObject *kwds) { return reducer("nanmedian", args, kwds, nanmedian_all_float64, nanmedian_all_float32, median_all_int64, median_all_int32, nanmedian_one_float64, nanmedian_one_float32, median_one_int64, median_one_int32, 0); } /* anynan ---------------------------------------------------------------- */ /* dtype = [['float64'], ['float32']] */ REDUCE_ALL(anynan, DTYPE0) { int f = 0; npy_DTYPE0 ai; INIT_ALL BN_BEGIN_ALLOW_THREADS WHILE { FOR { ai = AI(DTYPE0); if (ai != ai) { f = 1; goto done; } } NEXT } done: BN_END_ALLOW_THREADS if (f) Py_RETURN_TRUE; Py_RETURN_FALSE; } REDUCE_ONE(anynan, DTYPE0) { int f; npy_DTYPE0 ai; INIT_ONE(BOOL, uint8) BN_BEGIN_ALLOW_THREADS if (LENGTH == 0) { FILL_Y(0) } else { WHILE { f = 0; FOR { ai = AI(DTYPE0); if (ai != ai) { f = 1; break; } } YPP = f; NEXT } } BN_END_ALLOW_THREADS return y; } /* dtype end */ /* dtype = [['int64'], ['int32']] */ REDUCE_ALL(anynan, DTYPE0) { Py_RETURN_FALSE; } REDUCE_ONE(anynan, DTYPE0) { INIT_ONE(BOOL, uint8) BN_BEGIN_ALLOW_THREADS FILL_Y(0); BN_END_ALLOW_THREADS return y; } /* dtype end */ REDUCE_MAIN(anynan, 0) /* allnan ---------------------------------------------------------------- */ /* dtype = [['float64'], ['float32']] */ REDUCE_ALL(allnan, DTYPE0) { int f = 0; npy_DTYPE0 ai; INIT_ALL BN_BEGIN_ALLOW_THREADS WHILE { FOR { ai = AI(DTYPE0); if (ai == ai) { f = 1; goto done; } } NEXT } done: BN_END_ALLOW_THREADS if (f) Py_RETURN_FALSE; Py_RETURN_TRUE; } REDUCE_ONE(allnan, DTYPE0) { int f; npy_DTYPE0 ai; INIT_ONE(BOOL, uint8) BN_BEGIN_ALLOW_THREADS if (LENGTH == 0) { FILL_Y(1) } else { WHILE { f = 1; FOR { ai = AI(DTYPE0); if (ai == ai) { f = 0; break; } } YPP = f; NEXT } } BN_END_ALLOW_THREADS return y; } /* dtype end */ /* dtype = [['int64'], ['int32']] */ REDUCE_ALL(allnan, DTYPE0) { if (PyArray_SIZE(a) == 0) Py_RETURN_TRUE; Py_RETURN_FALSE; } REDUCE_ONE(allnan, DTYPE0) { INIT_ONE(BOOL, uint8) BN_BEGIN_ALLOW_THREADS if (SIZE == 0) { FILL_Y(1); } else { FILL_Y(0); } BN_END_ALLOW_THREADS return y; } /* dtype end */ REDUCE_MAIN(allnan, 0) /* python strings -------------------------------------------------------- */ PyObject *pystr_a = NULL; PyObject *pystr_axis = NULL; PyObject *pystr_ddof = NULL; static int intern_strings(void) { pystr_a = PyString_InternFromString("a"); pystr_axis = PyString_InternFromString("axis"); pystr_ddof = PyString_InternFromString("ddof"); return pystr_a && pystr_axis && pystr_ddof; } /* reducer --------------------------------------------------------------- */ static BN_INLINE int parse_args(PyObject *args, PyObject *kwds, int has_ddof, PyObject **a, PyObject **axis, PyObject **ddof) { const Py_ssize_t nargs = PyTuple_GET_SIZE(args); const Py_ssize_t nkwds = kwds == NULL ? 0 : PyDict_Size(kwds); if (nkwds) { int nkwds_found = 0; PyObject *tmp; switch (nargs) { case 2: if (has_ddof) { *axis = PyTuple_GET_ITEM(args, 1); } else { TYPE_ERR("wrong number of arguments"); return 0; } case 1: *a = PyTuple_GET_ITEM(args, 0); case 0: break; default: TYPE_ERR("wrong number of arguments"); return 0; } switch (nargs) { case 0: *a = PyDict_GetItem(kwds, pystr_a); if (*a == NULL) { TYPE_ERR("Cannot find `a` keyword input"); return 0; } nkwds_found += 1; case 1: tmp = PyDict_GetItem(kwds, pystr_axis); if (tmp != NULL) { *axis = tmp; nkwds_found++; } case 2: if (has_ddof) { tmp = PyDict_GetItem(kwds, pystr_ddof); if (tmp != NULL) { *ddof = tmp; nkwds_found++; } break; } break; default: TYPE_ERR("wrong number of arguments"); return 0; } if (nkwds_found != nkwds) { TYPE_ERR("wrong number of keyword arguments"); return 0; } if (nargs + nkwds_found > 2 + has_ddof) { TYPE_ERR("too many arguments"); return 0; } } else { switch (nargs) { case 3: if (has_ddof) { *ddof = PyTuple_GET_ITEM(args, 2); } else { TYPE_ERR("wrong number of arguments"); return 0; } case 2: *axis = PyTuple_GET_ITEM(args, 1); case 1: *a = PyTuple_GET_ITEM(args, 0); break; default: TYPE_ERR("wrong number of arguments"); return 0; } } return 1; } static PyObject * reducer(char *name, PyObject *args, PyObject *kwds, fall_t fall_float64, fall_t fall_float32, fall_t fall_int64, fall_t fall_int32, fone_t fone_float64, fone_t fone_float32, fone_t fone_int64, fone_t fone_int32, int has_ddof) { int ndim; int axis = 0; /* initialize to avoid compiler error */ int dtype; int ddof; int reduce_all = 0; PyArrayObject *a; PyObject *a_obj = NULL; PyObject *axis_obj = Py_None; PyObject *ddof_obj = NULL; if (!parse_args(args, kwds, has_ddof, &a_obj, &axis_obj, &ddof_obj)) { return NULL; } /* convert to array if necessary */ if PyArray_Check(a_obj) { a = (PyArrayObject *)a_obj; } else { a = (PyArrayObject *)PyArray_FROM_O(a_obj); if (a == NULL) { return NULL; } } /* check for byte swapped input array */ if PyArray_ISBYTESWAPPED(a) { return slow(name, args, kwds); } /* does user want to reduce over all axes? */ if (axis_obj == Py_None) { reduce_all = 1; } else { axis = PyArray_PyIntAsInt(axis_obj); if (error_converting(axis)) { TYPE_ERR("`axis` must be an integer or None"); return NULL; } ndim = PyArray_NDIM(a); if (axis < 0) { axis += ndim; if (axis < 0) { PyErr_Format(PyExc_ValueError, "axis(=%d) out of bounds", axis); return NULL; } } else if (axis >= ndim) { PyErr_Format(PyExc_ValueError, "axis(=%d) out of bounds", axis); return NULL; } if (ndim == 1) { reduce_all = 1; } } /* ddof */ if (ddof_obj == NULL) { ddof = 0; } else { ddof = PyArray_PyIntAsInt(ddof_obj); if (error_converting(ddof)) { TYPE_ERR("`ddof` must be an integer"); return NULL; } } dtype = PyArray_TYPE(a); if (reduce_all == 1) { /* we are reducing the array along all axes */ if (dtype == NPY_FLOAT64) { return fall_float64(a, ddof); } else if (dtype == NPY_FLOAT32) { return fall_float32(a, ddof); } else if (dtype == NPY_INT64) { return fall_int64(a, ddof); } else if (dtype == NPY_INT32) { return fall_int32(a, ddof); } else { return slow(name, args, kwds); } } else { /* we are reducing an array with ndim > 1 over a single axis */ if (dtype == NPY_FLOAT64) { return fone_float64(a, axis, ddof); } else if (dtype == NPY_FLOAT32) { return fone_float32(a, axis, ddof); } else if (dtype == NPY_INT64) { return fone_int64(a, axis, ddof); } else if (dtype == NPY_INT32) { return fone_int32(a, axis, ddof); } else { return slow(name, args, kwds); } } } /* docstrings ------------------------------------------------------------- */ static char reduce_doc[] = "Bottleneck functions that reduce the input array along a specified axis."; static char nansum_doc[] = /* MULTILINE STRING BEGIN nansum(a, axis=None) Sum of array elements along given axis treating NaNs as zero. The data type (dtype) of the output is the same as the input. On 64-bit operating systems, 32-bit input is NOT upcast to 64-bit accumulator and return values. Parameters ---------- a : array_like Array containing numbers whose sum is desired. If `a` is not an array, a conversion is attempted. axis : {int, None}, optional Axis along which the sum is computed. The default (axis=None) is to compute the sum of the flattened array. Returns ------- y : ndarray An array with the same shape as `a`, with the specified axis removed. If `a` is a 0-d array, or if axis is None, a scalar is returned. Notes ----- No error is raised on overflow. If positive or negative infinity are present the result is positive or negative infinity. But if both positive and negative infinity are present, the result is Not A Number (NaN). Examples -------- >>> bn.nansum(1) 1 >>> bn.nansum([1]) 1 >>> bn.nansum([1, np.nan]) 1.0 >>> a = np.array([[1, 1], [1, np.nan]]) >>> bn.nansum(a) 3.0 >>> bn.nansum(a, axis=0) array([ 2., 1.]) When positive infinity and negative infinity are present: >>> bn.nansum([1, np.nan, np.inf]) inf >>> bn.nansum([1, np.nan, np.NINF]) -inf >>> bn.nansum([1, np.nan, np.inf, np.NINF]) nan MULTILINE STRING END */ static char nanmean_doc[] = /* MULTILINE STRING BEGIN nanmean(a, axis=None) Mean of array elements along given axis ignoring NaNs. `float64` intermediate and return values are used for integer inputs. Parameters ---------- a : array_like Array containing numbers whose mean is desired. If `a` is not an array, a conversion is attempted. axis : {int, None}, optional Axis along which the means are computed. The default (axis=None) is to compute the mean of the flattened array. Returns ------- y : ndarray An array with the same shape as `a`, with the specified axis removed. If `a` is a 0-d array, or if axis is None, a scalar is returned. `float64` intermediate and return values are used for integer inputs. See also -------- bottleneck.nanmedian: Median along specified axis, ignoring NaNs. Notes ----- No error is raised on overflow. (The sum is computed and then the result is divided by the number of non-NaN elements.) If positive or negative infinity are present the result is positive or negative infinity. But if both positive and negative infinity are present, the result is Not A Number (NaN). Examples -------- >>> bn.nanmean(1) 1.0 >>> bn.nanmean([1]) 1.0 >>> bn.nanmean([1, np.nan]) 1.0 >>> a = np.array([[1, 4], [1, np.nan]]) >>> bn.nanmean(a) 2.0 >>> bn.nanmean(a, axis=0) array([ 1., 4.]) When positive infinity and negative infinity are present: >>> bn.nanmean([1, np.nan, np.inf]) inf >>> bn.nanmean([1, np.nan, np.NINF]) -inf >>> bn.nanmean([1, np.nan, np.inf, np.NINF]) nan MULTILINE STRING END */ static char nanstd_doc[] = /* MULTILINE STRING BEGIN nanstd(a, axis=None, ddof=0) Standard deviation along the specified axis, ignoring NaNs. `float64` intermediate and return values are used for integer inputs. Instead of a faster one-pass algorithm, a more stable two-pass algorithm is used. An example of a one-pass algorithm: >>> np.sqrt((a*a).mean() - a.mean()**2) An example of a two-pass algorithm: >>> np.sqrt(((a - a.mean())**2).mean()) Note in the two-pass algorithm the mean must be found (first pass) before the squared deviation (second pass) can be found. Parameters ---------- a : array_like Input array. If `a` is not an array, a conversion is attempted. axis : {int, None}, optional Axis along which the standard deviation is computed. The default (axis=None) is to compute the standard deviation of the flattened array. ddof : int, optional Means Delta Degrees of Freedom. The divisor used in calculations is ``N - ddof``, where ``N`` represents the number of non-NaN elements. By default `ddof` is zero. Returns ------- y : ndarray An array with the same shape as `a`, with the specified axis removed. If `a` is a 0-d array, or if axis is None, a scalar is returned. `float64` intermediate and return values are used for integer inputs. If ddof is >= the number of non-NaN elements in a slice or the slice contains only NaNs, then the result for that slice is NaN. See also -------- bottleneck.nanvar: Variance along specified axis ignoring NaNs Notes ----- If positive or negative infinity are present the result is Not A Number (NaN). Examples -------- >>> bn.nanstd(1) 0.0 >>> bn.nanstd([1]) 0.0 >>> bn.nanstd([1, np.nan]) 0.0 >>> a = np.array([[1, 4], [1, np.nan]]) >>> bn.nanstd(a) 1.4142135623730951 >>> bn.nanstd(a, axis=0) array([ 0., 0.]) When positive infinity or negative infinity are present NaN is returned: >>> bn.nanstd([1, np.nan, np.inf]) nan MULTILINE STRING END */ static char nanvar_doc[] = /* MULTILINE STRING BEGIN nanvar(a, axis=None, ddof=0) Variance along the specified axis, ignoring NaNs. `float64` intermediate and return values are used for integer inputs. Instead of a faster one-pass algorithm, a more stable two-pass algorithm is used. An example of a one-pass algorithm: >>> (a*a).mean() - a.mean()**2 An example of a two-pass algorithm: >>> ((a - a.mean())**2).mean() Note in the two-pass algorithm the mean must be found (first pass) before the squared deviation (second pass) can be found. Parameters ---------- a : array_like Input array. If `a` is not an array, a conversion is attempted. axis : {int, None}, optional Axis along which the variance is computed. The default (axis=None) is to compute the variance of the flattened array. ddof : int, optional Means Delta Degrees of Freedom. The divisor used in calculations is ``N - ddof``, where ``N`` represents the number of non_NaN elements. By default `ddof` is zero. Returns ------- y : ndarray An array with the same shape as `a`, with the specified axis removed. If `a` is a 0-d array, or if axis is None, a scalar is returned. `float64` intermediate and return values are used for integer inputs. If ddof is >= the number of non-NaN elements in a slice or the slice contains only NaNs, then the result for that slice is NaN. See also -------- bottleneck.nanstd: Standard deviation along specified axis ignoring NaNs. Notes ----- If positive or negative infinity are present the result is Not A Number (NaN). Examples -------- >>> bn.nanvar(1) 0.0 >>> bn.nanvar([1]) 0.0 >>> bn.nanvar([1, np.nan]) 0.0 >>> a = np.array([[1, 4], [1, np.nan]]) >>> bn.nanvar(a) 2.0 >>> bn.nanvar(a, axis=0) array([ 0., 0.]) When positive infinity or negative infinity are present NaN is returned: >>> bn.nanvar([1, np.nan, np.inf]) nan MULTILINE STRING END */ static char nanmin_doc[] = /* MULTILINE STRING BEGIN nanmin(a, axis=None) Minimum values along specified axis, ignoring NaNs. When all-NaN slices are encountered, NaN is returned for that slice. Parameters ---------- a : array_like Input array. If `a` is not an array, a conversion is attempted. axis : {int, None}, optional Axis along which the minimum is computed. The default (axis=None) is to compute the minimum of the flattened array. Returns ------- y : ndarray An array with the same shape as `a`, with the specified axis removed. If `a` is a 0-d array, or if axis is None, a scalar is returned. The same dtype as `a` is returned. See also -------- bottleneck.nanmax: Maximum along specified axis, ignoring NaNs. bottleneck.nanargmin: Indices of minimum values along axis, ignoring NaNs. Examples -------- >>> bn.nanmin(1) 1 >>> bn.nanmin([1]) 1 >>> bn.nanmin([1, np.nan]) 1.0 >>> a = np.array([[1, 4], [1, np.nan]]) >>> bn.nanmin(a) 1.0 >>> bn.nanmin(a, axis=0) array([ 1., 4.]) MULTILINE STRING END */ static char nanmax_doc[] = /* MULTILINE STRING BEGIN nanmax(a, axis=None) Maximum values along specified axis, ignoring NaNs. When all-NaN slices are encountered, NaN is returned for that slice. Parameters ---------- a : array_like Input array. If `a` is not an array, a conversion is attempted. axis : {int, None}, optional Axis along which the maximum is computed. The default (axis=None) is to compute the maximum of the flattened array. Returns ------- y : ndarray An array with the same shape as `a`, with the specified axis removed. If `a` is a 0-d array, or if axis is None, a scalar is returned. The same dtype as `a` is returned. See also -------- bottleneck.nanmin: Minimum along specified axis, ignoring NaNs. bottleneck.nanargmax: Indices of maximum values along axis, ignoring NaNs. Examples -------- >>> bn.nanmax(1) 1 >>> bn.nanmax([1]) 1 >>> bn.nanmax([1, np.nan]) 1.0 >>> a = np.array([[1, 4], [1, np.nan]]) >>> bn.nanmax(a) 4.0 >>> bn.nanmax(a, axis=0) array([ 1., 4.]) MULTILINE STRING END */ static char nanargmin_doc[] = /* MULTILINE STRING BEGIN nanargmin(a, axis=None) Indices of the minimum values along an axis, ignoring NaNs. For all-NaN slices ``ValueError`` is raised. Unlike NumPy, the results can be trusted if a slice contains only NaNs and Infs. Parameters ---------- a : array_like Input array. If `a` is not an array, a conversion is attempted. axis : {int, None}, optional Axis along which to operate. By default (axis=None) flattened input is used. See also -------- bottleneck.nanargmax: Indices of the maximum values along an axis. bottleneck.nanmin: Minimum values along specified axis, ignoring NaNs. Returns ------- index_array : ndarray An array of indices or a single index value. Examples -------- >>> a = np.array([[np.nan, 4], [2, 3]]) >>> bn.nanargmin(a) 2 >>> a.flat[2] 2.0 >>> bn.nanargmin(a, axis=0) array([1, 1]) >>> bn.nanargmin(a, axis=1) array([1, 0]) MULTILINE STRING END */ static char nanargmax_doc[] = /* MULTILINE STRING BEGIN nanargmax(a, axis=None) Indices of the maximum values along an axis, ignoring NaNs. For all-NaN slices ``ValueError`` is raised. Unlike NumPy, the results can be trusted if a slice contains only NaNs and Infs. Parameters ---------- a : array_like Input array. If `a` is not an array, a conversion is attempted. axis : {int, None}, optional Axis along which to operate. By default (axis=None) flattened input is used. See also -------- bottleneck.nanargmin: Indices of the minimum values along an axis. bottleneck.nanmax: Maximum values along specified axis, ignoring NaNs. Returns ------- index_array : ndarray An array of indices or a single index value. Examples -------- >>> a = np.array([[np.nan, 4], [2, 3]]) >>> bn.nanargmax(a) 1 >>> a.flat[1] 4.0 >>> bn.nanargmax(a, axis=0) array([1, 0]) >>> bn.nanargmax(a, axis=1) array([1, 1]) MULTILINE STRING END */ static char ss_doc[] = /* MULTILINE STRING BEGIN ss(a, axis=None) Sum of the square of each element along the specified axis. Parameters ---------- a : array_like Array whose sum of squares is desired. If `a` is not an array, a conversion is attempted. axis : {int, None}, optional Axis along which the sum of squares is computed. The default (axis=None) is to sum the squares of the flattened array. Returns ------- y : ndarray The sum of a**2 along the given axis. Examples -------- >>> a = np.array([1., 2., 5.]) >>> bn.ss(a) 30.0 And calculating along an axis: >>> b = np.array([[1., 2., 5.], [2., 5., 6.]]) >>> bn.ss(b, axis=1) array([ 30., 65.]) MULTILINE STRING END */ static char median_doc[] = /* MULTILINE STRING BEGIN median(a, axis=None) Median of array elements along given axis. Parameters ---------- a : array_like Input array. If `a` is not an array, a conversion is attempted. axis : {int, None}, optional Axis along which the median is computed. The default (axis=None) is to compute the median of the flattened array. Returns ------- y : ndarray An array with the same shape as `a`, except that the specified axis has been removed. If `a` is a 0d array, or if axis is None, a scalar is returned. `float64` return values are used for integer inputs. NaN is returned for a slice that contains one or more NaNs. See also -------- bottleneck.nanmedian: Median along specified axis ignoring NaNs. Examples -------- >>> a = np.array([[10, 7, 4], [3, 2, 1]]) >>> bn.median(a) 3.5 >>> bn.median(a, axis=0) array([ 6.5, 4.5, 2.5]) >>> bn.median(a, axis=1) array([ 7., 2.]) MULTILINE STRING END */ static char nanmedian_doc[] = /* MULTILINE STRING BEGIN nanmedian(a, axis=None) Median of array elements along given axis ignoring NaNs. Parameters ---------- a : array_like Input array. If `a` is not an array, a conversion is attempted. axis : {int, None}, optional Axis along which the median is computed. The default (axis=None) is to compute the median of the flattened array. Returns ------- y : ndarray An array with the same shape as `a`, except that the specified axis has been removed. If `a` is a 0d array, or if axis is None, a scalar is returned. `float64` return values are used for integer inputs. See also -------- bottleneck.median: Median along specified axis. Examples -------- >>> a = np.array([[np.nan, 7, 4], [3, 2, 1]]) >>> a array([[ nan, 7., 4.], [ 3., 2., 1.]]) >>> bn.nanmedian(a) 3.0 >> bn.nanmedian(a, axis=0) array([ 3. , 4.5, 2.5]) >> bn.nanmedian(a, axis=1) array([ 5.5, 2. ]) MULTILINE STRING END */ static char anynan_doc[] = /* MULTILINE STRING BEGIN anynan(a, axis=None) Test whether any array element along a given axis is NaN. Returns the same output as np.isnan(a).any(axis) Parameters ---------- a : array_like Input array. If `a` is not an array, a conversion is attempted. axis : {int, None}, optional Axis along which NaNs are searched. The default (`axis` = ``None``) is to search for NaNs over a flattened input array. Returns ------- y : bool or ndarray A boolean or new `ndarray` is returned. See also -------- bottleneck.allnan: Test if all array elements along given axis are NaN Examples -------- >>> bn.anynan(1) False >>> bn.anynan(np.nan) True >>> bn.anynan([1, np.nan]) True >>> a = np.array([[1, 4], [1, np.nan]]) >>> bn.anynan(a) True >>> bn.anynan(a, axis=0) array([False, True], dtype=bool) MULTILINE STRING END */ static char allnan_doc[] = /* MULTILINE STRING BEGIN allnan(a, axis=None) Test whether all array elements along a given axis are NaN. Returns the same output as np.isnan(a).all(axis) Note that allnan([]) is True to match np.isnan([]).all() and all([]) Parameters ---------- a : array_like Input array. If `a` is not an array, a conversion is attempted. axis : {int, None}, optional Axis along which NaNs are searched. The default (`axis` = ``None``) is to search for NaNs over a flattened input array. Returns ------- y : bool or ndarray A boolean or new `ndarray` is returned. See also -------- bottleneck.anynan: Test if any array element along given axis is NaN Examples -------- >>> bn.allnan(1) False >>> bn.allnan(np.nan) True >>> bn.allnan([1, np.nan]) False >>> a = np.array([[1, np.nan], [1, np.nan]]) >>> bn.allnan(a) False >>> bn.allnan(a, axis=0) array([False, True], dtype=bool) An empty array returns True: >>> bn.allnan([]) True which is similar to: >>> all([]) True >>> np.isnan([]).all() True MULTILINE STRING END */ /* python wrapper -------------------------------------------------------- */ static PyMethodDef reduce_methods[] = { {"nansum", (PyCFunction)nansum, VARKEY, nansum_doc}, {"nanmean", (PyCFunction)nanmean, VARKEY, nanmean_doc}, {"nanstd", (PyCFunction)nanstd, VARKEY, nanstd_doc}, {"nanvar", (PyCFunction)nanvar, VARKEY, nanvar_doc}, {"nanmin", (PyCFunction)nanmin, VARKEY, nanmin_doc}, {"nanmax", (PyCFunction)nanmax, VARKEY, nanmax_doc}, {"nanargmin", (PyCFunction)nanargmin, VARKEY, nanargmin_doc}, {"nanargmax", (PyCFunction)nanargmax, VARKEY, nanargmax_doc}, {"ss", (PyCFunction)ss, VARKEY, ss_doc}, {"median", (PyCFunction)median, VARKEY, median_doc}, {"nanmedian", (PyCFunction)nanmedian, VARKEY, nanmedian_doc}, {"anynan", (PyCFunction)anynan, VARKEY, anynan_doc}, {"allnan", (PyCFunction)allnan, VARKEY, allnan_doc}, {NULL, NULL, 0, NULL} }; #if PY_MAJOR_VERSION >= 3 static struct PyModuleDef reduce_def = { PyModuleDef_HEAD_INIT, "reduce", reduce_doc, -1, reduce_methods }; #endif PyMODINIT_FUNC #if PY_MAJOR_VERSION >= 3 #define RETVAL m PyInit_reduce(void) #else #define RETVAL initreduce(void) #endif { #if PY_MAJOR_VERSION >=3 PyObject *m = PyModule_Create(&reduce_def); #else PyObject *m = Py_InitModule3("reduce", reduce_methods, reduce_doc); #endif if (m == NULL) return RETVAL; import_array(); if (!intern_strings()) { return RETVAL; } return RETVAL; } Bottleneck-1.2.1/bottleneck/slow/0000775000175000017500000000000013106356012015746 5ustar kgkg00000000000000Bottleneck-1.2.1/bottleneck/slow/reduce.py0000664000175000017500000000500213103166630017566 0ustar kgkg00000000000000import warnings import numpy as np from numpy import nanmean __all__ = ['median', 'nanmedian', 'nansum', 'nanmean', 'nanvar', 'nanstd', 'nanmin', 'nanmax', 'nanargmin', 'nanargmax', 'ss', 'anynan', 'allnan'] def nansum(a, axis=None): "Slow nansum function used for unaccelerated dtype." a = np.asarray(a) y = np.nansum(a, axis=axis) if y.dtype != a.dtype: y = y.astype(a.dtype) return y def nanargmin(a, axis=None): "Slow nanargmin function used for unaccelerated dtypes." with warnings.catch_warnings(): warnings.simplefilter("ignore") return np.nanargmin(a, axis=axis) def nanargmax(a, axis=None): "Slow nanargmax function used for unaccelerated dtypes." with warnings.catch_warnings(): warnings.simplefilter("ignore") return np.nanargmax(a, axis=axis) def nanvar(a, axis=None, ddof=0): "Slow nanvar function used for unaccelerated dtypes." with warnings.catch_warnings(): warnings.simplefilter("ignore") return np.nanvar(a, axis=axis, ddof=ddof) def nanstd(a, axis=None, ddof=0): "Slow nanstd function used for unaccelerated dtypes." with warnings.catch_warnings(): warnings.simplefilter("ignore") return np.nanstd(a, axis=axis, ddof=ddof) def nanmin(a, axis=None): "Slow nanmin function used for unaccelerated dtypes." with warnings.catch_warnings(): warnings.simplefilter("ignore") return np.nanmin(a, axis=axis) def nanmax(a, axis=None): "Slow nanmax function used for unaccelerated dtypes." with warnings.catch_warnings(): warnings.simplefilter("ignore") return np.nanmax(a, axis=axis) def median(a, axis=None): "Slow median function used for unaccelerated dtypes." with warnings.catch_warnings(): warnings.simplefilter("ignore") return np.median(a, axis=axis) def nanmedian(a, axis=None): "Slow nanmedian function used for unaccelerated dtypes." with warnings.catch_warnings(): warnings.simplefilter("ignore") return np.nanmedian(a, axis=axis) def ss(a, axis=None): "Slow sum of squares used for unaccelerated dtypes." a = np.asarray(a) y = np.multiply(a, a).sum(axis) if y.dtype != a.dtype: y = y.astype(a.dtype) return y def anynan(a, axis=None): "Slow check for Nans used for unaccelerated dtypes." return np.isnan(a).any(axis) def allnan(a, axis=None): "Slow check for all Nans used for unaccelerated dtypes." return np.isnan(a).all(axis) Bottleneck-1.2.1/bottleneck/slow/move.py0000664000175000017500000001664313103166630017302 0ustar kgkg00000000000000"Alternative methods of calculating moving window statistics." import warnings import numpy as np __all__ = ['move_sum', 'move_mean', 'move_std', 'move_var', 'move_min', 'move_max', 'move_argmin', 'move_argmax', 'move_median', 'move_rank'] def move_sum(a, window, min_count=None, axis=-1): "Slow move_sum for unaccelerated dtype" return move_func(np.nansum, a, window, min_count, axis=axis) def move_mean(a, window, min_count=None, axis=-1): "Slow move_mean for unaccelerated dtype" return move_func(np.nanmean, a, window, min_count, axis=axis) def move_std(a, window, min_count=None, axis=-1, ddof=0): "Slow move_std for unaccelerated dtype" return move_func(np.nanstd, a, window, min_count, axis=axis, ddof=ddof) def move_var(a, window, min_count=None, axis=-1, ddof=0): "Slow move_var for unaccelerated dtype" return move_func(np.nanvar, a, window, min_count, axis=axis, ddof=ddof) def move_min(a, window, min_count=None, axis=-1): "Slow move_min for unaccelerated dtype" return move_func(np.nanmin, a, window, min_count, axis=axis) def move_max(a, window, min_count=None, axis=-1): "Slow move_max for unaccelerated dtype" return move_func(np.nanmax, a, window, min_count, axis=axis) def move_argmin(a, window, min_count=None, axis=-1): "Slow move_argmin for unaccelerated dtype" def argmin(a, axis): a = np.array(a, copy=False) flip = [slice(None)] * a.ndim flip[axis] = slice(None, None, -1) a = a[flip] # if tie, pick index of rightmost tie try: idx = np.nanargmin(a, axis=axis) except ValueError: # an all nan slice encountered a = a.copy() mask = np.isnan(a) np.copyto(a, np.inf, where=mask) idx = np.argmin(a, axis=axis).astype(np.float64) if idx.ndim == 0: idx = np.nan else: mask = np.all(mask, axis=axis) idx[mask] = np.nan return idx return move_func(argmin, a, window, min_count, axis=axis) def move_argmax(a, window, min_count=None, axis=-1): "Slow move_argmax for unaccelerated dtype" def argmax(a, axis): a = np.array(a, copy=False) flip = [slice(None)] * a.ndim flip[axis] = slice(None, None, -1) a = a[flip] # if tie, pick index of rightmost tie try: idx = np.nanargmax(a, axis=axis) except ValueError: # an all nan slice encountered a = a.copy() mask = np.isnan(a) np.copyto(a, -np.inf, where=mask) idx = np.argmax(a, axis=axis).astype(np.float64) if idx.ndim == 0: idx = np.nan else: mask = np.all(mask, axis=axis) idx[mask] = np.nan return idx return move_func(argmax, a, window, min_count, axis=axis) def move_median(a, window, min_count=None, axis=-1): "Slow move_median for unaccelerated dtype" return move_func(np.nanmedian, a, window, min_count, axis=axis) def move_rank(a, window, min_count=None, axis=-1): "Slow move_rank for unaccelerated dtype" return move_func(lastrank, a, window, min_count, axis=axis) # magic utility functions --------------------------------------------------- def move_func(func, a, window, min_count=None, axis=-1, **kwargs): "Generic moving window function implemented with a python loop." a = np.array(a, copy=False) if min_count is None: mc = window else: mc = min_count if mc > window: msg = "min_count (%d) cannot be greater than window (%d)" raise ValueError(msg % (mc, window)) elif mc <= 0: raise ValueError("`min_count` must be greater than zero.") if a.ndim == 0: raise ValueError("moving window functions require ndim > 0") if axis is None: raise ValueError("An `axis` value of None is not supported.") if window < 1: raise ValueError("`window` must be at least 1.") if window > a.shape[axis]: raise ValueError("`window` is too long.") if issubclass(a.dtype.type, np.inexact): y = np.empty_like(a) else: y = np.empty(a.shape) idx1 = [slice(None)] * a.ndim idx2 = list(idx1) with warnings.catch_warnings(): warnings.simplefilter("ignore") for i in range(a.shape[axis]): win = min(window, i + 1) idx1[axis] = slice(i + 1 - win, i + 1) idx2[axis] = i y[idx2] = func(a[idx1], axis=axis, **kwargs) idx = _mask(a, window, mc, axis) y[idx] = np.nan return y def _mask(a, window, min_count, axis): n = (a == a).cumsum(axis) idx1 = [slice(None)] * a.ndim idx2 = [slice(None)] * a.ndim idx3 = [slice(None)] * a.ndim idx1[axis] = slice(window, None) idx2[axis] = slice(None, -window) idx3[axis] = slice(None, window) nidx1 = n[idx1] nidx1 = nidx1 - n[idx2] idx = np.empty(a.shape, dtype=np.bool) idx[idx1] = nidx1 < min_count idx[idx3] = n[idx3] < min_count return idx # --------------------------------------------------------------------------- def lastrank(a, axis=-1): """ The ranking of the last element along the axis, ignoring NaNs. The ranking is normalized to be between -1 and 1 instead of the more common 1 and N. The results are adjusted for ties. Parameters ---------- a : ndarray Input array. If `a` is not an array, a conversion is attempted. axis : int, optional The axis over which to rank. By default (axis=-1) the ranking (and reducing) is performed over the last axis. Returns ------- d : array In the case of, for example, a 2d array of shape (n, m) and axis=1, the output will contain the rank (normalized to be between -1 and 1 and adjusted for ties) of the the last element of each row. The output in this example will have shape (n,). Examples -------- Create an array: >>> y1 = larry([1, 2, 3]) What is the rank of the last element (the value 3 in this example)? It is the largest element so the rank is 1.0: >>> import numpy as np >>> from la.afunc import lastrank >>> x1 = np.array([1, 2, 3]) >>> lastrank(x1) 1.0 Now let's try an example where the last element has the smallest value: >>> x2 = np.array([3, 2, 1]) >>> lastrank(x2) -1.0 Here's an example where the last element is not the minimum or maximum value: >>> x3 = np.array([1, 3, 4, 5, 2]) >>> lastrank(x3) -0.5 """ a = np.array(a, copy=False) ndim = a.ndim if a.size == 0: # At least one dimension has length 0 shape = list(a.shape) shape.pop(axis) r = np.empty(shape, dtype=a.dtype) r.fill(np.nan) if (r.ndim == 0) and (r.size == 1): r = np.nan return r indlast = [slice(None)] * ndim indlast[axis] = slice(-1, None) indlast2 = [slice(None)] * ndim indlast2[axis] = -1 n = (~np.isnan(a)).sum(axis) a_indlast = a[indlast] g = (a_indlast > a).sum(axis) e = (a_indlast == a).sum(axis) r = (g + g + e - 1.0) / 2.0 r = r / (n - 1.0) r = 2.0 * (r - 0.5) if ndim == 1: if n == 1: r = 0 if np.isnan(a[indlast2]): # elif? r = np.nan else: np.putmask(r, n == 1, 0) np.putmask(r, np.isnan(a[indlast2]), np.nan) return r Bottleneck-1.2.1/bottleneck/slow/nonreduce_axis.py0000664000175000017500000001170413103166630021333 0ustar kgkg00000000000000import numpy as np from numpy import partition, argpartition __all__ = ['rankdata', 'nanrankdata', 'partition', 'argpartition', 'push'] def rankdata(a, axis=None): "Slow rankdata function used for unaccelerated dtypes." return _rank(scipy_rankdata, a, axis) def nanrankdata(a, axis=None): "Slow nanrankdata function used for unaccelerated dtypes." return _rank(_nanrankdata_1d, a, axis) def _rank(func1d, a, axis): a = np.array(a, copy=False) if axis is None: a = a.ravel() axis = 0 if a.size == 0: y = a.astype(np.float64, copy=True) else: y = np.apply_along_axis(func1d, axis, a) if a.dtype != np.float64: y = y.astype(np.float64) return y def _nanrankdata_1d(a): y = np.empty(a.shape, dtype=np.float64) y.fill(np.nan) idx = ~np.isnan(a) y[idx] = scipy_rankdata(a[idx]) return y def push(a, n=np.inf, axis=-1): "Slow push used for unaccelerated dtypes." if axis is None: raise ValueError("`axis` cannot be None") y = np.array(a) ndim = y.ndim if axis != -1 or axis != ndim - 1: y = np.rollaxis(y, axis, ndim) if ndim == 1: y = y[None, :] elif ndim == 0: return y fidx = ~np.isnan(y) recent = np.empty(y.shape[:-1]) count = np.empty(y.shape[:-1]) recent.fill(np.nan) count.fill(np.nan) with np.errstate(invalid='ignore'): for i in range(y.shape[-1]): idx = (i - count) > n recent[idx] = np.nan idx = ~fidx[..., i] y[idx, i] = recent[idx] idx = fidx[..., i] count[idx] = i recent[idx] = y[idx, i] if axis != -1 or axis != ndim - 1: y = np.rollaxis(y, ndim - 1, axis) if ndim == 1: return y[0] return y # --------------------------------------------------------------------------- # # SciPy # # Local copy of SciPy's rankdata to avoid a SciPy dependency. The SciPy # license is included in the Bottleneck license file, which is distributed # with Bottleneck. # # Code taken from scipy master branch on Aug 31, 2016. def scipy_rankdata(a, method='average'): """ rankdata(a, method='average') Assign ranks to data, dealing with ties appropriately. Ranks begin at 1. The `method` argument controls how ranks are assigned to equal values. See [1]_ for further discussion of ranking methods. Parameters ---------- a : array_like The array of values to be ranked. The array is first flattened. method : str, optional The method used to assign ranks to tied elements. The options are 'average', 'min', 'max', 'dense' and 'ordinal'. 'average': The average of the ranks that would have been assigned to all the tied values is assigned to each value. 'min': The minimum of the ranks that would have been assigned to all the tied values is assigned to each value. (This is also referred to as "competition" ranking.) 'max': The maximum of the ranks that would have been assigned to all the tied values is assigned to each value. 'dense': Like 'min', but the rank of the next highest element is assigned the rank immediately after those assigned to the tied elements. 'ordinal': All values are given a distinct rank, corresponding to the order that the values occur in `a`. The default is 'average'. Returns ------- ranks : ndarray An array of length equal to the size of `a`, containing rank scores. References ---------- .. [1] "Ranking", http://en.wikipedia.org/wiki/Ranking Examples -------- >>> from scipy.stats import rankdata >>> rankdata([0, 2, 3, 2]) array([ 1. , 2.5, 4. , 2.5]) >>> rankdata([0, 2, 3, 2], method='min') array([ 1, 2, 4, 2]) >>> rankdata([0, 2, 3, 2], method='max') array([ 1, 3, 4, 3]) >>> rankdata([0, 2, 3, 2], method='dense') array([ 1, 2, 3, 2]) >>> rankdata([0, 2, 3, 2], method='ordinal') array([ 1, 2, 4, 3]) """ if method not in ('average', 'min', 'max', 'dense', 'ordinal'): raise ValueError('unknown method "{0}"'.format(method)) a = np.ravel(np.asarray(a)) algo = 'mergesort' if method == 'ordinal' else 'quicksort' sorter = np.argsort(a, kind=algo) inv = np.empty(sorter.size, dtype=np.intp) inv[sorter] = np.arange(sorter.size, dtype=np.intp) if method == 'ordinal': return inv + 1 a = a[sorter] obs = np.r_[True, a[1:] != a[:-1]] dense = obs.cumsum()[inv] if method == 'dense': return dense # cumulative counts of each unique value count = np.r_[np.nonzero(obs)[0], len(obs)] if method == 'max': return count[dense] if method == 'min': return count[dense - 1] + 1 # average method return .5 * (count[dense] + count[dense - 1] + 1) Bottleneck-1.2.1/bottleneck/slow/nonreduce.py0000664000175000017500000000121013103166630020276 0ustar kgkg00000000000000import numpy as np __all__ = ['replace'] def replace(a, old, new): "Slow replace (inplace) used for unaccelerated dtypes." if type(a) is not np.ndarray: raise TypeError("`a` must be a numpy array.") if not issubclass(a.dtype.type, np.inexact): if old != old: # int arrays do not contain NaN return if int(old) != old: raise ValueError("Cannot safely cast `old` to int.") if int(new) != new: raise ValueError("Cannot safely cast `new` to int.") if old != old: mask = np.isnan(a) else: mask = a == old np.putmask(a, mask, new) Bottleneck-1.2.1/bottleneck/slow/__init__.py0000664000175000017500000000025513103166630020063 0ustar kgkg00000000000000# flake8: noqa from bottleneck.slow.reduce import * from bottleneck.slow.nonreduce import * from bottleneck.slow.nonreduce_axis import * from bottleneck.slow.move import * Bottleneck-1.2.1/bottleneck/LICENSE0000664000175000017500000005236313103166630016002 0ustar kgkg00000000000000======= License ======= Bottleneck is distributed under a Simplified BSD license. Parts of NumPy, SciPy and and numpydoc, which all have BSD licenses, are included in Bottleneck. Bottleneck license ================== Copyright (c) 2010-2017 Keith Goodman All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. Other licenses ============== Bottleneck contains doc strings from NumPy and SciPy and Sphinx extensions from numpydoc. Bottleneck also contains the file ez_setup.py from setuptools. NumPy license ------------- Copyright (c) 2005-2014, NumPy Developers. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the NumPy Developers nor the names of any contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. itions of this License Agreement. SciPy license ------------- Copyright (c) 2001, 2002 Enthought, Inc. All rights reserved. Copyright (c) 2003-2014 SciPy Developers. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: a. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. b. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. c. Neither the name of the Enthought nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. numpydoc license ---------------- The numpydoc license is in bottleneck/doc/sphinxext/LICENSE.txt setuptools license ------------------ setuptools is dual-licensed under the Python Software Foundation (PSF) license and the Zope Public License (ZPL). Python Software Foundation License ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ A. HISTORY OF THE SOFTWARE """""""""""""""""""""""""" Python was created in the early 1990s by Guido van Rossum at Stichting Mathematisch Centrum (CWI, see http://www.cwi.nl) in the Netherlands as a successor of a language called ABC. Guido remains Python's principal author, although it includes many contributions from others. In 1995, Guido continued his work on Python at the Corporation for National Research Initiatives (CNRI, see http://www.cnri.reston.va.us) in Reston, Virginia where he released several versions of the software. In May 2000, Guido and the Python core development team moved to BeOpen.com to form the BeOpen PythonLabs team. In October of the same year, the PythonLabs team moved to Digital Creations (now Zope Corporation, see http://www.zope.com). In 2001, the Python Software Foundation (PSF, see http://www.python.org/psf/) was formed, a non-profit organization created specifically to own Python-related Intellectual Property. Zope Corporation is a sponsoring member of the PSF. All Python releases are Open Source (see http://www.opensource.org for the Open Source Definition). Historically, most, but not all, Python releases have also been GPL-compatible; the table below summarizes the various releases. Release Derived Year Owner GPL- from compatible? (1) 0.9.0 thru 1.2 1991-1995 CWI yes 1.3 thru 1.5.2 1.2 1995-1999 CNRI yes 1.6 1.5.2 2000 CNRI no 2.0 1.6 2000 BeOpen.com no 1.6.1 1.6 2001 CNRI yes (2) 2.1 2.0+1.6.1 2001 PSF no 2.0.1 2.0+1.6.1 2001 PSF yes 2.1.1 2.1+2.0.1 2001 PSF yes 2.2 2.1.1 2001 PSF yes 2.1.2 2.1.1 2002 PSF yes 2.1.3 2.1.2 2002 PSF yes 2.2.1 2.2 2002 PSF yes 2.2.2 2.2.1 2002 PSF yes 2.2.3 2.2.2 2003 PSF yes 2.3 2.2.2 2002-2003 PSF yes 2.3.1 2.3 2002-2003 PSF yes 2.3.2 2.3.1 2002-2003 PSF yes 2.3.3 2.3.2 2002-2003 PSF yes 2.3.4 2.3.3 2004 PSF yes 2.3.5 2.3.4 2005 PSF yes 2.4 2.3 2004 PSF yes 2.4.1 2.4 2005 PSF yes 2.4.2 2.4.1 2005 PSF yes 2.4.3 2.4.2 2006 PSF yes 2.4.4 2.4.3 2006 PSF yes 2.5 2.4 2006 PSF yes 2.5.1 2.5 2007 PSF yes 2.5.2 2.5.1 2008 PSF yes 2.5.3 2.5.2 2008 PSF yes 2.6 2.5 2008 PSF yes 2.6.1 2.6 2008 PSF yes 2.6.2 2.6.1 2009 PSF yes 2.6.3 2.6.2 2009 PSF yes 2.6.4 2.6.3 2009 PSF yes 2.6.5 2.6.4 2010 PSF yes Footnotes: (1) GPL-compatible doesn't mean that we're distributing Python under the GPL. All Python licenses, unlike the GPL, let you distribute a modified version without making your changes open source. The GPL-compatible licenses make it possible to combine Python with other software that is released under the GPL; the others don't. (2) According to Richard Stallman, 1.6.1 is not GPL-compatible, because its license has a choice of law clause. According to CNRI, however, Stallman's lawyer has told CNRI's lawyer that 1.6.1 is "not incompatible" with the GPL. Thanks to the many outside volunteers who have worked under Guido's direction to make these releases possible. B. TERMS AND CONDITIONS FOR ACCESSING OR OTHERWISE USING PYTHON """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2 ............................................ 1. This LICENSE AGREEMENT is between the Python Software Foundation ("PSF"), and the Individual or Organization ("Licensee") accessing and otherwise using this software ("Python") in source or binary form and its associated documentation. 2. Subject to the terms and conditions of this License Agreement, PSF hereby grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce, analyze, test, perform and/or display publicly, prepare derivative works, distribute, and otherwise use Python alone or in any derivative version, provided, however, that PSF's License Agreement and PSF's notice of copyright, i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Python Software Foundation; All Rights Reserved" are retained in Python alone or in any derivative version prepared by Licensee. 3. In the event Licensee prepares a derivative work that is based on or incorporates Python or any part thereof, and wants to make the derivative work available to others as provided herein, then Licensee hereby agrees to include in any such work a brief summary of the changes made to Python. 4. PSF is making Python available to Licensee on an "AS IS" basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT INFRINGE ANY THIRD PARTY RIGHTS. 5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON, OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. 6. This License Agreement will automatically terminate upon a material breach of its terms and conditions. 7. Nothing in this License Agreement shall be deemed to create any relationship of agency, partnership, or joint venture between PSF and Licensee. This License Agreement does not grant permission to use PSF trademarks or trade name in a trademark sense to endorse or promote products or services of Licensee, or any third party. 8. By copying, installing or otherwise using Python, Licensee agrees to be bound by the terms and conditions of this License Agreement. BEOPEN.COM LICENSE AGREEMENT FOR PYTHON 2.0 ........................................... BEOPEN PYTHON OPEN SOURCE LICENSE AGREEMENT VERSION 1 1. This LICENSE AGREEMENT is between BeOpen.com ("BeOpen"), having an office at 160 Saratoga Avenue, Santa Clara, CA 95051, and the Individual or Organization ("Licensee") accessing and otherwise using this software in source or binary form and its associated documentation ("the Software"). 2. Subject to the terms and conditions of this BeOpen Python License Agreement, BeOpen hereby grants Licensee a non-exclusive, royalty-free, world-wide license to reproduce, analyze, test, perform and/or display publicly, prepare derivative works, distribute, and otherwise use the Software alone or in any derivative version, provided, however, that the BeOpen Python License is retained in the Software, alone or in any derivative version prepared by Licensee. 3. BeOpen is making the Software available to Licensee on an "AS IS" basis. BEOPEN MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, BEOPEN MAKES NO AND DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE WILL NOT INFRINGE ANY THIRD PARTY RIGHTS. 4. BEOPEN SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF THE SOFTWARE FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THE SOFTWARE, OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. 5. This License Agreement will automatically terminate upon a material breach of its terms and conditions. 6. This License Agreement shall be governed by and interpreted in all respects by the law of the State of California, excluding conflict of law provisions. Nothing in this License Agreement shall be deemed to create any relationship of agency, partnership, or joint venture between BeOpen and Licensee. This License Agreement does not grant permission to use BeOpen trademarks or trade names in a trademark sense to endorse or promote products or services of Licensee, or any third party. As an exception, the "BeOpen Python" logos available at http://www.pythonlabs.com/logos.html may be used according to the permissions granted on that web page. 7. By copying, installing or otherwise using the software, Licensee agrees to be bound by the terms and conditions of this License Agreement. CNRI LICENSE AGREEMENT FOR PYTHON 1.6.1 ....................................... 1. This LICENSE AGREEMENT is between the Corporation for National Research Initiatives, having an office at 1895 Preston White Drive, Reston, VA 20191 ("CNRI"), and the Individual or Organization ("Licensee") accessing and otherwise using Python 1.6.1 software in source or binary form and its associated documentation. 2. Subject to the terms and conditions of this License Agreement, CNRI hereby grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce, analyze, test, perform and/or display publicly, prepare derivative works, distribute, and otherwise use Python 1.6.1 alone or in any derivative version, provided, however, that CNRI's License Agreement and CNRI's notice of copyright, i.e., "Copyright (c) 1995-2001 Corporation for National Research Initiatives; All Rights Reserved" are retained in Python 1.6.1 alone or in any derivative version prepared by Licensee. Alternately, in lieu of CNRI's License Agreement, Licensee may substitute the following text (omitting the quotes): "Python 1.6.1 is made available subject to the terms and conditions in CNRI's License Agreement. This Agreement together with Python 1.6.1 may be located on the Internet using the following unique, persistent identifier (known as a handle): 1895.22/1013. This Agreement may also be obtained from a proxy server on the Internet using the following URL: http://hdl.handle.net/1895.22/1013". 3. In the event Licensee prepares a derivative work that is based on or incorporates Python 1.6.1 or any part thereof, and wants to make the derivative work available to others as provided herein, then Licensee hereby agrees to include in any such work a brief summary of the changes made to Python 1.6.1. 4. CNRI is making Python 1.6.1 available to Licensee on an "AS IS" basis. CNRI MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, CNRI MAKES NO AND DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON 1.6.1 WILL NOT INFRINGE ANY THIRD PARTY RIGHTS. 5. CNRI SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON 1.6.1 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 1.6.1, OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. 6. This License Agreement will automatically terminate upon a material breach of its terms and conditions. 7. This License Agreement shall be governed by the federal intellectual property law of the United States, including without limitation the federal copyright law, and, to the extent such U.S. federal law does not apply, by the law of the Commonwealth of Virginia, excluding Virginia's conflict of law provisions. Notwithstanding the foregoing, with regard to derivative works based on Python 1.6.1 that incorporate non-separable material that was previously distributed under the GNU General Public License (GPL), the law of the Commonwealth of Virginia shall govern this License Agreement only as to issues arising under or with respect to Paragraphs 4, 5, and 7 of this License Agreement. Nothing in this License Agreement shall be deemed to create any relationship of agency, partnership, or joint venture between CNRI and Licensee. This License Agreement does not grant permission to use CNRI trademarks or trade name in a trademark sense to endorse or promote products or services of Licensee, or any third party. 8. By clicking on the "ACCEPT" button where indicated, or by copying, installing or otherwise using Python 1.6.1, Licensee agrees to be bound by the terms and conditions of this License Agreement. ACCEPT CWI LICENSE AGREEMENT FOR PYTHON 0.9.0 THROUGH 1.2 .................................................. Copyright (c) 1991 - 1995, Stichting Mathematisch Centrum Amsterdam, The Netherlands. All rights reserved. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation, and that the name of Stichting Mathematisch Centrum or CWI not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. Zope Public License ^^^^^^^^^^^^^^^^^^^ This software is Copyright (c) Zope Corporation (tm) and Contributors. All rights reserved. This license has been certified as open source. It has also been designated as GPL compatible by the Free Software Foundation (FSF). Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions in source code must retain the above copyright notice, this list of conditions, and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions, and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The name Zope Corporation (tm) must not be used to endorse or promote products derived from this software without prior written permission from Zope Corporation. 4. The right to distribute this software or to use it for any purpose does not give you the right to use Servicemarks (sm) or Trademarks (tm) of Zope Corporation. Use of them is covered in a separate agreement (see http://www.zope.com/Marks). 5. If any files are modified, you must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. Disclaimer THIS SOFTWARE IS PROVIDED BY ZOPE CORPORATION ''AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ZOPE CORPORATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. This software consists of contributions made by Zope Corporation and many individuals on behalf of Zope Corporation. Specific attributions are listed in the accompanying credits file. Bottleneck-1.2.1/bottleneck/version.py0000664000175000017500000000005413106353464017030 0ustar kgkg00000000000000"Bottleneck version" __version__ = "1.2.1" Bottleneck-1.2.1/bottleneck/__init__.py0000664000175000017500000000245213103166630017100 0ustar kgkg00000000000000# flake8: noqa # If you bork the build (e.g. by messing around with the templates), # you still want to be able to import Bottleneck so that you can # rebuild using the templates. So try to import the compiled Bottleneck # functions to the top level, but move on if not successful. try: from .reduce import (nansum, nanmean, nanstd, nanvar, nanmin, nanmax, median, nanmedian, ss, nanargmin, nanargmax, anynan, allnan) except: pass try: from .nonreduce import replace except: pass try: from .nonreduce_axis import (partition, argpartition, rankdata, nanrankdata, push) except: pass try: from .move import (move_sum, move_mean, move_std, move_var, move_min, move_max, move_argmin, move_argmax, move_median, move_rank) except: pass try: from . import slow from bottleneck.version import __version__ from bottleneck.benchmark.bench import bench from bottleneck.benchmark.bench_detailed import bench_detailed from bottleneck.tests.util import get_functions except: pass try: from numpy.testing import Tester test = Tester().test del Tester except (ImportError, ValueError): print("No Bottleneck unit testing available.") Bottleneck-1.2.1/bottleneck/benchmark/0000775000175000017500000000000013106356012016714 5ustar kgkg00000000000000Bottleneck-1.2.1/bottleneck/benchmark/autotimeit.py0000664000175000017500000000076712161161230021460 0ustar kgkg00000000000000 import timeit def autotimeit(stmt, setup='pass', repeat=3, mintime=0.2): timer = timeit.Timer(stmt, setup) number, time1 = autoscaler(timer, mintime) time2 = timer.repeat(repeat=repeat-1, number=number) return min(time2 + [time1]) / number def autoscaler(timer, mintime): number = 1 for i in range(12): time = timer.timeit(number) if time > mintime: return number, time number *= 10 raise RuntimeError('function is too fast to test') Bottleneck-1.2.1/bottleneck/benchmark/bench.py0000664000175000017500000001523313103166630020353 0ustar kgkg00000000000000 import numpy as np import bottleneck as bn from .autotimeit import autotimeit __all__ = ['bench'] def bench(shapes=[(100,), (1000, 1000), (1000, 1000), (1000, 1000), (1000, 1000)], axes=[0, 0, 0, 1, 1], nans=[False, False, True, False, True], dtype='float64', order='C', functions=None): """ Bottleneck benchmark. Parameters ---------- shapes : list, optional A list of tuple shapes of input arrays to use in the benchmark. axes : list, optional List of axes along which to perform the calculations that are being benchmarked. nans : list, optional A list of the bools (True or False), one for each tuple in the `shapes` list, that tells whether the input arrays should be randomly filled with one-fifth NaNs. dtype : str, optional Data type string such as 'float64', which is the default. order : {'C', 'F'}, optional Whether to store multidimensional data in C- or Fortran-contiguous (row- or column-wise) order in memory. functions : {list, None}, optional A list of strings specifying which functions to include in the benchmark. By default (None) all functions are included in the benchmark. Returns ------- A benchmark report is printed to stdout. """ if len(shapes) != len(nans): raise ValueError("`shapes` and `nans` must have the same length") if len(shapes) != len(axes): raise ValueError("`shapes` and `axes` must have the same length") # Header print('Bottleneck performance benchmark') print(" Bottleneck %s; Numpy %s" % (bn.__version__, np.__version__)) print(" Speed is NumPy time divided by Bottleneck time") print(" NaN means approx one-fifth NaNs; %s used" % str(dtype)) print('') header = [" "*11] for nan in nans: if nan: header.append("NaN".center(11)) else: header.append("no NaN".center(11)) print("".join(header)) header = ["".join(str(shape).split(" ")).center(11) for shape in shapes] header = [" "*12] + header print("".join(header)) header = ["".join(("axis=" + str(axis)).split(" ")).center(11) for axis in axes] header = [" "*12] + header print("".join(header)) suite = benchsuite(shapes, dtype, nans, axes, order, functions) for test in suite: name = test["name"].ljust(12) fmt = name + "%7.1f" + "%11.1f"*(len(shapes) - 1) speed = timer(test['statements'], test['setups']) print(fmt % tuple(speed)) def timer(statements, setups): speed = [] if len(statements) != 2: raise ValueError("Two statements needed.") for setup in setups: with np.errstate(invalid='ignore'): t0 = autotimeit(statements[0], setup) t1 = autotimeit(statements[1], setup) speed.append(t1 / t0) return speed def getarray(shape, dtype, nans, order): a = np.arange(np.prod(shape), dtype=dtype) if nans and issubclass(a.dtype.type, np.inexact): a[::5] = np.nan rs = np.random.RandomState(shape) rs.shuffle(a) return np.array(a.reshape(*shape), order=order) def benchsuite(shapes, dtype, nans, axes, order, functions): suite = [] def getsetups(setup, shapes, nans, axes, dtype, order): template = """ from bottleneck.benchmark.bench import getarray a = getarray(%s, '%s', %s, '%s') axis=%s %s""" setups = [] for shape, axis, nan in zip(shapes, axes, nans): s = template % (str(shape), str(dtype), str(nan), str(order), str(axis), setup) s = '\n'.join([line.strip() for line in s.split('\n')]) setups.append(s) return setups # non-moving window functions funcs = bn.get_functions("reduce", as_string=True) funcs += ['rankdata', 'nanrankdata'] for func in funcs: if functions is not None and func not in functions: continue run = {} run['name'] = func run['statements'] = ["bn_func(a, axis)", "sl_func(a, axis)"] setup = """ from bottleneck import %s as bn_func try: from numpy import %s as sl_func except ImportError: from bottleneck.slow import %s as sl_func if "%s" == "median": from bottleneck.slow import median as sl_func """ % (func, func, func, func) run['setups'] = getsetups(setup, shapes, nans, axes, dtype, order) suite.append(run) # partition, argpartition funcs = ['partition', 'argpartition'] for func in funcs: if functions is not None and func not in functions: continue run = {} run['name'] = func run['statements'] = ["bn_func(a, n, axis)", "sl_func(a, n, axis)"] setup = """ from bottleneck import %s as bn_func from bottleneck.slow import %s as sl_func if axis is None: n = a.size else: n = a.shape[axis] - 1 n = max(n / 2, 0) """ % (func, func) run['setups'] = getsetups(setup, shapes, nans, axes, dtype, order) suite.append(run) # replace, push funcs = ['replace', 'push'] for func in funcs: if functions is not None and func not in functions: continue run = {} run['name'] = func if func == 'replace': run['statements'] = ["bn_func(a, nan, 0)", "slow_func(a, nan, 0)"] elif func == 'push': run['statements'] = ["bn_func(a, 5, axis)", "slow_func(a, 5, axis)"] else: raise ValueError('Unknow function name') setup = """ from numpy import nan from bottleneck import %s as bn_func from bottleneck.slow import %s as slow_func """ % (func, func) run['setups'] = getsetups(setup, shapes, nans, axes, dtype, order) suite.append(run) # moving window functions funcs = bn.get_functions('move', as_string=True) for func in funcs: if functions is not None and func not in functions: continue run = {} run['name'] = func run['statements'] = ["bn_func(a, w, 1, axis)", "sw_func(a, w, 1, axis)"] setup = """ from bottleneck.slow.move import %s as sw_func from bottleneck import %s as bn_func w = a.shape[axis] // 5 """ % (func, func) run['setups'] = getsetups(setup, shapes, nans, axes, dtype, order) suite.append(run) return suite Bottleneck-1.2.1/bottleneck/benchmark/bench_detailed.py0000664000175000017500000001462213103166630022207 0ustar kgkg00000000000000 import numpy as np import bottleneck as bn from .autotimeit import autotimeit __all__ = ['bench_detailed'] def bench_detailed(function='nansum', fraction_nan=0.0): """ Benchmark a single function in detail or, optionally, all functions. Parameters ---------- function : str, optional Name of function, as a string, to benchmark. Default ('nansum') is to benchmark bn.nansum. If `function` is 'all' then detailed benchmarks are run on all bottleneck functions. fraction_nan : float, optional Fraction of array elements that should, on average, be NaN. The default (0.0) is not to set any elements to NaN. Returns ------- A benchmark report is printed to stdout. """ if function == 'all': # benchmark all bottleneck functions funcs = bn.get_functions('all', as_string=True) funcs.sort() for func in funcs: bench_detailed(func, fraction_nan) if fraction_nan < 0 or fraction_nan > 1: raise ValueError("`fraction_nan` must be between 0 and 1, inclusive") tab = ' ' # Header print('%s benchmark' % function) print("%sBottleneck %s; Numpy %s" % (tab, bn.__version__, np.__version__)) print("%sSpeed is NumPy time divided by Bottleneck time" % tab) if fraction_nan == 0: print("%sNone of the array elements are NaN" % tab) else: print("%s%.1f%% of the array elements are NaN (on average)" % (tab, fraction_nan * 100)) print("") print(" Speed Call Array") suite = benchsuite(function, fraction_nan) for test in suite: name = test["name"] speed = timer(test['statements'], test['setup'], test['repeat']) print("%8.1f %s %s" % (speed, name[0].ljust(27), name[1])) def timer(statements, setup, repeat): if len(statements) != 2: raise ValueError("Two statements needed.") with np.errstate(invalid='ignore'): t0 = autotimeit(statements[0], setup, repeat=repeat) t1 = autotimeit(statements[1], setup, repeat=repeat) speed = t1 / t0 return speed def benchsuite(function, fraction_nan): # setup is called before each run of each function setup = """ from bottleneck import %s as bn_fn try: from numpy import %s as sl_fn except ImportError: from bottleneck.slow import %s as sl_fn # avoid all-nan slice warnings from np.median and np.nanmedian if "%s" == "median": from bottleneck.slow import median as sl_fn if "%s" == "nanmedian": from bottleneck.slow import nanmedian as sl_fn from numpy import array, nan from numpy.random import RandomState rand = RandomState(123).rand a = %s if %s != 0: a[a < %s] = nan """ setup = '\n'.join([s.strip() for s in setup.split('\n')]) # what kind of function signature do we need to use? if function in bn.get_functions('reduce', as_string=True): index = 0 elif function in ['rankdata', 'nanrankdata']: index = 0 elif function in bn.get_functions('move', as_string=True): index = 1 elif function in ['partition', 'argpartition', 'push']: index = 2 elif function == 'replace': index = 3 else: raise ValueError("`function` (%s) not recognized" % function) # create benchmark suite instructions = get_instructions() f = function suite = [] for instruction in instructions: signature = instruction[index + 1] if signature is None: continue array = instruction[0] repeat = instruction[-1] run = {} run['name'] = [f + signature, array] run['statements'] = ["bn_fn" + signature, "sl_fn" + signature] run['setup'] = setup % (f, f, f, f, f, array, fraction_nan, fraction_nan) run['repeat'] = repeat suite.append(run) return suite def get_instructions(): instructions = [ # 1d input array ("rand(1)", "(a)", # reduce + (nan)rankdata "(a, 1)", # move "(a, 0)", # (arg)partition "(a, np.nan, 0)", # replace 10), ("rand(10)", "(a)", "(a, 2)", "(a, 2)", "(a, np.nan, 0)", 10), ("rand(100)", "(a)", "(a, 20)", "(a, 20)", "(a, np.nan, 0)", 6), ("rand(1000)", "(a)", "(a, 200)", "(a, 200)", "(a, np.nan, 0)", 3), ("rand(1000000)", "(a)", "(a, 200)", "(a, 200)", "(a, np.nan, 0)", 2), # 2d input array ("rand(10, 10)", "(a)", "(a, 2)", "(a, 2)", "(a, np.nan, 0)", 6), ("rand(100, 100)", "(a)", "(a, 20)", "(a, 20)", "(a, np.nan, 0)", 3), ("rand(1000, 1000)", "(a)", "(a, 200)", "(a, 200)", "(a, np.nan, 0)", 2), ("rand(10, 10)", "(a, 1)", None, None, None, 6), ("rand(100, 100)", "(a, 1)", None, None, None, 3), ("rand(1000, 1000)", "(a, 1)", None, None, None, 2), ("rand(100000, 2)", "(a, 1)", "(a, 1)", "(a, 1)", None, 2), ("rand(10, 10)", "(a, 0)", None, None, None, 6), ("rand(100, 100)", "(a, 0)", "(a, 20, axis=0)", None, None, 3), ("rand(1000, 1000)", "(a, 0)", "(a, 200, axis=0)", None, None, 2), # 3d input array ("rand(100, 100, 100)", "(a, 0)", "(a, 20, axis=0)", "(a, 20, axis=0)", None, 2), ("rand(100, 100, 100)", "(a, 1)", "(a, 20, axis=1)", "(a, 20, axis=1)", None, 2), ("rand(100, 100, 100)", "(a, 2)", "(a, 20, axis=2)", "(a, 20, axis=2)", "(a, np.nan, 0)", 2), # 0d input array ("array(1.0)", "(a)", None, None, "(a, 0, 2)", 10), ] return instructions Bottleneck-1.2.1/bottleneck/benchmark/__init__.py0000664000175000017500000000000011747525210021022 0ustar kgkg00000000000000Bottleneck-1.2.1/bottleneck/tests/0000775000175000017500000000000013106356012016124 5ustar kgkg00000000000000Bottleneck-1.2.1/bottleneck/tests/util.py0000664000175000017500000001345713106351713017470 0ustar kgkg00000000000000import numpy as np import bottleneck as bn DTYPES = [np.float64, np.float32, np.int64, np.int32] def get_functions(module_name, as_string=False): "Returns a list of functions, optionally as string function names" if module_name == 'all': funcs = [] funcs_in_dict = func_dict() for key in funcs_in_dict: for func in funcs_in_dict[key]: funcs.append(func) else: funcs = func_dict()[module_name] if as_string: funcs = [f.__name__ for f in funcs] return funcs def func_dict(): d = {} d['reduce'] = [bn.nansum, bn.nanmean, bn.nanstd, bn.nanvar, bn.nanmin, bn.nanmax, bn.median, bn.nanmedian, bn.ss, bn.nanargmin, bn.nanargmax, bn.anynan, bn.allnan, ] d['move'] = [bn.move_sum, bn.move_mean, bn.move_std, bn.move_var, bn.move_min, bn.move_max, bn.move_argmin, bn.move_argmax, bn.move_median, bn.move_rank, ] d['nonreduce'] = [bn.replace] d['nonreduce_axis'] = [bn.partition, bn.argpartition, bn.rankdata, bn.nanrankdata, bn.push, ] return d # --------------------------------------------------------------------------- def arrays(func_name, dtypes=DTYPES): return array_iter(array_generator, func_name, dtypes) def array_iter(arrays_func, *args): for a in arrays_func(*args): if a.ndim < 2: yield a # this is good for an extra check but in everyday development it # is a pain because it doubles the unit test run time # elif a.ndim == 3: # for axes in permutations(range(a.ndim)): # yield np.transpose(a, axes) else: yield a yield a.T def array_generator(func_name, dtypes): "Iterator that yields arrays to use for unit testing." # define nan and inf if func_name in ('partition', 'argpartition'): nan = 0 else: nan = np.nan if func_name in ('move_sum', 'move_mean', 'move_std', 'move_var'): # these functions can't handle inf inf = 8 else: inf = np.inf # nan and inf yield np.array([inf, nan]) yield np.array([inf, -inf]) yield np.array([nan, 2, 3]) yield np.array([-inf, 2, 3]) if func_name != 'nanargmin': yield np.array([nan, inf]) # byte swapped yield np.array([1, 2, 3], dtype='>f4') yield np.array([1, 2, 3], dtype=' 3: continue for axis in list(range(-1, a.ndim)) + [None]: if axis is None: nmax = a.size - 1 else: nmax = a.shape[axis] - 1 if nmax < 1: continue n = rs.randint(nmax) s0 = func0(a, n, axis) s1 = func(a, n, axis) if name == 'argpartition': s0 = complete_the_argpartition(s0, a, n, axis) s1 = complete_the_argpartition(s1, a, n, axis) else: s0 = complete_the_partition(s0, n, axis) s1 = complete_the_partition(s1, n, axis) tup = (name, 'a'+str(i), str(a.dtype), str(a.shape), n, str(axis), array_order(a), a) err_msg = msg % tup assert_array_equal(s1, s0, err_msg) def complete_the_partition(a, n, axis): def func1d(a, n): a[:n] = np.sort(a[:n]) a[n+1:] = np.sort(a[n+1:]) return a a = a.copy() ndim = a.ndim if axis is None: if ndim != 1: raise ValueError("`a` must be 1d when axis is None") axis = 0 elif axis < 0: axis += ndim if axis < 0: raise ValueError("`axis` out of range") a = np.apply_along_axis(func1d, axis, a, n) return a def complete_the_argpartition(index, a, n, axis): a = a.copy() ndim = a.ndim if axis is None: if index.ndim != 1: raise ValueError("`index` must be 1d when axis is None") axis = 0 ndim = 1 a = a.reshape(-1) elif axis < 0: axis += ndim if axis < 0: raise ValueError("`axis` out of range") if ndim == 1: a = a[index] elif ndim == 2: if axis == 0: for i in range(a.shape[1]): a[:, i] = a[index[:, i], i] elif axis == 1: for i in range(a.shape[0]): a[i] = a[i, index[i]] else: raise ValueError("`axis` out of range") elif ndim == 3: if axis == 0: for i in range(a.shape[1]): for j in range(a.shape[2]): a[:, i, j] = a[index[:, i, j], i, j] elif axis == 1: for i in range(a.shape[0]): for j in range(a.shape[2]): a[i, :, j] = a[i, index[i, :, j], j] elif axis == 2: for i in range(a.shape[0]): for j in range(a.shape[1]): a[i, j, :] = a[i, j, index[i, j, :]] else: raise ValueError("`axis` out of range") else: raise ValueError("`a.ndim` must be 1, 2, or 3") a = complete_the_partition(a, n, axis) return a def test_transpose(): "partition transpose test" a = np.arange(12).reshape(4, 3) actual = bn.partition(a.T, 2, -1).T desired = bn.slow.partition(a.T, 2, -1).T assert_equal(actual, desired, 'partition transpose test') # --------------------------------------------------------------------------- # rankdata, nanrankdata, push def test_nonreduce_axis(): "Test nonreduce axis functions" funcs = [bn.rankdata, bn.nanrankdata, bn.push] for func in funcs: yield reduce_unit_maker, func def test_push(): "Test push" ns = (0, 1, 2, 3, 4, 5) a = np.array([np.nan, 1, 2, np.nan, np.nan, np.nan, np.nan, 3, np.nan]) for n in ns: actual = bn.push(a.copy(), n=n) desired = bn.slow.push(a.copy(), n=n) assert_array_equal(actual, desired, "failed on n=%s" % str(n)) # --------------------------------------------------------------------------- # Test argument parsing def test_arg_parsing(): "test argument parsing in nonreduce_axis" for func in bn.get_functions('nonreduce_axis'): name = func.__name__ if name in ('partition', 'argpartition'): yield unit_maker_parse, func elif name in ('push'): yield unit_maker_parse, func elif name in ('rankdata', 'nanrankdata'): yield unit_maker_parse_rankdata, func else: fmt = "``%s` is an unknown nonreduce_axis function" raise ValueError(fmt % name) yield unit_maker_raises, func def unit_maker_parse(func, decimal=5): "test argument parsing." name = func.__name__ func0 = eval('bn.slow.%s' % name) a = np.array([1., 2, 3]) fmt = '\n%s' % func fmt += '%s\n' fmt += '\nInput array:\n%s\n' % a actual = func(a, 1) desired = func0(a, 1) err_msg = fmt % "(a, 1)" assert_array_almost_equal(actual, desired, decimal, err_msg) actual = func(a, 1, axis=0) desired = func0(a, 1, axis=0) err_msg = fmt % "(a, 1, axis=0)" assert_array_almost_equal(actual, desired, decimal, err_msg) if name != 'push': actual = func(a, 2, None) desired = func0(a, 2, None) err_msg = fmt % "(a, 2, None)" assert_array_almost_equal(actual, desired, decimal, err_msg) actual = func(a, 1, axis=None) desired = func0(a, 1, axis=None) err_msg = fmt % "(a, 1, axis=None)" assert_array_almost_equal(actual, desired, decimal, err_msg) # regression test: make sure len(kwargs) == 0 doesn't raise args = (a, 1, -1) kwargs = {} func(*args, **kwargs) else: # regression test: make sure len(kwargs) == 0 doesn't raise args = (a, 1) kwargs = {} func(*args, **kwargs) def unit_maker_raises(func): "test argument parsing raises in nonreduce_axis" a = np.array([1., 2, 3]) assert_raises(TypeError, func) assert_raises(TypeError, func, axis=a) assert_raises(TypeError, func, a, axis=0, extra=0) assert_raises(TypeError, func, a, axis=0, a=a) if func.__name__ in ('partition', 'argpartition'): assert_raises(TypeError, func, a, 0, 0, 0, 0, 0) assert_raises(TypeError, func, a, axis='0') Bottleneck-1.2.1/bottleneck/tests/__init__.py0000664000175000017500000000000012504304115020221 0ustar kgkg00000000000000Bottleneck-1.2.1/bottleneck/tests/input_modifcation_test.py0000664000175000017500000000414513103166630023256 0ustar kgkg00000000000000"Test functions." import warnings import numpy as np from numpy.testing import assert_equal import bottleneck as bn from .util import DTYPES def arrays(dtypes): "Iterator that yield arrays to use for unit testing." ss = {} ss[1] = {'size': 4, 'shapes': [(4,)]} ss[2] = {'size': 6, 'shapes': [(2, 3)]} ss[3] = {'size': 6, 'shapes': [(1, 2, 3)]} rs = np.random.RandomState([1, 2, 3]) for ndim in ss: size = ss[ndim]['size'] shapes = ss[ndim]['shapes'] for dtype in dtypes: a = np.arange(size, dtype=dtype) if issubclass(a.dtype.type, np.inexact): idx = rs.rand(*a.shape) < 0.2 a[idx] = np.inf idx = rs.rand(*a.shape) < 0.2 a[idx] = np.nan idx = rs.rand(*a.shape) < 0.2 a[idx] *= -1 for shape in shapes: a = a.reshape(shape) yield a def unit_maker(func, nans=True): "Test that bn.xxx gives the same output as np.xxx." msg = "\nInput array modifed by %s.\n\n" msg += "input array before:\n%s\nafter:\n%s\n" for i, a in enumerate(arrays(DTYPES)): for axis in list(range(-a.ndim, a.ndim)) + [None]: with np.errstate(invalid='ignore'): a1 = a.copy() a2 = a.copy() if ('move_' in func.__name__) or ('sort' in func.__name__): if axis is None: continue with warnings.catch_warnings(): warnings.simplefilter("ignore") func(a1, 1, axis=axis) else: try: with warnings.catch_warnings(): warnings.simplefilter("ignore") func(a1, axis=axis) except: continue assert_equal(a1, a2, msg % (func.__name__, a1, a2)) def test_modification(): "Test for illegal inplace modification of input array" for func in bn.get_functions('all'): yield unit_maker, func Bottleneck-1.2.1/bottleneck/tests/move_test.py0000664000175000017500000001673113103166630020515 0ustar kgkg00000000000000"Test moving window functions." from nose.tools import assert_true import numpy as np from numpy.testing import (assert_equal, assert_array_almost_equal, assert_raises) import bottleneck as bn from .util import arrays, array_order def test_move(): "test move functions" for func in bn.get_functions('move'): yield unit_maker, func def unit_maker(func): "Test that bn.xxx gives the same output as a reference function." fmt = ('\nfunc %s | window %d | min_count %s | input %s (%s) | shape %s | ' 'axis %s | order %s\n') fmt += '\nInput array:\n%s\n' aaae = assert_array_almost_equal func_name = func.__name__ func0 = eval('bn.slow.%s' % func_name) if func_name == "move_var": decimal = 3 else: decimal = 5 for i, a in enumerate(arrays(func_name)): axes = range(-1, a.ndim) for axis in axes: windows = range(1, a.shape[axis]) for window in windows: min_counts = list(range(1, window + 1)) + [None] for min_count in min_counts: actual = func(a, window, min_count, axis=axis) desired = func0(a, window, min_count, axis=axis) tup = (func_name, window, str(min_count), 'a'+str(i), str(a.dtype), str(a.shape), str(axis), array_order(a), a) err_msg = fmt % tup aaae(actual, desired, decimal, err_msg) err_msg += '\n dtype mismatch %s %s' da = actual.dtype dd = desired.dtype assert_equal(da, dd, err_msg % (da, dd)) # --------------------------------------------------------------------------- # Test argument parsing def test_arg_parsing(): "test argument parsing" for func in bn.get_functions('move'): yield unit_maker_argparse, func def unit_maker_argparse(func, decimal=5): "test argument parsing." name = func.__name__ func0 = eval('bn.slow.%s' % name) a = np.array([1., 2, 3]) fmt = '\n%s' % func fmt += '%s\n' fmt += '\nInput array:\n%s\n' % a actual = func(a, 2) desired = func0(a, 2) err_msg = fmt % "(a, 2)" assert_array_almost_equal(actual, desired, decimal, err_msg) actual = func(a, 2, 1) desired = func0(a, 2, 1) err_msg = fmt % "(a, 2, 1)" assert_array_almost_equal(actual, desired, decimal, err_msg) actual = func(a, window=2) desired = func0(a, window=2) err_msg = fmt % "(a, window=2)" assert_array_almost_equal(actual, desired, decimal, err_msg) actual = func(a, window=2, min_count=1) desired = func0(a, window=2, min_count=1) err_msg = fmt % "(a, window=2, min_count=1)" assert_array_almost_equal(actual, desired, decimal, err_msg) actual = func(a, window=2, min_count=1, axis=0) desired = func0(a, window=2, min_count=1, axis=0) err_msg = fmt % "(a, window=2, min_count=1, axis=0)" assert_array_almost_equal(actual, desired, decimal, err_msg) actual = func(a, min_count=1, window=2, axis=0) desired = func0(a, min_count=1, window=2, axis=0) err_msg = fmt % "(a, min_count=1, window=2, axis=0)" assert_array_almost_equal(actual, desired, decimal, err_msg) actual = func(a, axis=-1, min_count=None, window=2) desired = func0(a, axis=-1, min_count=None, window=2) err_msg = fmt % "(a, axis=-1, min_count=None, window=2)" assert_array_almost_equal(actual, desired, decimal, err_msg) actual = func(a=a, axis=-1, min_count=None, window=2) desired = func0(a=a, axis=-1, min_count=None, window=2) err_msg = fmt % "(a=a, axis=-1, min_count=None, window=2)" assert_array_almost_equal(actual, desired, decimal, err_msg) if name in ('move_std', 'move_var'): actual = func(a, 2, 1, -1, ddof=1) desired = func0(a, 2, 1, -1, ddof=1) err_msg = fmt % "(a, 2, 1, -1, ddof=1)" assert_array_almost_equal(actual, desired, decimal, err_msg) # regression test: make sure len(kwargs) == 0 doesn't raise args = (a, 1, 1, -1) kwargs = {} func(*args, **kwargs) def test_arg_parse_raises(): "test argument parsing raises in move" for func in bn.get_functions('move'): yield unit_maker_argparse_raises, func def unit_maker_argparse_raises(func): "test argument parsing raises in move" a = np.array([1., 2, 3]) assert_raises(TypeError, func) assert_raises(TypeError, func, axis=a) assert_raises(TypeError, func, a, 2, axis=0, extra=0) assert_raises(TypeError, func, a, 2, axis=0, a=a) assert_raises(TypeError, func, a, 2, 2, 0, 0, 0) assert_raises(TypeError, func, a, 2, axis='0') assert_raises(TypeError, func, a, 1, min_count='1') if func.__name__ not in ('move_std', 'move_var'): assert_raises(TypeError, func, a, 2, ddof=0) # --------------------------------------------------------------------------- # move_median.c is complicated. Let's do some more testing. # # If you make changes to move_median.c then do lots of tests by increasing # range(100) in the two functions below to range(10000). And for extra credit # increase size to 30. With those two changes the unit tests will take a # LONG time to run. def test_move_median_with_nans(): "test move_median.c with nans" fmt = '\nfunc %s | window %d | min_count %s\n\nInput array:\n%s\n' aaae = assert_array_almost_equal min_count = 1 size = 10 func = bn.move_median func0 = bn.slow.move_median rs = np.random.RandomState([1, 2, 3]) for i in range(100): a = np.arange(size, dtype=np.float64) idx = rs.rand(*a.shape) < 0.1 a[idx] = np.inf idx = rs.rand(*a.shape) < 0.2 a[idx] = np.nan rs.shuffle(a) for window in range(2, size + 1): actual = func(a, window=window, min_count=min_count) desired = func0(a, window=window, min_count=min_count) err_msg = fmt % (func.__name__, window, min_count, a) aaae(actual, desired, decimal=5, err_msg=err_msg) def test_move_median_without_nans(): "test move_median.c without nans" fmt = '\nfunc %s | window %d | min_count %s\n\nInput array:\n%s\n' aaae = assert_array_almost_equal min_count = 1 size = 10 func = bn.move_median func0 = bn.slow.move_median rs = np.random.RandomState([1, 2, 3]) for i in range(100): a = np.arange(size, dtype=np.int64) rs.shuffle(a) for window in range(2, size + 1): actual = func(a, window=window, min_count=min_count) desired = func0(a, window=window, min_count=min_count) err_msg = fmt % (func.__name__, window, min_count, a) aaae(actual, desired, decimal=5, err_msg=err_msg) # ---------------------------------------------------------------------------- # Regression test for square roots of negative numbers def test_move_std_sqrt(): "Test move_std for neg sqrt." a = [0.0011448196318903589, 0.00028718669878572767, 0.00028718669878572767, 0.00028718669878572767, 0.00028718669878572767] err_msg = "Square root of negative number. ndim = %d" b = bn.move_std(a, window=3) assert_true(np.isfinite(b[2:]).all(), err_msg % 1) a2 = np.array([a, a]) b = bn.move_std(a2, window=3, axis=1) assert_true(np.isfinite(b[:, 2:]).all(), err_msg % 2) a3 = np.array([[a, a], [a, a]]) b = bn.move_std(a3, window=3, axis=2) assert_true(np.isfinite(b[:, :, 2:]).all(), err_msg % 3) Bottleneck-1.2.1/bottleneck/tests/list_input_test.py0000664000175000017500000000320013103166630021724 0ustar kgkg00000000000000"Check that functions can handle list input" import warnings import numpy as np from numpy.testing import assert_array_almost_equal import bottleneck as bn from .util import DTYPES def test_list_input(): "Check that functions can handle list input" for func in bn.get_functions('all'): if func.__name__ != 'replace': yield unit_maker, func def lists(dtypes=DTYPES): "Iterator that yields lists to use for unit testing." ss = {} ss[1] = {'size': 4, 'shapes': [(4,)]} ss[2] = {'size': 6, 'shapes': [(1, 6), (2, 3)]} ss[3] = {'size': 6, 'shapes': [(1, 2, 3)]} ss[4] = {'size': 24, 'shapes': [(1, 2, 3, 4)]} for ndim in ss: size = ss[ndim]['size'] shapes = ss[ndim]['shapes'] a = np.arange(size) for shape in shapes: a = a.reshape(shape) for dtype in dtypes: yield a.astype(dtype).tolist() def unit_maker(func): "Test that bn.xxx gives the same output as bn.slow.xxx for list input." msg = '\nfunc %s | input %s (%s) | shape %s\n' msg += '\nInput array:\n%s\n' name = func.__name__ func0 = eval('bn.slow.%s' % name) for i, a in enumerate(lists()): with warnings.catch_warnings(): warnings.simplefilter("ignore") try: actual = func(a) desired = func0(a) except TypeError: actual = func(a, 2) desired = func0(a, 2) a = np.array(a) tup = (name, 'a'+str(i), str(a.dtype), str(a.shape), a) err_msg = msg % tup assert_array_almost_equal(actual, desired, err_msg=err_msg) Bottleneck-1.2.1/bottleneck/tests/scalar_input_test.py0000664000175000017500000000140313103166630022221 0ustar kgkg00000000000000"Check that functions can handle scalar input" from numpy.testing import assert_array_almost_equal import bottleneck as bn def unit_maker(func, func0, args=tuple()): "Test that bn.xxx gives the same output as bn.slow.xxx for scalar input." msg = '\nfunc %s | input %s\n' a = -9 argsi = [a] + list(args) actual = func(*argsi) desired = func0(*argsi) err_msg = msg % (func.__name__, a) assert_array_almost_equal(actual, desired, err_msg=err_msg) def test_scalar_input(): "Test scalar input" funcs = bn.get_functions('reduce') + bn.get_functions('nonreduce_axis') for func in funcs: if func.__name__ not in ('partition', 'argpartition', 'push'): yield unit_maker, func, eval('bn.slow.%s' % func.__name__) Bottleneck-1.2.1/Bottleneck.egg-info/0000775000175000017500000000000013106356012016414 5ustar kgkg00000000000000Bottleneck-1.2.1/Bottleneck.egg-info/PKG-INFO0000664000175000017500000001465013106356012017517 0ustar kgkg00000000000000Metadata-Version: 1.1 Name: Bottleneck Version: 1.2.1 Summary: Fast NumPy array functions written in C Home-page: https://github.com/kwgoodman/bottleneck Author: Keith Goodman Author-email: bottle-neck@googlegroups.com License: Simplified BSD Download-URL: http://pypi.python.org/pypi/Bottleneck Description: Bottleneck is a collection of fast NumPy array functions written in C. Let's give it a try. Create a NumPy array:: >>> import numpy as np >>> a = np.array([1, 2, np.nan, 4, 5]) Find the nanmean:: >>> import bottleneck as bn >>> bn.nanmean(a) 3.0 Moving window mean:: >>> bn.move_mean(a, window=2, min_count=1) array([ 1. , 1.5, 2. , 4. , 4.5]) Benchmark ========= Bottleneck comes with a benchmark suite:: >>> bn.bench() Bottleneck performance benchmark Bottleneck 1.3.0.dev0; Numpy 1.12.1 Speed is NumPy time divided by Bottleneck time NaN means approx one-fifth NaNs; float64 used no NaN no NaN NaN no NaN NaN (100,) (1000,1000)(1000,1000)(1000,1000)(1000,1000) axis=0 axis=0 axis=0 axis=1 axis=1 nansum 67.3 0.3 0.7 2.5 2.4 nanmean 194.8 1.9 2.1 3.4 3.1 nanstd 241.5 1.6 2.1 2.7 2.6 nanvar 229.7 1.7 2.1 2.7 2.5 nanmin 34.1 0.7 1.1 0.8 2.6 nanmax 45.6 0.7 2.7 1.0 3.7 median 111.0 1.3 5.6 1.0 4.8 nanmedian 108.8 5.9 6.7 5.6 6.7 ss 16.3 1.1 1.2 1.6 1.6 nanargmin 89.2 2.9 5.1 2.2 5.6 nanargmax 107.4 3.0 5.4 2.2 5.8 anynan 19.4 0.3 35.0 0.5 29.9 allnan 39.9 146.6 128.3 115.8 75.6 rankdata 55.0 2.6 2.3 2.9 2.8 nanrankdata 59.8 2.8 2.2 3.2 2.5 partition 4.4 1.2 1.6 1.0 1.4 argpartition 3.5 1.1 1.4 1.1 1.6 replace 17.7 1.4 1.4 1.3 1.4 push 3440.0 7.8 9.5 20.0 15.5 move_sum 4743.0 75.7 156.1 195.4 211.1 move_mean 8760.9 116.2 167.4 252.1 258.8 move_std 8979.9 96.1 196.3 144.0 256.3 move_var 11216.8 127.3 243.9 225.9 321.4 move_min 2245.3 20.6 36.7 23.2 42.1 move_max 2223.7 20.5 37.2 24.1 42.4 move_argmin 3664.0 48.2 73.3 40.2 83.9 move_argmax 3916.9 42.0 75.4 41.5 81.2 move_median 2023.3 166.8 173.7 153.8 154.3 move_rank 1208.5 1.9 1.9 2.5 2.8 You can also run a detailed benchmark for a single function using, for example, the command:: >>> bn.bench_detailed("move_median", fraction_nan=0.3) Only arrays with data type (dtype) int32, int64, float32, and float64 are accelerated. All other dtypes result in calls to slower, unaccelerated functions. In the rare case of a byte-swapped input array (e.g. a big-endian array on a little-endian operating system) the function will not be accelerated regardless of dtype. Where ===== =================== ======================================================== download https://pypi.python.org/pypi/Bottleneck docs http://berkeleyanalytics.com/bottleneck code https://github.com/kwgoodman/bottleneck mailing list https://groups.google.com/group/bottle-neck =================== ======================================================== License ======= Bottleneck is distributed under a Simplified BSD license. See the LICENSE file for details. Install ======= Requirements: ======================== ==================================================== Bottleneck Python 2.7, 3.5, 3.6; NumPy 1.12.1 Compile gcc, clang, MinGW or MSVC Unit tests nose ======================== ==================================================== To install Bottleneck on GNU/Linux, Mac OS X, et al.:: $ sudo python setup.py install To install bottleneck on Windows, first install MinGW and add it to your system path. Then install Bottleneck with the commands:: python setup.py install --compiler=mingw32 Alternatively, you can use the Windows binaries created by Christoph Gohlke: http://www.lfd.uci.edu/~gohlke/pythonlibs/#bottleneck Unit tests ========== After you have installed Bottleneck, run the suite of unit tests:: >>> import bottleneck as bn >>> bn.test() Ran 169 tests in 57.205s OK Platform: OS Independent Classifier: Development Status :: 4 - Beta Classifier: Environment :: Console Classifier: Intended Audience :: Science/Research Classifier: License :: OSI Approved :: BSD License Classifier: Operating System :: OS Independent Classifier: Programming Language :: C Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 3 Classifier: Topic :: Scientific/Engineering Requires: numpy Bottleneck-1.2.1/Bottleneck.egg-info/top_level.txt0000664000175000017500000000001313106356012021140 0ustar kgkg00000000000000bottleneck Bottleneck-1.2.1/Bottleneck.egg-info/requires.txt0000664000175000017500000000000613106356012021010 0ustar kgkg00000000000000numpy Bottleneck-1.2.1/Bottleneck.egg-info/dependency_links.txt0000664000175000017500000000000113106356012022462 0ustar kgkg00000000000000 Bottleneck-1.2.1/Bottleneck.egg-info/SOURCES.txt0000664000175000017500000000371713106356012020310 0ustar kgkg00000000000000LICENSE MANIFEST.in Makefile README.rst RELEASE.rst setup.py tox.ini Bottleneck.egg-info/PKG-INFO Bottleneck.egg-info/SOURCES.txt Bottleneck.egg-info/dependency_links.txt Bottleneck.egg-info/requires.txt Bottleneck.egg-info/top_level.txt bottleneck/LICENSE bottleneck/__init__.py bottleneck/version.py bottleneck/benchmark/__init__.py bottleneck/benchmark/autotimeit.py bottleneck/benchmark/bench.py bottleneck/benchmark/bench_detailed.py bottleneck/slow/__init__.py bottleneck/slow/move.py bottleneck/slow/nonreduce.py bottleneck/slow/nonreduce_axis.py bottleneck/slow/reduce.py bottleneck/src/__init__.py bottleneck/src/bottleneck.h bottleneck/src/iterators.h bottleneck/src/move_template.c bottleneck/src/nonreduce_axis_template.c bottleneck/src/nonreduce_template.c bottleneck/src/reduce_template.c bottleneck/src/template.py bottleneck/src/move_median/move_median.c bottleneck/src/move_median/move_median.h bottleneck/src/move_median/move_median_debug.c bottleneck/tests/__init__.py bottleneck/tests/input_modifcation_test.py bottleneck/tests/list_input_test.py bottleneck/tests/move_test.py bottleneck/tests/nonreduce_axis_test.py bottleneck/tests/nonreduce_test.py bottleneck/tests/reduce_test.py bottleneck/tests/scalar_input_test.py bottleneck/tests/util.py doc/doc_howto doc/image/icon.png doc/image/icon.xcf doc/image/icon14.png doc/source/.gitignore doc/source/conf.py doc/source/index.rst doc/source/intro.rst doc/source/license.rst doc/source/reference.rst doc/source/release.rst doc/sphinxext/LICENSE.txt doc/sphinxext/MANIFEST.in doc/sphinxext/PKG-INFO doc/sphinxext/README.txt doc/sphinxext/__init__.py doc/sphinxext/comment_eater.py doc/sphinxext/compiler_unparse.py doc/sphinxext/docscrape.py doc/sphinxext/docscrape.pyc doc/sphinxext/docscrape_sphinx.py doc/sphinxext/docscrape_sphinx.pyc doc/sphinxext/numpydoc.py doc/sphinxext/numpydoc.pyc doc/sphinxext/phantom_import.py doc/sphinxext/plot_directive.py doc/sphinxext/setup.cfg doc/sphinxext/setup.py doc/sphinxext/traitsdoc.py