import numpy as np
from contextlib import contextmanager
from copy import deepcopy
from collections import OrderedDict
from operator import (
add, div, mul, pow, sub, iadd, idiv, imul, ipow, isub)
from pandas import Series
from colour import (
CaseInsensitiveMapping,
CubicSplineInterpolator,
Extrapolator,
LinearInterpolator,
PchipInterpolator,
SpragueInterpolator,
as_numeric,
is_numeric,
tsplit,
tstack,
warning)
OPERATORS = CaseInsensitiveMapping({
'Add': (add, iadd),
'Sub': (sub, isub),
'Mul' : (mul, imul),
'Div': (div, idiv),
'Pow': (pow, ipow)})
# TODO: Is this the right name for the interpolator? Should we call the default
# null value `default`?
class NullInterpolator(object):
"""
Parameters
----------
x : ndarray
Independent :math:`x` variable values corresponding with :math:`y`
variable.
y : ndarray
Dependent and already known :math:`y` variable values to
interpolate.
Methods
-------
__call__
Examples
--------
"""
def __init__(self,
x=None,
y=None,
absolute_tolerance=10e-7,
relative_tolerance=10e-7,
default=np.nan):
self.__x = None
self.x = x
self.__y = None
self.y = y
self.__absolute_tolerance = None
self.absolute_tolerance = absolute_tolerance
self.__relative_tolerance = None
self.relative_tolerance = relative_tolerance
self.__default = None
self.default = default
self.__validate_dimensions()
@property
def x(self):
"""
Property for **self.__x** private attribute.
Returns
-------
array_like
self.__x
"""
return self.__x
@x.setter
def x(self, value):
"""
Setter for **self.__x** private attribute.
Parameters
----------
value : array_like
Attribute value.
"""
if value is not None:
value = np.atleast_1d(value).astype(np.float_)
assert value.ndim == 1, (
'"x" independent variable must have exactly one dimension!')
self.__x = value
@property
def y(self):
"""
Property for **self.__y** private attribute.
Returns
-------
array_like
self.__y
"""
return self.__y
@y.setter
def y(self, value):
"""
Setter for **self.__y** private attribute.
Parameters
----------
value : array_like
Attribute value.
"""
if value is not None:
value = np.atleast_1d(value).astype(np.float_)
assert value.ndim == 1, (
'"y" dependent variable must have exactly one dimension!')
self.__y = value
@property
def relative_tolerance(self):
"""
Property for **self.__relative_tolerance** private attribute.
Returns
-------
numeric
self.__relative_tolerance
"""
return self.__relative_tolerance
@relative_tolerance.setter
def relative_tolerance(self, value):
"""
Setter for **self.__relative_tolerance** private attribute.
Parameters
----------
value : numeric
Attribute value.
"""
if value is not None:
assert is_numeric(value), (
'"relative_tolerance" variable must be a "numeric"!')
self.__relative_tolerance = value
@property
def absolute_tolerance(self):
"""
Property for **self.__absolute_tolerance** private attribute.
Returns
-------
numeric
self.__absolute_tolerance
"""
return self.__absolute_tolerance
@absolute_tolerance.setter
def absolute_tolerance(self, value):
"""
Setter for **self.__absolute_tolerance** private attribute.
Parameters
----------
value : numeric
Attribute value.
"""
if value is not None:
assert is_numeric(value), (
'"absolute_tolerance" variable must be a "numeric"!')
self.__absolute_tolerance = value
@property
def default(self):
"""
Property for **self.__default** private attribute.
Returns
-------
numeric
self.__default
"""
return self.__default
@default.setter
def default(self, value):
"""
Setter for **self.__default** private attribute.
Parameters
----------
value : numeric
Attribute value.
"""
if value is not None:
assert is_numeric(value), (
'"default" variable must be a "numeric"!')
self.__default = value
def __call__(self, x):
"""
Evaluates the interpolatior at given point(s).
Parameters
----------
x : numeric or array_like
Point(s) to evaluate the interpolant at.
Returns
-------
float or ndarray
Interpolated value(s).
"""
x = np.atleast_1d(x).astype(np.float_)
xi = as_numeric(self.__evaluate(x))
return xi
def __evaluate(self, x):
"""
Performs the interpolator evaluation at given points.
Parameters
----------
x : ndarray
Points to evaluate the interpolant at.
Returns
-------
ndarray
Interpolated points values.
"""
self.__validate_dimensions()
self.__validate_interpolation_range(x)
index = nearest_index(self.__x, x)
values = self.__y[index]
values[~np.isclose(self.__x[index],
x,
rtol=self.__absolute_tolerance,
atol=self.__relative_tolerance)] = self.__default
return values
def __validate_dimensions(self):
"""
Validates variables dimensions to be the same.
"""
if len(self.__x) != len(self.__y):
raise ValueError(
('"x" independent and "y" dependent variables have different '
'dimensions: "{0}", "{1}"').format(len(self.__x),
len(self.__y)))
def __validate_interpolation_range(self, x):
"""
Validates given point to be in interpolation range.
"""
below_interpolation_range = x < self.__x[0]
above_interpolation_range = x > self.__x[-1]
if below_interpolation_range.any():
raise ValueError('"{0}" is below interpolation range.'.format(x))
if above_interpolation_range.any():
raise ValueError('"{0}" is above interpolation range.'.format(x))
INTERPOLATORS = CaseInsensitiveMapping({
'Cubic Spline': CubicSplineInterpolator,
'Linear': LinearInterpolator,
'Null' : NullInterpolator,
'Pchip': PchipInterpolator,
'Sprague': SpragueInterpolator})
def nearest_index(a, b):
index = np.searchsorted(a, b)
return np.where(np.abs(b - a[index-1]) < np.fabs(b - a[index]),
index - 1,
index)
def nearest(a, b):
a = np.asarray(a)
return a[nearest_index(a, b)]
@contextmanager
def ndarray_write(a):
a.setflags(write=True)
yield a
a.setflags(write=False)
def fill_nan(a, method='Interpolation', default=np.nan):
mask = np.isnan(a)
if method.lower() == 'interpolation':
a[mask] = np.interp(
np.flatnonzero(mask),
np.flatnonzero(~mask),
a[~mask])
elif method.lower() == 'constant':
a[mask] = default
return a
def is_pandas_installed():
try:
import pandas
return True
except ImportError:
return False
def unpack_data(data=None, domain=None):
domain_f, range_f, name_f = None, None, None
if isinstance(data, Signal):
domain_f = data.domain
range_f = data.range
if (isinstance(data, tuple) or
isinstance(data, list) or
isinstance(data, np.ndarray)):
data = np.asarray(data)
if data.ndim == 1:
range_f = data
elif data.ndim == 2:
domain_f, range_f = tsplit(data)
else:
raise ValueError('"data" must be a 1d or 2d array-like variable!')
elif (isinstance(data, dict) or
isinstance(data, OrderedDict)):
domain_f, range_f = tsplit(sorted(data.items()))
elif is_pandas_installed():
if isinstance(data, Series):
domain_f = data.index.values
range_f = data.values
name_f = data.name
domain_f = domain_f if domain is None else domain
return domain_f, range_f, name_f
class Signal(object):
def __init__(self,
data=None,
domain=None,
interpolation_method=None,
interpolation_options=None,
extrapolation_method=None,
extrapolation_options=None,
name=None):
self.__domain = None
self.__range = None
self.__interpolation_method = 'Linear'
self.__interpolation_options = {}
self.__extrapolation_method = 'Constant'
self.__extrapolation_options = {'left': np.nan, 'right': np.nan}
self.__name = '{0} ({1})'.format(self.__class__.__name__, id(self))
domain_f, range_f, name_f = unpack_data(data, domain)
name_f = name_f if name is None else name
self.domain = domain_f
self.range = range_f
self.interpolation_method = interpolation_method
self.interpolation_options = interpolation_options
self.extrapolation_method = extrapolation_method
self.extrapolation_options = extrapolation_options
self.name = name_f
self.__create_function()
@property
def domain(self):
return self.__domain
@domain.setter
def domain(self, value):
if value is not None:
if not np.all(np.isfinite(value)):
warning('"domain" variable is not finite, '
'unpredictable results may occur!\n{0}'.format(value))
# TODO: `self.domain` is a copy of `value` to avoid side effects,
# Is it a smart way to avoid them?
value = np.copy(np.asarray(value))
if self.__range is not None:
assert value.size == self.__range.size, (
'"domain" and "range" variables must have same size!')
value.setflags(write=False)
self.__domain = value
self.__create_function()
@property
def range(self):
return self.__range
@range.setter
def range(self, value):
if value is not None:
if not np.all(np.isfinite(value)):
warning('"range" variable is not finite, '
'unpredictable results may occur!\n{0}'.format(value))
# TODO: `self.range` is a copy of `value` to avoid side effects,
# Is it a smart way to avoid them?
value = np.copy(np.asarray(value))
if self.__domain is not None:
assert value.size == self.__domain.size, (
'"domain" and "range" variables must have same size!')
value.setflags(write=False)
self.__range = value
self.__create_function()
@property
def interpolation_method(self):
return self.__interpolation_method
@interpolation_method.setter
def interpolation_method(self, value):
if value is not None:
assert type(value) in (str, unicode), ( # noqa
('"{0}" attribute: "{1}" type is not '
'"str" or "unicode"!').format('interpolation_method', value))
assert value in INTERPOLATORS, (
('"{0}" attribute: "{1}" interpolation method is not defined! '
'Available methods: "{2}".').format(
'interpolation_method',
value,
sorted(INTERPOLATORS.keys())))
self.__interpolation_method = value
self.__create_function()
@property
def interpolation_options(self):
return self.__interpolation_options
@interpolation_options.setter
def interpolation_options(self, value):
if value is not None:
assert type(value) in (dict, OrderedDict), (
('"{0}" attribute: "{1}" type is not '
'"dict" or "OrderedDict"!').format(
'interpolation_options', value))
self.__interpolation_options = value
self.__create_function()
@property
def extrapolation_method(self):
return self.__extrapolation_method
@extrapolation_method.setter
def extrapolation_method(self, value):
if value is not None:
assert type(value) in (str, unicode), ( # noqa
('"{0}" attribute: "{1}" type is not '
'"str" or "unicode"!').format('extrapolation_method', value))
assert value in ('Constant', 'Linear'), (
('"{0}" attribute: "{1}" extrapolation method is not defined! '
'Available methods: "[\'Constant\', \'Linear\']".').format(
'interpolation_method', value))
self.__extrapolation_method = value
self.__create_function()
@property
def extrapolation_options(self):
return self.__extrapolation_options
@extrapolation_options.setter
def extrapolation_options(self, value):
if value is not None:
assert type(value) in (dict, OrderedDict), (
('"{0}" attribute: "{1}" type is not '
'"dict" or "OrderedDict"!').format(
'extrapolation_options',value))
self.__extrapolation_options = value
self.__create_function()
@property
def name(self):
return self.__name
@name.setter
def name(self, value):
if value is not None:
assert type(value) in (str, unicode), ( # noqa
('"{0}" attribute: "{1}" type is not '
'"str" or "unicode"!').format('name', value))
self.__name = value
@property
def function(self):
return self.__function
@function.setter
def function(self, value):
raise AttributeError(
'"{0}" attribute is read only!'.format('function'))
def __create_function(self):
if self.__domain is not None and self.__range is not None:
with ndarray_write(self.__domain), ndarray_write(self.__range):
# TODO: Providing a writeable copy of both `self.domain` and `
# self.range` to the interpolator to avoid issue regarding `MemoryView`
# being read-only. https://mail.python.org/pipermail/cython-devel/2013-February/003384.html
self.__function = Extrapolator(
INTERPOLATORS[self.__interpolation_method](
np.copy(self.__domain),
np.copy(self.__range),
**self.__interpolation_options),
method=self.__extrapolation_method,
**self.__extrapolation_options)
else:
def __undefined_signal_interpolator_function(*args, **kwargs):
raise RuntimeError(
'Underlying signal interpolator function does not exists, '
'please ensure you defined both '
'"domain" and "range" variables!')
self.__function = __undefined_signal_interpolator_function
def __str__(self):
try:
return str(tstack((self.domain, self.range)))
except TypeError:
return super(Signal, self).__str__()
def __repr__(self):
try:
representation = repr(tstack((self.domain, self.range)))
representation = representation.replace('array', self.__class__.__name__)
representation = representation.replace(' [',
'{0}['.format(' ' * (len(self.__class__.__name__) + 2)))
return representation
except TypeError:
return super(Signal, self).__repr__()
def __getitem__(self, x):
if type(x) is slice:
return self.__range[x]
else:
return self.__function(x)
def __setitem__(self, x, value):
if type(x) is slice:
with ndarray_write(self.__range):
self.__range[x] = value
else:
with ndarray_write(self.__domain), ndarray_write(self.__range):
x = np.atleast_1d(x)
value = np.resize(value, self.__domain.shape)
# Matching domain, replacing existing `self.range`.
mask = np.in1d(x, self.__domain)
x_m = x[mask]
indexes = np.searchsorted(self.__domain, x_m)
self.__range[indexes] = value[mask]
# Non matching domain, inserting into existing `self.domain`
# and `self.range`.
x_nm = x[~mask]
indexes = np.searchsorted(self.__domain, x_nm)
self.__domain = np.insert(self.__domain, indexes, x_nm)
self.__range = np.insert(self.__range, indexes, value[~mask])
self.__create_function
def __contains__(self, x):
return np.all(np.where(np.logical_and(x >= np.min(self.__domain),
x <= np.max(self.__domain)),
True,
False))
def __eq__(self, x):
if isinstance(x, self.__class__):
if all((np.array_equal(self.__domain, x.domain),
np.array_equal(self.__range, x.range),
self.__interpolation_method == x.interpolation_method,
self.__interpolation_options == x.interpolation_options,
self.__extrapolation_method == x.extrapolation_method,
self.__extrapolation_options == x.extrapolation_options)):
return True
return False
def __neq__(self, x):
return not (self == x)
def __fill_domain_nan(self, method='Interpolation', default=0):
with ndarray_write(self.__domain):
self.__domain = fill_nan(self.__domain, method, default)
self.__create_function()
def __fill_range_nan(self, method='Interpolation', default=0):
with ndarray_write(self.__range):
self.__range = fill_nan(self.__range, method, default)
self.__create_function()
def __arithmetical_ioperation(self, x, operator):
operator, ioperator = OPERATORS[operator]
if isinstance(x, self.__class__):
with ndarray_write(self.__domain), ndarray_write(self.__range):
# Operation from `self` domain and range.
self[self.__domain] = operator(self.__range, x[self.__domain])
# Operation from `x` domain and range.
self[x.domain] = operator(self[x.domain], x.range)
else:
with ndarray_write(self.__range):
self.range = ioperator(self.range, x)
return self
def __arithmetical_operation(self, x, operator):
_operator, ioperator = OPERATORS[operator]
copy = ioperator(self.copy(), x)
return copy
def __iadd__(self, x):
return self.__arithmetical_ioperation(x, 'Add')
def __add__(self, x):
return self.__arithmetical_operation(x, 'Add')
def __isub__(self, x):
return self.__arithmetical_ioperation(x, 'Sub')
def __sub__(self, x):
return self.__arithmetical_operation(x, 'Sub')
def __imul__(self, x):
return self.__arithmetical_ioperation(x, 'Mul')
def __mul__(self, x):
return self.__arithmetical_operation(x, 'Mul')
def __idiv__(self, x):
return self.__arithmetical_ioperation(x, 'Div')
def __div__(self, x):
return self.__arithmetical_operation(x, 'Div')
__itruediv__ = __idiv__
__truediv__ = __div__
def __ipow__(self, x):
return self.__arithmetical_ioperation(x, 'Pow')
def __pow__(self, x):
return self.__arithmetical_operation(x, 'Pow')
def copy(self):
return deepcopy(self)
def fill_nan(self, method='Interpolation', default=0):
self.__fill_domain_nan(method, default)
self.__fill_range_nan(method, default)
def uncertainty(self, x):
n = nearest(self.__domain, x)
return np.abs(x - n)
cs1 = Signal()
print('1) cs1[0]')
try:
print(cs1[0])
except RuntimeError as error:
print(error)
print('\n')
domain = np.arange(0, 1000, 100)
cs1 = Signal(domain=domain)
print('2) cs1[0]')
try:
print(cs1[0])
except RuntimeError as error:
print(error)
print('\n')
range = np.linspace(1, 10, domain.size)
cs1 = Signal(range, domain)
print('3) cs1[0]')
print(cs1[0])
print('\n')
print('4) cs1 = Signal(range, [])')
try:
cs1 = Signal(range, [])
except AssertionError as error:
print(error)
1) cs1[0] Underlying signal interpolator function does not exists, please ensure you defined both "domain" and "range" variables! 2) cs1[0] Underlying signal interpolator function does not exists, please ensure you defined both "domain" and "range" variables! 3) cs1[0] 1.0 4) cs1 = Signal(range, []) "domain" and "range" variables must have same size!
domain = np.arange(0, 1000, 100)
domain_a = np.linspace(0, 1, 10)
range = np.linspace(1, 10, domain.size)
data = zip(domain, range)
print('1) cs1 = Signal(range, domain)')
cs1 = Signal(range, domain)
print(cs1.name)
print(cs1)
print('\n')
print('2) cs1 = Signal(data)')
cs1 = Signal(data)
print(cs1)
print('\n')
print('3) cs1 = Signal(data, domain_a)')
cs1 = Signal(data, domain_a)
print(cs1)
print('\n')
print('4) cs1 = Signal(Signal(data))')
cs1 = Signal(Signal(data))
print(cs1)
print('\n')
print('5) cs1 = Signal(Series(range, domain))')
cs1 = Signal(Series(range, domain))
print(cs1)
print('\n')
print('6) cs1 = Signal(Series(range, domain, name="D65"))')
cs1 = Signal(Series(range, domain, name="D65"))
print(cs1.name)
print(cs1)
print('\n')
1) cs1 = Signal(range, domain) Signal (139872258696208) [[ 0. 1.] [ 100. 2.] [ 200. 3.] [ 300. 4.] [ 400. 5.] [ 500. 6.] [ 600. 7.] [ 700. 8.] [ 800. 9.] [ 900. 10.]] 2) cs1 = Signal(data) [[ 0. 1.] [ 100. 2.] [ 200. 3.] [ 300. 4.] [ 400. 5.] [ 500. 6.] [ 600. 7.] [ 700. 8.] [ 800. 9.] [ 900. 10.]] 3) cs1 = Signal(data, domain_a) [[ 0. 1. ] [ 0.11111111 2. ] [ 0.22222222 3. ] [ 0.33333333 4. ] [ 0.44444444 5. ] [ 0.55555556 6. ] [ 0.66666667 7. ] [ 0.77777778 8. ] [ 0.88888889 9. ] [ 1. 10. ]] 4) cs1 = Signal(Signal(data)) [[ 0. 1.] [ 100. 2.] [ 200. 3.] [ 300. 4.] [ 400. 5.] [ 500. 6.] [ 600. 7.] [ 700. 8.] [ 800. 9.] [ 900. 10.]] 5) cs1 = Signal(Series(range, domain)) [[ 0. 1.] [ 100. 2.] [ 200. 3.] [ 300. 4.] [ 400. 5.] [ 500. 6.] [ 600. 7.] [ 700. 8.] [ 800. 9.] [ 900. 10.]] 6) cs1 = Signal(Series(range, domain, name="D65")) D65 [[ 0. 1.] [ 100. 2.] [ 200. 3.] [ 300. 4.] [ 400. 5.] [ 500. 6.] [ 600. 7.] [ 700. 8.] [ 800. 9.] [ 900. 10.]]
domain = np.arange(0, 1000, 100)
range = np.linspace(1, 10, domain.size)
cs1 = Signal(range, domain)
print('1) id(cs1)')
print(id(cs1))
print(cs1.function)
print('\n')
cs2 = cs1.copy()
print('2) id(cs2)')
print(id(cs2))
print(cs2.function)
1) id(cs1) 139872194502224 <colour.algebra.extrapolation.Extrapolator object at 0x7f3688792810> 2) id(cs2) 139872194602512 <colour.algebra.extrapolation.Extrapolator object at 0x7f36887926d0>
domain = np.arange(0, 1000, 100)
range = np.linspace(1, 10, domain.size)
cs1 = Signal(range, domain)
print('1) cs1')
print(cs1)
print('\n')
print('2) cs1[150.25]')
print(cs1[150.25])
print('\n')
print('3) cs1[np.linspace(100, 400, 10)]')
print(cs1[np.linspace(100, 400, 10)])
print('\n')
print('4) cs1[0:3]')
print(cs1[0:3])
print('\n')
print('5) cs1[10] = np.pi')
cs1[10] = np.pi
print(cs1)
print('\n')
print('6) cs1[(200, 300)] = np.pi')
cs1[(200, 300)] = np.pi
print(cs1)
print('\n')
print('7) cs1[(0, 850)] = np.pi')
cs1[(0, 850)] = np.pi
print(cs1)
print('\n')
print('8) cs1[0:9] = np.pi')
cs1[0:9] = np.pi
print(cs1)
print('\n')
print('9) cs1[(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)] = np.pi')
cs1[(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)] = np.pi
print(cs1)
1) cs1 [[ 0. 1.] [ 100. 2.] [ 200. 3.] [ 300. 4.] [ 400. 5.] [ 500. 6.] [ 600. 7.] [ 700. 8.] [ 800. 9.] [ 900. 10.]] 2) cs1[150.25] 2.5025 3) cs1[np.linspace(100, 400, 10)] [ 2. 2.33333333 2.66666667 3. 3.33333333 3.66666667 4. 4.33333333 4.66666667 5. ] 4) cs1[0:3] [ 1. 2. 3.] 5) cs1[10] = np.pi [[ 0. 1. ] [ 10. 3.14159265] [ 100. 2. ] [ 200. 3. ] [ 300. 4. ] [ 400. 5. ] [ 500. 6. ] [ 600. 7. ] [ 700. 8. ] [ 800. 9. ] [ 900. 10. ]] 6) cs1[(200, 300)] = np.pi [[ 0. 1. ] [ 10. 3.14159265] [ 100. 2. ] [ 200. 3.14159265] [ 300. 3.14159265] [ 400. 5. ] [ 500. 6. ] [ 600. 7. ] [ 700. 8. ] [ 800. 9. ] [ 900. 10. ]] 7) cs1[(0, 850)] = np.pi [[ 0. 3.14159265] [ 10. 3.14159265] [ 100. 2. ] [ 200. 3.14159265] [ 300. 3.14159265] [ 400. 5. ] [ 500. 6. ] [ 600. 7. ] [ 700. 8. ] [ 800. 9. ] [ 850. 3.14159265] [ 900. 10. ]] 8) cs1[0:9] = np.pi [[ 0. 3.14159265] [ 10. 3.14159265] [ 100. 3.14159265] [ 200. 3.14159265] [ 300. 3.14159265] [ 400. 3.14159265] [ 500. 3.14159265] [ 600. 3.14159265] [ 700. 3.14159265] [ 800. 9. ] [ 850. 3.14159265] [ 900. 10. ]] 9) cs1[(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)] = np.pi [[ 0. 3.14159265] [ 1. 3.14159265] [ 2. 3.14159265] [ 3. 3.14159265] [ 4. 3.14159265] [ 5. 3.14159265] [ 6. 3.14159265] [ 7. 3.14159265] [ 8. 3.14159265] [ 9. 3.14159265] [ 10. 3.14159265] [ 100. 3.14159265] [ 200. 3.14159265] [ 300. 3.14159265] [ 400. 3.14159265] [ 500. 3.14159265] [ 600. 3.14159265] [ 700. 3.14159265] [ 800. 9. ] [ 850. 3.14159265] [ 900. 10. ]]
domain = np.arange(0, 1000, 100)
range = np.linspace(1, 10, domain.size)
cs1 = Signal(range, domain)
print('1) cs1')
print(cs1)
print('\n')
print('2) 110 in cs1')
print(110 in cs1)
print('\n')
print('3) (110, 1000) in cs1')
print((110, 1000) in cs1)
1) cs1 [[ 0. 1.] [ 100. 2.] [ 200. 3.] [ 300. 4.] [ 400. 5.] [ 500. 6.] [ 600. 7.] [ 700. 8.] [ 800. 9.] [ 900. 10.]] 2) 110 in cs1 True 3) (110, 1000) in cs1 False
domain = np.arange(0, 1000, 100)
range = np.linspace(1, 10, domain.size)
cs1 = Signal(range, domain)
cs2 = cs1.copy()
print('1) cs1 == cs2')
print(cs1 == cs2)
print('\n')
print('2) cs1[0] = 0.1; cs1 == cs2')
cs1[0] = 0.1
print(cs1 == cs2)
print('\n')
print('3)cs1.interpolation_method = "Null"; cs1 == cs2')
cs2 = cs1.copy()
cs1.interpolation_method = 'Null'
print(cs1 == cs2)
1) cs1 == cs2 True 2) cs1[0] = 0.1; cs1 == cs2 False 3)cs1.interpolation_method = "Null"; cs1 == cs2 False
domain = np.arange(0, 1000, 100)
range = np.linspace(1, 10, domain.size)
cs1 = Signal(range, domain, interpolation_method='Null')
print('1) cs1')
print(cs1)
print('\n')
print('2) cs1[100]')
print(cs1[100])
print('\n')
print('3) cs1[100.1, 500]')
print(cs1[100.1, 500])
print('\n')
print('4) cs1[100.0000001]')
print(cs1[100.0000001])
1) cs1 [[ 0. 1.] [ 100. 2.] [ 200. 3.] [ 300. 4.] [ 400. 5.] [ 500. 6.] [ 600. 7.] [ 700. 8.] [ 800. 9.] [ 900. 10.]] 2) cs1[100] 2.0 3) cs1[100.1, 500] [ nan 6.] 4) cs1[100.0000001] 2.0
domain = np.arange(0, 1000, 100)
range = np.linspace(1, 10, domain.size)
cs1 = Signal(range, domain)
print('1) cs1')
print(cs1)
print('\n')
print('2) cs1.uncertainty((0.1, 150, 200))')
print(cs1.uncertainty((0.1, 150, 200)))
1) cs1 [[ 0. 1.] [ 100. 2.] [ 200. 3.] [ 300. 4.] [ 400. 5.] [ 500. 6.] [ 600. 7.] [ 700. 8.] [ 800. 9.] [ 900. 10.]] 2) cs1.uncertainty((0.1, 150, 200)) [ 0.1 50. 0. ]
domain = np.arange(0, 1000, 100)
range = np.linspace(1, 10, domain.size)
cs1 = Signal(range, domain)
cs2 = Signal(range, domain)
print('1) cs1')
print(cs1)
print('\n')
print('2) cs2')
print(cs2)
print('\n')
print('3) cs1 += cs2')
cs1 += cs2
print(cs1)
print('\n')
print('4) cs1 += 1')
cs1 += 1
print(cs1)
print('\n')
print('5) cs1 -= np.ones(domain.size)')
cs1 -= np.ones(domain.size)
print(cs1)
print('\n')
print('6) cs2 = cs1 + cs1')
cs2 = cs1 + cs1
print(cs2)
print('\n')
1) cs1 [[ 0. 1.] [ 100. 2.] [ 200. 3.] [ 300. 4.] [ 400. 5.] [ 500. 6.] [ 600. 7.] [ 700. 8.] [ 800. 9.] [ 900. 10.]] 2) cs2 [[ 0. 1.] [ 100. 2.] [ 200. 3.] [ 300. 4.] [ 400. 5.] [ 500. 6.] [ 600. 7.] [ 700. 8.] [ 800. 9.] [ 900. 10.]] 3) cs1 += cs2 [[ 0. 2.] [ 100. 4.] [ 200. 6.] [ 300. 8.] [ 400. 10.] [ 500. 12.] [ 600. 14.] [ 700. 16.] [ 800. 18.] [ 900. 20.]] 4) cs1 += 1 [[ 0. 3.] [ 100. 5.] [ 200. 7.] [ 300. 9.] [ 400. 11.] [ 500. 13.] [ 600. 15.] [ 700. 17.] [ 800. 19.] [ 900. 21.]] 5) cs1 -= np.ones(domain.size) [[ 0. 2.] [ 100. 4.] [ 200. 6.] [ 300. 8.] [ 400. 10.] [ 500. 12.] [ 600. 14.] [ 700. 16.] [ 800. 18.] [ 900. 20.]] 6) cs2 = cs1 + cs1 [[ 0. 4.] [ 100. 8.] [ 200. 12.] [ 300. 16.] [ 400. 20.] [ 500. 24.] [ 600. 28.] [ 700. 32.] [ 800. 36.] [ 900. 40.]]
domain = np.arange(0, 1000, 100)
range = np.linspace(1, 10, domain.size)
cs1 = Signal(range, domain)
cs2 = Signal(range, domain + 400)
print('1) cs1')
print(cs1)
print('\n')
print('2) cs2')
print(cs2)
print('\n')
print('3) cs1 += cs2')
cs1 += cs2
print(cs1)
print('\n')
print('4) cs1 += 1')
cs1 += 1
print(cs1)
print('\n')
print('5) cs1 -= np.ones(cs1.domain.size)')
cs1 -= np.ones(cs1.domain.size)
print(cs1)
print('\n')
print('6) cs2 = cs1 + cs1')
cs2 = cs1 + cs1
print(cs2)
print('\n')
print('7) Cubic Spline interpolation fails with Nan(s) values.')
cs1.interpolation_method = 'Cubic Spline'
cs2 = cs1 + cs1
print(cs2)
print('\n')
print('8) Cubic Spline interpolation with filled Nan(s) values.')
cs1.fill_nan('constant')
cs2 = cs1 + cs1
print(cs2)
1) cs1 [[ 0. 1.] [ 100. 2.] [ 200. 3.] [ 300. 4.] [ 400. 5.] [ 500. 6.] [ 600. 7.] [ 700. 8.] [ 800. 9.] [ 900. 10.]] 2) cs2 [[ 4.00000000e+02 1.00000000e+00] [ 5.00000000e+02 2.00000000e+00] [ 6.00000000e+02 3.00000000e+00] [ 7.00000000e+02 4.00000000e+00] [ 8.00000000e+02 5.00000000e+00] [ 9.00000000e+02 6.00000000e+00] [ 1.00000000e+03 7.00000000e+00] [ 1.10000000e+03 8.00000000e+00] [ 1.20000000e+03 9.00000000e+00] [ 1.30000000e+03 1.00000000e+01]] 3) cs1 += cs2 [[ 0. nan] [ 100. nan] [ 200. nan] [ 300. nan] [ 400. 6.] [ 500. 8.] [ 600. 10.] [ 700. 12.] [ 800. 14.] [ 900. 16.] [ 1000. nan] [ 1100. nan] [ 1200. nan] [ 1300. nan]] 4) cs1 += 1 [[ 0. nan] [ 100. nan] [ 200. nan] [ 300. nan] [ 400. 7.] [ 500. 9.] [ 600. 11.] [ 700. 13.] [ 800. 15.] [ 900. 17.] [ 1000. nan] [ 1100. nan] [ 1200. nan] [ 1300. nan]] 5) cs1 -= np.ones(cs1.domain.size) [[ 0. nan] [ 100. nan] [ 200. nan] [ 300. nan] [ 400. 6.] [ 500. 8.] [ 600. 10.] [ 700. 12.] [ 800. 14.] [ 900. 16.] [ 1000. nan] [ 1100. nan] [ 1200. nan] [ 1300. nan]] 6) cs2 = cs1 + cs1 [[ 0. nan] [ 100. nan] [ 200. nan] [ 300. nan] [ 400. 12.] [ 500. 16.] [ 600. 20.] [ 700. 24.] [ 800. 28.] [ 900. nan] [ 1000. nan] [ 1100. nan] [ 1200. nan] [ 1300. nan]] 7) Cubic Spline interpolation fails with Nan(s) values. [[ 0. nan] [ 100. nan] [ 200. nan] [ 300. nan] [ 400. nan] [ 500. nan] [ 600. nan] [ 700. nan] [ 800. nan] [ 900. nan] [ 1000. nan] [ 1100. nan] [ 1200. nan] [ 1300. nan]] 8) Cubic Spline interpolation with filled Nan(s) values. [[ 0.00000000e+00 1.21014310e-14] [ 1.00000000e+02 -5.41233725e-15] [ 2.00000000e+02 -2.72698530e-15] [ 3.00000000e+02 1.24067423e-14] [ 4.00000000e+02 1.20000000e+01] [ 5.00000000e+02 1.60000000e+01] [ 6.00000000e+02 2.00000000e+01] [ 7.00000000e+02 2.40000000e+01] [ 8.00000000e+02 2.80000000e+01] [ 9.00000000e+02 3.20000000e+01] [ 1.00000000e+03 -8.88178420e-16] [ 1.10000000e+03 -6.55031585e-15] [ 1.20000000e+03 -5.19029264e-15] [ 1.30000000e+03 -7.85482790e-15]]
/colour-science/colour/colour/utilities/verbose.py:125: UserWarning: "range" variable is not finite, unpredictable results may occur! [ nan nan nan nan 7. 9. 11. 13. 15. 17. nan nan nan nan] warn(*args, **kwargs) /colour-science/colour/colour/utilities/verbose.py:125: UserWarning: "range" variable is not finite, unpredictable results may occur! [ nan nan nan nan 6. 8. 10. 12. 14. 16. nan nan nan nan] warn(*args, **kwargs)
from colour.colorimetry.dataset.illuminants.spds import ILLUMINANTS_RELATIVE_SPDS_DATA
class Spectrum(Signal):
def __init__(self, *args, **kwargs):
# TODO: Define relevant default for spectral computations.
settings = {
'interpolation_method': 'Pchip',
'extrapolation_options': {'left': None, 'right': None}}
settings.update(kwargs)
super(Spectrum, self).__init__(*args, **settings)
@property
def wavelengths(self):
return self.domain
@wavelengths.setter
def wavelengths(self, value):
self.domain = value
@property
def values(self):
return self.range
@values.setter
def values(self, value):
self.range = value
@property
def title(self):
if self.__title is not None:
return self.__title
else:
return self.__name
@title.setter
def title(self, value):
if value is not None:
assert type(value) in (str, unicode), ( # noqa
('"{0}" attribute: "{1}" type is not '
'"str" or "unicode"!').format('title', value))
self.__title = value
def normalise(self, factor=100):
self.range = (self.range * (1 / np.max(self.range))) * factor
return self
s1 = Spectrum(ILLUMINANTS_RELATIVE_SPDS_DATA['A'])
print('1) repr(s1)')
print(repr(s1))
1) repr(s1) Spectrum([[ 300. , 0.930483], [ 305. , 1.12821 ], [ 310. , 1.35769 ], [ 315. , 1.62219 ], [ 320. , 1.92508 ], [ 325. , 2.2698 ], [ 330. , 2.65981 ], [ 335. , 3.09861 ], [ 340. , 3.58968 ], [ 345. , 4.13648 ], [ 350. , 4.74238 ], [ 355. , 5.4107 ], [ 360. , 6.14462 ], [ 365. , 6.9472 ], [ 370. , 7.82135 ], [ 375. , 8.7698 ], [ 380. , 9.7951 ], [ 385. , 10.8996 ], [ 390. , 12.0853 ], [ 395. , 13.3543 ], [ 400. , 14.708 ], [ 405. , 16.148 ], [ 410. , 17.6753 ], [ 415. , 19.2907 ], [ 420. , 20.995 ], [ 425. , 22.7883 ], [ 430. , 24.6709 ], [ 435. , 26.6425 ], [ 440. , 28.7027 ], [ 445. , 30.8508 ], [ 450. , 33.0859 ], [ 455. , 35.4068 ], [ 460. , 37.8121 ], [ 465. , 40.3002 ], [ 470. , 42.8693 ], [ 475. , 45.5174 ], [ 480. , 48.2423 ], [ 485. , 51.0418 ], [ 490. , 53.9132 ], [ 495. , 56.8539 ], [ 500. , 59.8611 ], [ 505. , 62.932 ], [ 510. , 66.0635 ], [ 515. , 69.2525 ], [ 520. , 72.4959 ], [ 525. , 75.7903 ], [ 530. , 79.1326 ], [ 535. , 82.5193 ], [ 540. , 85.947 ], [ 545. , 89.4124 ], [ 550. , 92.912 ], [ 555. , 96.4423 ], [ 560. , 100. ], [ 565. , 103.582 ], [ 570. , 107.184 ], [ 575. , 110.803 ], [ 580. , 114.436 ], [ 585. , 118.08 ], [ 590. , 121.731 ], [ 595. , 125.386 ], [ 600. , 129.043 ], [ 605. , 132.697 ], [ 610. , 136.346 ], [ 615. , 139.988 ], [ 620. , 143.618 ], [ 625. , 147.235 ], [ 630. , 150.836 ], [ 635. , 154.418 ], [ 640. , 157.979 ], [ 645. , 161.516 ], [ 650. , 165.028 ], [ 655. , 168.51 ], [ 660. , 171.963 ], [ 665. , 175.383 ], [ 670. , 178.769 ], [ 675. , 182.118 ], [ 680. , 185.429 ], [ 685. , 188.701 ], [ 690. , 191.931 ], [ 695. , 195.118 ], [ 700. , 198.261 ], [ 705. , 201.359 ], [ 710. , 204.409 ], [ 715. , 207.411 ], [ 720. , 210.365 ], [ 725. , 213.268 ], [ 730. , 216.12 ], [ 735. , 218.92 ], [ 740. , 221.667 ], [ 745. , 224.361 ], [ 750. , 227. ], [ 755. , 229.585 ], [ 760. , 232.115 ], [ 765. , 234.589 ], [ 770. , 237.008 ], [ 775. , 239.37 ], [ 780. , 241.675 ]])
s1 = Spectrum(ILLUMINANTS_RELATIVE_SPDS_DATA['A'])
print('1) s1.normalise()')
print(s1.normalise())
1) s1.normalise() [[ 3.00000000e+02 3.85014172e-01] [ 3.05000000e+02 4.66829420e-01] [ 3.10000000e+02 5.61783387e-01] [ 3.15000000e+02 6.71227889e-01] [ 3.20000000e+02 7.96557360e-01] [ 3.25000000e+02 9.39195200e-01] [ 3.30000000e+02 1.10057308e+00] [ 3.35000000e+02 1.28213924e+00] [ 3.40000000e+02 1.48533361e+00] [ 3.45000000e+02 1.71158788e+00] [ 3.50000000e+02 1.96229647e+00] [ 3.55000000e+02 2.23883314e+00] [ 3.60000000e+02 2.54251371e+00] [ 3.65000000e+02 2.87460432e+00] [ 3.70000000e+02 3.23630909e+00] [ 3.75000000e+02 3.62875763e+00] [ 3.80000000e+02 4.05300507e+00] [ 3.85000000e+02 4.51002379e+00] [ 3.90000000e+02 5.00064136e+00] [ 3.95000000e+02 5.52572670e+00] [ 4.00000000e+02 6.08585911e+00] [ 4.05000000e+02 6.68170063e+00] [ 4.10000000e+02 7.31366505e+00] [ 4.15000000e+02 7.98208338e+00] [ 4.20000000e+02 8.68728665e+00] [ 4.25000000e+02 9.42931623e+00] [ 4.30000000e+02 1.02082963e+01] [ 4.35000000e+02 1.10241026e+01] [ 4.40000000e+02 1.18765698e+01] [ 4.45000000e+02 1.27654081e+01] [ 4.50000000e+02 1.36902452e+01] [ 4.55000000e+02 1.46505845e+01] [ 4.60000000e+02 1.56458467e+01] [ 4.65000000e+02 1.66753698e+01] [ 4.70000000e+02 1.77384090e+01] [ 4.75000000e+02 1.88341368e+01] [ 4.80000000e+02 1.99616427e+01] [ 4.85000000e+02 2.11200166e+01] [ 4.90000000e+02 2.23081411e+01] [ 4.95000000e+02 2.35249405e+01] [ 5.00000000e+02 2.47692562e+01] [ 5.05000000e+02 2.60399297e+01] [ 5.10000000e+02 2.73356781e+01] [ 5.15000000e+02 2.86552188e+01] [ 5.20000000e+02 2.99972691e+01] [ 5.25000000e+02 3.13604221e+01] [ 5.30000000e+02 3.27433951e+01] [ 5.35000000e+02 3.41447398e+01] [ 5.40000000e+02 3.55630496e+01] [ 5.45000000e+02 3.69969587e+01] [ 5.50000000e+02 3.84450191e+01] [ 5.55000000e+02 3.99057826e+01] [ 5.60000000e+02 4.13778835e+01] [ 5.65000000e+02 4.28600393e+01] [ 5.70000000e+02 4.43504707e+01] [ 5.75000000e+02 4.58479363e+01] [ 5.80000000e+02 4.73511948e+01] [ 5.85000000e+02 4.88590049e+01] [ 5.90000000e+02 5.03697114e+01] [ 5.95000000e+02 5.18820730e+01] [ 6.00000000e+02 5.33952622e+01] [ 6.05000000e+02 5.49072101e+01] [ 6.10000000e+02 5.64170891e+01] [ 6.15000000e+02 5.79240716e+01] [ 6.20000000e+02 5.94260888e+01] [ 6.25000000e+02 6.09227268e+01] [ 6.30000000e+02 6.24127444e+01] [ 6.35000000e+02 6.38949002e+01] [ 6.40000000e+02 6.53683666e+01] [ 6.45000000e+02 6.68319023e+01] [ 6.50000000e+02 6.82850936e+01] [ 6.55000000e+02 6.97258715e+01] [ 6.60000000e+02 7.11546498e+01] [ 6.65000000e+02 7.25697735e+01] [ 6.70000000e+02 7.39708286e+01] [ 6.75000000e+02 7.53565739e+01] [ 6.80000000e+02 7.67265956e+01] [ 6.85000000e+02 7.80804800e+01] [ 6.90000000e+02 7.94169856e+01] [ 6.95000000e+02 8.07356988e+01] [ 7.00000000e+02 8.20362056e+01] [ 7.05000000e+02 8.33180925e+01] [ 7.10000000e+02 8.45801179e+01] [ 7.15000000e+02 8.58222820e+01] [ 7.20000000e+02 8.70445847e+01] [ 7.25000000e+02 8.82457846e+01] [ 7.30000000e+02 8.94258819e+01] [ 7.35000000e+02 9.05844626e+01] [ 7.40000000e+02 9.17211131e+01] [ 7.45000000e+02 9.28358332e+01] [ 7.50000000e+02 9.39277956e+01] [ 7.55000000e+02 9.49974139e+01] [ 7.60000000e+02 9.60442743e+01] [ 7.65000000e+02 9.70679632e+01] [ 7.70000000e+02 9.80688942e+01] [ 7.75000000e+02 9.90462398e+01] [ 7.80000000e+02 1.00000000e+02]]