colour.continuous.Signal#

class colour.continuous.Signal(data: ArrayLike | dict | Self | Series | ValuesView | None = None, domain: ArrayLike | KeysView | None = None, **kwargs: Any)[source]#

Bases: AbstractContinuousFunction

Define the base class for a continuous signal.

The class implements the Signal.function() method so that evaluating the function for any independent domain variable \(x \in\mathbb{R}\) returns a corresponding range variable \(y \in\mathbb{R}\). It adopts an interpolating function encapsulated inside an extrapolating function. The resulting function independent domain, stored as discrete values in the colour.continuous.Signal.domain property corresponds with the function dependent and already known range stored in the colour.continuous.Signal.range property.

Important

Specific documentation about getting, setting, indexing and slicing the continuous signal values is available in the Spectral Representation and Continuous Signal section.

Parameters:
  • data (ArrayLike | dict | Self | Series | ValuesView | None) – Data to be stored in the continuous signal.

  • domain (ArrayLike | KeysView | None) – Values to initialise the colour.continuous.Signal.domain attribute with. If both data and domain arguments are defined, the latter will be used to initialise the colour.continuous.Signal.domain property.

  • dtype – Floating point data type.

  • extrapolator – Extrapolator class type to use as extrapolating function.

  • extrapolator_kwargs – Arguments to use when instantiating the extrapolating function.

  • interpolator – Interpolator class type to use as interpolating function.

  • interpolator_kwargs – Arguments to use when instantiating the interpolating function.

  • name – Continuous signal name.

  • kwargs (Any)

Attributes

Methods

Examples

Instantiation with implicit domain:

>>> range_ = np.linspace(10, 100, 10)
>>> print(Signal(range_))
[[   0.   10.]
 [   1.   20.]
 [   2.   30.]
 [   3.   40.]
 [   4.   50.]
 [   5.   60.]
 [   6.   70.]
 [   7.   80.]
 [   8.   90.]
 [   9.  100.]]

Instantiation with explicit domain:

>>> domain = np.arange(100, 1100, 100)
>>> print(Signal(range_, domain))
[[  100.    10.]
 [  200.    20.]
 [  300.    30.]
 [  400.    40.]
 [  500.    50.]
 [  600.    60.]
 [  700.    70.]
 [  800.    80.]
 [  900.    90.]
 [ 1000.   100.]]

Instantiation with a dict:

>>> print(Signal(dict(zip(domain, range_))))
[[  100.    10.]
 [  200.    20.]
 [  300.    30.]
 [  400.    40.]
 [  500.    50.]
 [  600.    60.]
 [  700.    70.]
 [  800.    80.]
 [  900.    90.]
 [ 1000.   100.]]

Instantiation with a Pandas pandas.Series:

>>> if is_pandas_installed():
...     from pandas import Series
...
...     print(Signal(Series(dict(zip(domain, range_)))))
[[  100.    10.]
 [  200.    20.]
 [  300.    30.]
 [  400.    40.]
 [  500.    50.]
 [  600.    60.]
 [  700.    70.]
 [  800.    80.]
 [  900.    90.]
 [ 1000.   100.]]

Retrieving domain y variable for arbitrary range x variable:

>>> x = 150
>>> range_ = np.sin(np.linspace(0, 1, 10))
>>> Signal(range_, domain)[x]
0.0359701...
>>> x = np.linspace(100, 1000, 3)
>>> Signal(range_, domain)[x]
array([  ...,   4.7669395...e-01,   8.4147098...e-01])

Using an alternative interpolating function:

>>> x = 150
>>> from colour.algebra import CubicSplineInterpolator
>>> Signal(range_, domain, interpolator=CubicSplineInterpolator)[
...     x
... ]
0.0555274...
>>> x = np.linspace(100, 1000, 3)
>>> Signal(range_, domain, interpolator=CubicSplineInterpolator)[
...     x
... ]
array([ 0.        ,  0.4794253...,  0.8414709...])
__init__(data: ArrayLike | dict | Self | Series | ValuesView | None = None, domain: ArrayLike | KeysView | None = None, **kwargs: Any) None[source]#
Parameters:
Return type:

