.. currentmodule:: scikits.timeseries

.. _timeseries_operations:

Arithmetic and comparison operations
------------------------------------

The unary and binary operations defined in :mod:`numpy` or :mod:`numpy.ma`
can be directly applied to :class:`TimeSeries` objects.

.. note::
   If possible, it is recommended to use the :mod:`numpy.ma` function instead
   of its standard numpy counterpart.
   The reason is that the :meth:`reduce` and :meth:`accumulate` methods of
   most numpy.ufuncs (such as :func:`~numpy.add` or :func:`~numpy.multiply`)
   cannot properly handle masked values.



Unary operations
~~~~~~~~~~~~~~~~

When applied to a :class:`TimeSeries`, unary functions that operate on a whole
array at once (like :func:`numpy.sum`) return a scalar or
the :const:`numpy.ma.masked` constant, depending on whether some entries are
not invalid or if all the entries are masked.
If the function can be applied on an axis, the result is
a :class:`~numpy.ma.MaskedArray`.

Unary functions that operate element-wise on an array (like :func:`numpy.log`)
return a  new :class:`TimeSeries` object with the same dates and frequency
as the input, and whose :attr:`series` attribute is the result of the operation
on the input :attr:`series`.

Output values are masked if the corresponding input values are themselves
masked, or if they fall outside the validity domain of the operation.

Examples
''''''''

   >>> s = ts.time_series([-2, -1, 0, 1, 2, 3], mask=[0, 0, 0, 0, 1, 0]
   ...                    start_date=ts.Date('M','2001-01'))
   >>> logs = ma.log(s)
   >>> logs
   timeseries([-- -- -- 0.0 -- 1.09861228867],
      dates = [Jan-2001 ... Jun-2001],
      freq  = M)

.. note::
   In the previous example, ``logs[-2]`` is masked because ``s[-2]`` is
   itself masked; ``logs[:3]`` is masked because ``s[:3] <= 0``.



Binary operations
~~~~~~~~~~~~~~~~~

The binary operations defined in :mod:`numpy` or :mod:`numpy.ma` can also be
directly applied to :class:`TimeSeries` if the second input is a scalar,
a sequence, a :class:`~numpy.ndarray` or a :class:`~numpy.ma.MaskedArray`.
The standard `broadcasting <http://docs.scipy.org/doc/numpy/reference/ufuncs.html#index-121>`_ rules about shape compatibility apply.

When the second input is another :class:`TimeSeries` object, the two series
must satisfy the following conditions:

   * they must have the same frequency;
   * they must be sorted in chronological order;
   * they must have matching dates;
   * they must have the same :attr:`shape`.

Note that the two series may have duplicated and/or missing dates.

If any of these conditions is not satisfied, the result is a standard
:class:`MaskedArray`.
Otherwise, the result is a new :class:`TimeSeries` object, with the same
:attr:`dates` as the two inputs.

The function :func:`~align_series` (or its alias :func:`~aligned`) forces
series to have matching starting and ending dates.
By default, the starting date will be set to the smallest starting date
sof the series, and the ending date to the largest.



Examples
''''''''

Adding two series with compatible dates and frequency
   >>> a = ts.time_series([1, 2, 3],
   ...                    dates=[2001, 2002, 2003], freq='A')
   >>> b = ts.time_series([10, 20, 30],
   ...                    dates=[2001, 2002, 2003], freq='A')
   >>> a+b
   timeseries([11 22 33],
      dates = [2001 ... 2003],
      freq  = A-DEC)

Adding two series with incompatible dates
   >>> b = ts.time_series([1,2,3], dates=[2001,2001,2003], freq='A')
   >>> a+b
   masked_array(data = [11 22 33],
             mask = False,
       fill_value = 999999)