235 lines
6.9 KiB
Python
235 lines
6.9 KiB
Python
"""Compatibility fixes for older version of python, numpy and scipy
|
|
|
|
If you add content to this file, please give the version of the package
|
|
at which the fixe is no longer needed.
|
|
"""
|
|
# Authors: Emmanuelle Gouillart <emmanuelle.gouillart@normalesup.org>
|
|
# Gael Varoquaux <gael.varoquaux@normalesup.org>
|
|
# Fabian Pedregosa <fpedregosa@acm.org>
|
|
#
|
|
# License: BSD 3 clause
|
|
|
|
from operator import itemgetter
|
|
import inspect
|
|
from sklearn.externals import six
|
|
|
|
import numpy as np
|
|
|
|
|
|
def lsqr(X, y, tol=1e-3):
|
|
import scipy.sparse.linalg as sp_linalg
|
|
from ..utils.extmath import safe_sparse_dot
|
|
|
|
if hasattr(sp_linalg, 'lsqr'):
|
|
# scipy 0.8 or greater
|
|
return sp_linalg.lsqr(X, y)
|
|
else:
|
|
n_samples, n_features = X.shape
|
|
if n_samples > n_features:
|
|
coef, _ = sp_linalg.cg(safe_sparse_dot(X.T, X),
|
|
safe_sparse_dot(X.T, y),
|
|
tol=tol)
|
|
else:
|
|
coef, _ = sp_linalg.cg(safe_sparse_dot(X, X.T), y, tol=tol)
|
|
coef = safe_sparse_dot(X.T, coef)
|
|
|
|
residues = y - safe_sparse_dot(X, coef)
|
|
return coef, None, None, residues
|
|
|
|
|
|
def _unique(ar, return_index=False, return_inverse=False):
|
|
"""A replacement for the np.unique that appeared in numpy 1.4.
|
|
|
|
While np.unique existed long before, keyword return_inverse was
|
|
only added in 1.4.
|
|
"""
|
|
try:
|
|
ar = ar.flatten()
|
|
except AttributeError:
|
|
if not return_inverse and not return_index:
|
|
items = sorted(set(ar))
|
|
return np.asarray(items)
|
|
else:
|
|
ar = np.asarray(ar).flatten()
|
|
|
|
if ar.size == 0:
|
|
if return_inverse and return_index:
|
|
return ar, np.empty(0, np.bool), np.empty(0, np.bool)
|
|
elif return_inverse or return_index:
|
|
return ar, np.empty(0, np.bool)
|
|
else:
|
|
return ar
|
|
|
|
if return_inverse or return_index:
|
|
perm = ar.argsort()
|
|
aux = ar[perm]
|
|
flag = np.concatenate(([True], aux[1:] != aux[:-1]))
|
|
if return_inverse:
|
|
iflag = np.cumsum(flag) - 1
|
|
iperm = perm.argsort()
|
|
if return_index:
|
|
return aux[flag], perm[flag], iflag[iperm]
|
|
else:
|
|
return aux[flag], iflag[iperm]
|
|
else:
|
|
return aux[flag], perm[flag]
|
|
|
|
else:
|
|
ar.sort()
|
|
flag = np.concatenate(([True], ar[1:] != ar[:-1]))
|
|
return ar[flag]
|
|
|
|
np_version = []
|
|
for x in np.__version__.split('.'):
|
|
try:
|
|
np_version.append(int(x))
|
|
except ValueError:
|
|
# x may be of the form dev-1ea1592
|
|
np_version.append(x)
|
|
np_version = tuple(np_version)
|
|
|
|
if np_version[:2] < (1, 5):
|
|
unique = _unique
|
|
else:
|
|
unique = np.unique
|
|
|
|
|
|
def _logaddexp(x1, x2, out=None):
|
|
"""Fix np.logaddexp in numpy < 1.4 when x1 == x2 == -np.inf."""
|
|
if out is not None:
|
|
result = np.logaddexp(x1, x2, out=out)
|
|
else:
|
|
result = np.logaddexp(x1, x2)
|
|
|
|
result[np.logical_and(x1 == -np.inf, x2 == -np.inf)] = -np.inf
|
|
|
|
return result
|
|
|
|
if np_version[:2] < (1, 4):
|
|
logaddexp = _logaddexp
|
|
else:
|
|
logaddexp = np.logaddexp
|
|
|
|
|
|
def _bincount(X, weights=None, minlength=None):
|
|
"""Replacing np.bincount in numpy < 1.6 to provide minlength."""
|
|
result = np.bincount(X, weights)
|
|
if len(result) >= minlength:
|
|
return result
|
|
out = np.zeros(minlength, np.int)
|
|
out[:len(result)] = result
|
|
return out
|
|
|
|
if np_version[:2] < (1, 6):
|
|
bincount = _bincount
|
|
else:
|
|
bincount = np.bincount
|
|
|
|
|
|
def _copysign(x1, x2):
|
|
"""Slow replacement for np.copysign, which was introduced in numpy 1.4"""
|
|
return np.abs(x1) * np.sign(x2)
|
|
|
|
if not hasattr(np, 'copysign'):
|
|
copysign = _copysign
|
|
else:
|
|
copysign = np.copysign
|
|
|
|
|
|
def _in1d(ar1, ar2, assume_unique=False):
|
|
"""Replacement for in1d that is provided for numpy >= 1.4"""
|
|
if not assume_unique:
|
|
ar1, rev_idx = unique(ar1, return_inverse=True)
|
|
ar2 = np.unique(ar2)
|
|
ar = np.concatenate((ar1, ar2))
|
|
# We need this to be a stable sort, so always use 'mergesort'
|
|
# here. The values from the first array should always come before
|
|
# the values from the second array.
|
|
order = ar.argsort(kind='mergesort')
|
|
sar = ar[order]
|
|
equal_adj = (sar[1:] == sar[:-1])
|
|
flag = np.concatenate((equal_adj, [False]))
|
|
indx = order.argsort(kind='mergesort')[:len(ar1)]
|
|
|
|
if assume_unique:
|
|
return flag[indx]
|
|
else:
|
|
return flag[indx][rev_idx]
|
|
|
|
if not hasattr(np, 'in1d'):
|
|
in1d = _in1d
|
|
else:
|
|
in1d = np.in1d
|
|
|
|
|
|
def qr_economic(A, **kwargs):
|
|
"""Compat function for the QR-decomposition in economic mode
|
|
|
|
Scipy 0.9 changed the keyword econ=True to mode='economic'
|
|
"""
|
|
import scipy.linalg
|
|
# trick: triangular solve has introduced in 0.9
|
|
if hasattr(scipy.linalg, 'solve_triangular'):
|
|
return scipy.linalg.qr(A, mode='economic', **kwargs)
|
|
else:
|
|
import warnings
|
|
with warnings.catch_warnings():
|
|
warnings.simplefilter("ignore", DeprecationWarning)
|
|
return scipy.linalg.qr(A, econ=True, **kwargs)
|
|
|
|
|
|
def savemat(file_name, mdict, oned_as="column", **kwargs):
|
|
"""MATLAB-format output routine that is compatible with SciPy 0.7's.
|
|
|
|
0.7.2 (or .1?) added the oned_as keyword arg with 'column' as the default
|
|
value. It issues a warning if this is not provided, stating that "This will
|
|
change to 'row' in future versions."
|
|
"""
|
|
import scipy.io
|
|
try:
|
|
return scipy.io.savemat(file_name, mdict, oned_as=oned_as, **kwargs)
|
|
except TypeError:
|
|
return scipy.io.savemat(file_name, mdict, **kwargs)
|
|
|
|
try:
|
|
from numpy import count_nonzero
|
|
except ImportError:
|
|
def count_nonzero(X):
|
|
return len(np.flatnonzero(X))
|
|
|
|
# little danse to see if np.copy has an 'order' keyword argument
|
|
if 'order' in inspect.getargspec(np.copy)[0]:
|
|
def safe_copy(X):
|
|
# Copy, but keep the order
|
|
return np.copy(X, order='K')
|
|
else:
|
|
# Before an 'order' argument was introduced, numpy wouldn't muck with
|
|
# the ordering
|
|
safe_copy = np.copy
|
|
|
|
try:
|
|
if (not np.allclose(np.divide(.4, 1), np.divide(.4, 1, dtype=np.float))
|
|
or not np.allclose(np.divide(.4, 1), .4)):
|
|
raise TypeError('Divide not working with dtype: '
|
|
'https://github.com/numpy/numpy/issues/3484')
|
|
divide = np.divide
|
|
|
|
except TypeError:
|
|
# Compat for old versions of np.divide that do not provide support for
|
|
# the dtype args
|
|
def divide(x1, x2, out=None, dtype=None):
|
|
out_orig = out
|
|
if out is None:
|
|
out = np.asarray(x1, dtype=dtype)
|
|
if out is x1:
|
|
out = x1.copy()
|
|
else:
|
|
if out is not x1:
|
|
out[:] = x1
|
|
if dtype is not None and out.dtype != dtype:
|
|
out = out.astype(dtype)
|
|
out /= x2
|
|
if out_orig is None and np.isscalar(x1):
|
|
out = np.asscalar(out)
|
|
return out
|