None

property dtype: Type[DTypeFloat]#

Getter and setter for the continuous signal dtype.

Parameters:

value – Value to set the continuous signal dtype with.

Returns:

Continuous signal dtype.

Return type:

Type[DTypeFloat]

property domain: NDArrayFloat#

Getter and setter for the continuous signal’s independent domain variable \(x\).

Parameters:

value – Value to set the continuous signal independent domain variable \(x\) with.

Returns:

Continuous signal independent domain variable \(x\).

Return type:

numpy.ndarray

property range: NDArrayFloat#

Getter and setter for the continuous signal’s range variable \(y\).

Parameters:

value – Value to set the continuous signal’s range variable \(y\) with.

Returns:

Continuous signal’s range variable \(y\).

Return type:

numpy.ndarray

property interpolator: Type[ProtocolInterpolator]#

Getter and setter for the continuous signal interpolator type.

Parameters:

value – Value to set the continuous signal interpolator type with.

Returns:

Continuous signal interpolator type.

Return type:

Type[ProtocolInterpolator]

property interpolator_kwargs: dict#

Getter and setter for the interpolator instantiation time arguments.

Parameters:

value – Value to set the continuous signal interpolator instantiation time arguments to.

Returns:

Continuous signal interpolator instantiation time arguments.

Return type:

dict

property extrapolator: Type[ProtocolExtrapolator]#

Getter and setter for the continuous signal extrapolator type.

Parameters:

value – Value to set the continuous signal extrapolator type with.

Returns:

Continuous signal extrapolator type.

Return type:

Type[ProtocolExtrapolator]

property extrapolator_kwargs: dict#

Getter and setter for the continuous signal extrapolator instantiation time arguments.

Parameters:

value – Value to set the continuous signal extrapolator instantiation time arguments to.

Returns:

Continuous signal extrapolator instantiation time arguments.

Return type:

dict

property function: Callable#

Getter for the continuous signal callable.

Returns:

Continuous signal callable.

Return type:

Callable

__str__() str[source]#

Return a formatted string representation of the continuous signal.

Returns:

Formatted string representation.

Return type:

str

Examples

>>> range_ = np.linspace(10, 100, 10)
>>> print(Signal(range_))
[[   0.   10.]
 [   1.   20.]
 [   2.   30.]
 [   3.   40.]
 [   4.   50.]
 [   5.   60.]
 [   6.   70.]
 [   7.   80.]
 [   8.   90.]
 [   9.  100.]]
__repr__() str[source]#

Return an evaluable string representation of the continuous signal.

Returns:

Evaluable string representation.

Return type:

str

Examples

>>> range_ = np.linspace(10, 100, 10)
>>> Signal(range_)
Signal([[   0.,   10.],
        [   1.,   20.],
        [   2.,   30.],
        [   3.,   40.],
        [   4.,   50.],
        [   5.,   60.],
        [   6.,   70.],
        [   7.,   80.],
        [   8.,   90.],
        [   9.,  100.]],
       KernelInterpolator,
       {},
       Extrapolator,
       {'method': 'Constant', 'left': nan, 'right': nan})
__hash__() int[source]#

Compute the hash of the continuous signal.

Returns:

Object hash.

Return type:

int

__getitem__(x: TypeAliasForwardRef('ArrayLike') | slice) NDArrayFloat[source]#

Return the corresponding range variable \(y\) for the specified independent domain variable \(x\).

Parameters:

x (TypeAliasForwardRef('ArrayLike') | slice) – Independent domain variable \(x\).

Returns:

Variable \(y\) range value.

Return type:

numpy.ndarray

Examples

>>> range_ = np.linspace(10, 100, 10)
>>> signal = Signal(range_)
>>> print(signal)
[[   0.   10.]
 [   1.   20.]
 [   2.   30.]
 [   3.   40.]
 [   4.   50.]
 [   5.   60.]
 [   6.   70.]
 [   7.   80.]
 [   8.   90.]
 [   9.  100.]]
>>> signal[0]
10.0
>>> signal[np.array([0, 1, 2])]
array([ 10.,  20.,  30.])
>>> signal[0:3]
array([ 10.,  20.,  30.])
>>> signal[np.linspace(0, 5, 5)]
array([ 10.        ,  22.8348902...,  34.8004492...,  47.5535392...,  60.        ])
__setitem__(x: TypeAliasForwardRef('ArrayLike') | slice, y: ArrayLike) None[source]#

Set the corresponding range variable \(y\) for the specified independent domain variable \(x\).

Parameters:
  • x (TypeAliasForwardRef('ArrayLike') | slice) – Independent domain variable \(x\).

  • y (ArrayLike) – Corresponding range variable \(y\).

Return type:

None

Examples

>>> range_ = np.linspace(10, 100, 10)
>>> signal = Signal(range_)
>>> print(signal)
[[   0.   10.]
 [   1.   20.]
 [   2.   30.]
 [   3.   40.]
 [   4.   50.]
 [   5.   60.]
 [   6.   70.]
 [   7.   80.]
 [   8.   90.]
 [   9.  100.]]
>>> signal[0] = 20
>>> signal[0]
20.0
>>> signal[np.array([0, 1, 2])] = 30
>>> signal[np.array([0, 1, 2])]
array([ 30.,  30.,  30.])
>>> signal[0:3] = 40
>>> signal[0:3]
array([ 40.,  40.,  40.])
>>> signal[np.linspace(0, 5, 5)] = 50
>>> print(signal)
[[   0.     50.  ]
 [   1.     40.  ]
 [   1.25   50.  ]
 [   2.     40.  ]
 [   2.5    50.  ]
 [   3.     40.  ]
 [   3.75   50.  ]
 [   4.     50.  ]
 [   5.     50.  ]
 [   6.     70.  ]
 [   7.     80.  ]
 [   8.     90.  ]
 [   9.    100.  ]]
>>> signal[np.array([0, 1, 2])] = np.array([10, 20, 30])
>>> print(signal)
[[   0.     10.  ]
 [   1.     20.  ]
 [   1.25   50.  ]
 [   2.     30.  ]
 [   2.5    50.  ]
 [   3.     40.  ]
 [   3.75   50.  ]
 [   4.     50.  ]
 [   5.     50.  ]
 [   6.     70.  ]
 [   7.     80.  ]
 [   8.     90.  ]
 [   9.    100.  ]]
__contains__(x: TypeAliasForwardRef('ArrayLike') | slice) bool[source]#

Determine whether the continuous signal contains the specified independent domain variable \(x\).

Parameters:

x (TypeAliasForwardRef('ArrayLike') | slice) – Independent domain variable \(x\).

Returns:

Whether \(x\) domain value is contained.

Return type:

bool

Examples

>>> range_ = np.linspace(10, 100, 10)
>>> signal = Signal(range_)
>>> 0 in signal
True
>>> 0.5 in signal
True
>>> 1000 in signal
False
__eq__(other: object) bool[source]#

Determine whether the continuous signal equals the specified object.

Parameters:

other (object) – Object to determine for equality with the continuous signal.

Returns:

Whether the specified object is equal to the continuous signal.

Return type:

bool

Examples

>>> range_ = np.linspace(10, 100, 10)
>>> signal_1 = Signal(range_)
>>> signal_2 = Signal(range_)
>>> signal_1 == signal_2
True
>>> signal_2[0] = 20
>>> signal_1 == signal_2
False
>>> signal_2[0] = 10
>>> signal_1 == signal_2
True
>>> from colour.algebra import CubicSplineInterpolator
>>> signal_2.interpolator = CubicSplineInterpolator
>>> signal_1 == signal_2
False
__ne__(other: object) bool[source]#

Determine whether the continuous signal is not equal to the specified other object.

Parameters:

other (object) – Object to determine whether it is not equal to the continuous signal.

Returns:

Whether the specified object is not equal to the continuous signal.

Return type:

bool

Examples

>>> range_ = np.linspace(10, 100, 10)
>>> signal_1 = Signal(range_)
>>> signal_2 = Signal(range_)
>>> signal_1 != signal_2
False
>>> signal_2[0] = 20
>>> signal_1 != signal_2
True
>>> signal_2[0] = 10
>>> signal_1 != signal_2
False
>>> from colour.algebra import CubicSplineInterpolator
>>> signal_2.interpolator = CubicSplineInterpolator
>>> signal_1 != signal_2
True
arithmetical_operation(a: ArrayLike | AbstractContinuousFunction, operation: Literal['+', '-', '*', '/', '**'], in_place: bool = False) AbstractContinuousFunction[source]#

Perform the specified arithmetical operation with operand \(a\).

The operation can be performed either on a copy of the signal or in-place.

Parameters:
  • a (ArrayLike | AbstractContinuousFunction) – Operand \(a\). Can be a numeric value, array-like object, or another continuous function instance.

  • operation (Literal['+', '-', '*', '/', '**']) – Arithmetical operation to perform. Supported operations are addition ("+"), subtraction ("-"), multiplication ("*"), division ("/"), and exponentiation ("**").

  • in_place (bool) – Whether the operation is performed in-place on the current signal instance. Default is False.

Returns:

Continuous signal after the arithmetical operation. If in_place is True, returns the modified instance; otherwise returns a new instance.

Return type:

colour.continuous.Signal

Examples

Adding a single numeric variable:

>>> range_ = np.linspace(10, 100, 10)
>>> signal_1 = Signal(range_)
>>> print(signal_1)
[[   0.   10.]
 [   1.   20.]
 [   2.   30.]
 [   3.   40.]
 [   4.   50.]
 [   5.   60.]
 [   6.   70.]
 [   7.   80.]
 [   8.   90.]
 [   9.  100.]]
>>> print(signal_1.arithmetical_operation(10, "+", True))
[[   0.   20.]
 [   1.   30.]
 [   2.   40.]
 [   3.   50.]
 [   4.   60.]
 [   5.   70.]
 [   6.   80.]
 [   7.   90.]
 [   8.  100.]
 [   9.  110.]]

Adding an ArrayLike variable:

>>> a = np.linspace(10, 100, 10)
>>> print(signal_1.arithmetical_operation(a, "+", True))
[[   0.   30.]
 [   1.   50.]
 [   2.   70.]
 [   3.   90.]
 [   4.  110.]
 [   5.  130.]
 [   6.  150.]
 [   7.  170.]
 [   8.  190.]
 [   9.  210.]]

Adding a colour.continuous.Signal class:

>>> signal_2 = Signal(range_)
>>> print(signal_1.arithmetical_operation(signal_2, "+", True))
[[   0.   40.]
 [   1.   70.]
 [   2.  100.]
 [   3.  130.]
 [   4.  160.]
 [   5.  190.]
 [   6.  220.]
 [   7.  250.]
 [   8.  280.]
 [   9.  310.]]
static signal_unpack_data(data: ArrayLike | dict | Series | Signal | ValuesView | None, domain: ArrayLike | KeysView | None = None, dtype: Type[DTypeFloat] | None = None) tuple[source]#

Unpack specified data for continuous signal instantiation.

Parameters:
Returns:

Independent domain variable \(x\) and corresponding range variable \(y\) unpacked for continuous signal instantiation.

Return type:

tuple

Examples

Unpacking using implicit domain:

>>> range_ = np.linspace(10, 100, 10)
>>> domain, range_ = Signal.signal_unpack_data(range_)
>>> print(domain)
[ 0.  1.  2.  3.  4.  5.  6.  7.  8.  9.]
>>> print(range_)
[  10.   20.   30.   40.   50.   60.   70.   80.   90.  100.]

Unpacking using explicit domain:

>>> domain = np.arange(100, 1100, 100)
>>> domain, range = Signal.signal_unpack_data(range_, domain)
>>> print(domain)
[  100.   200.   300.   400.   500.   600.   700.   800.   900.  1000.]
>>> print(range_)
[  10.   20.   30.   40.   50.   60.   70.   80.   90.  100.]

Unpacking using a dict:

>>> domain, range_ = Signal.signal_unpack_data(dict(zip(domain, range_)))
>>> print(domain)
[  100.   200.   300.   400.   500.   600.   700.   800.   900.  1000.]
>>> print(range_)
[  10.   20.   30.   40.   50.   60.   70.   80.   90.  100.]

Unpacking using a Pandas pandas.Series:

>>> if is_pandas_installed():
...     from pandas import Series
...
...     domain, range = Signal.signal_unpack_data(
...         Series(dict(zip(domain, range_)))
...     )
...
>>> print(domain)
[  100.   200.   300.   400.   500.   600.   700.   800.   900.  1000.]
>>> print(range_)
[  10.   20.   30.   40.   50.   60.   70.   80.   90.  100.]

Unpacking using a colour.continuous.Signal class:

>>> domain, range_ = Signal.signal_unpack_data(Signal(range_, domain))
>>> print(domain)
[  100.   200.   300.   400.   500.   600.   700.   800.   900.  1000.]
>>> print(range_)
[  10.   20.   30.   40.   50.   60.   70.   80.   90.  100.]
fill_nan(method: Literal['Constant', 'Interpolation'] | str = 'Interpolation', default: Real = 0) Signal[source]#

Fill NaNs in independent domain variable \(x\) and corresponding range variable \(y\) using the specified method.

Parameters:
  • method (Literal['Constant', 'Interpolation'] | str) – Interpolation method linearly interpolates through the NaNs, Constant method replaces NaNs with default.

  • default (Real) – Value to use with the Constant method.

Returns:

Continuous signal with NaN values filled.

Return type:

colour.continuous.Signal

Examples

>>> range_ = np.linspace(10, 100, 10)
>>> signal = Signal(range_)
>>> signal[3:7] = np.nan
>>> print(signal)
[[   0.   10.]
 [   1.   20.]
 [   2.   30.]
 [   3.   nan]
 [   4.   nan]
 [   5.   nan]
 [   6.   nan]
 [   7.   80.]
 [   8.   90.]
 [   9.  100.]]
>>> print(signal.fill_nan())
[[   0.   10.]
 [   1.   20.]
 [   2.   30.]
 [   3.   40.]
 [   4.   50.]
 [   5.   60.]
 [   6.   70.]
 [   7.   80.]
 [   8.   90.]
 [   9.  100.]]
>>> signal[3:7] = np.nan
>>> print(signal.fill_nan(method="Constant"))
[[   0.   10.]
 [   1.   20.]
 [   2.   30.]
 [   3.    0.]
 [   4.    0.]
 [   5.    0.]
 [   6.    0.]
 [   7.   80.]
 [   8.   90.]
 [   9.  100.]]
to_series() Series[source]#

Convert the continuous signal to a Pandas pandas.Series class instance.

Returns:

Continuous signal as a Pandas pandas.Series class instance.

Return type:

pandas.Series

Examples

>>> if is_pandas_installed():
...     range_ = np.linspace(10, 100, 10)
...     signal = Signal(range_)
...     print(signal.to_series())
0.0     10.0
1.0     20.0
2.0     30.0
3.0     40.0
4.0     50.0
5.0     60.0
6.0     70.0
7.0     80.0
8.0     90.0
9.0    100.0
Name: Signal (...), dtype: float64