

Numpy
=====

A popular solution for scientific computing with Python is :mod:`numpy` 
(previous instances were :mod:`Numpy` and :mod:`numarray`).

:mod:`rpy2` has features to ease bidirectional communication with :mod:`numpy`.

High-level interface
--------------------

From `rpy2` to `numpy`:
^^^^^^^^^^^^^^^^^^^^^^^

R vectors or arrays can be converted to :mod:`numpy` arrays using
:meth:`numpy.array` or :meth:`numpy.asarray`:

.. code-block:: python

   import numpy

   ltr = robjects.r.letters
   ltr_np = numpy.array(ltr)

This behavior is inherited from the low-level interface;
vector-like objects inheriting from :class:`rpy2.rinterface.SexpVector`
present an interface recognized by `numpy`.


.. code-block:: python

   from rpy2.robjects.packages import importr, data
   import numpy

   datasets = importr('datasets')
   ostatus = data(datasets).fetch('occupationalStatus')['occupationalStatus']
   ostatus_np = numpy.array(ostatus)
   ostatus_npnc = numpy.asarray(ostatus)

The matrix *ostatus* is an 8x8 matrix:

>>> print(ostatus)
      destination
origin   1   2   3   4   5   6   7   8
     1  50  19  26   8   7  11   6   2
     2  16  40  34  18  11  20   8   3
     3  12  35  65  66  35  88  23  21
     4  11  20  58 110  40 183  64  32
     5   2   8  12  23  25  46  28  12
     6  12  28 102 162  90 554 230 177
     7   0   6  19  40  21 158 143  71
     8   0   3  14  32  15 126  91 106

Its content has been copied to a numpy array:

>>> ostatus_np
array([[ 50,  19,  26,   8,   7,  11,   6,   2],
       [ 16,  40,  34,  18,  11,  20,   8,   3],
       [ 12,  35,  65,  66,  35,  88,  23,  21],
       [ 11,  20,  58, 110,  40, 183,  64,  32],
       [  2,   8,  12,  23,  25,  46,  28,  12],
       [ 12,  28, 102, 162,  90, 554, 230, 177],
       [  0,   6,  19,  40,  21, 158, 143,  71],
       [  0,   3,  14,  32,  15, 126,  91, 106]])
>>> ostatus_np[0, 0]
50
>>> ostatus_np[0, 0] = 123
>>> ostatus_np[0, 0]
123
>>> ostatus.rx(1, 1)[0]
50

On the other hand, *ostatus_npnc* is a view on *ostatus*; no copy was made:

>>> ostatus_npnc[0, 0] = 456
>>> ostatus.rx(1, 1)[0]
456

Since we did modify an actual R dataset for the session, we should restore it:

>>> ostatus_npnc[0, 0] = 50

As we see, :meth:`numpy.asarray`: provides a way to build a *view* on the underlying
R array, without making a copy. This will be of particular appeal to developpers whishing
to mix :mod:`rpy2` and :mod:`numpy` code, with the :mod:`rpy2` objects or the :mod:`numpy` view passed to
functions, or for interactive users much more familiar with the :mod:`numpy` syntax.


.. note::

   The current interface is relying on the *__array_struct__* defined
   in numpy.
   
   Python buffers, as defined in :pep:`3118`, is the way to the future,
   and rpy2 is already offering them... although as a (poorly documented)
   experimental feature.

From `numpy` to `rpy2`:
^^^^^^^^^^^^^^^^^^^^^^^

The activation (and deactivation) of the automatic conversion
of `numpy` objects into `rpy2` objects can be made with:

.. code-block:: python
   
   from rpy2.robjects import numpy2ri
   numpy2ri.activate()
   numpy2ri.deactivate()
   
.. warning::

   In earlier versions of rpy2, the import was all that was needed to
   have the conversion. A side-effect when importing a module can
   lead to problems, and there is now an extra step to make the
   conversion active: call the function :func:`rpy2.robjects.numpy2ri.activate`.

.. note::

   Why make this an optional import, while it could have been included
   in the function :func:`py2ri` (as done in the original patch 
   submitted for that function) ?

   Although both are valid and reasonable options, the design decision
   was taken in order to decouple `rpy2` from `numpy` the most, and
   do not assume that having `numpy` installed automatically
   meant that a programmer wanted to use it. 

.. note::

   The module :mod:`numpy2ri` is an example of how custom conversion to
   and from :mod:`rpy2.robjects` can be performed.

Low-level interface
-------------------

The :class:`rpy2.rinterface.SexpVector` objects are made to 
behave like arrays, as defined in the Python package :mod:`numpy`.

The functions :func:`numpy.array` and :func:`numpy.asarray` can
be used to construct `numpy` arrays:


>>> import numpy
>>> rx = rinterface.SexpVector([1,2,3,4], rinterface.INTSXP)
>>> nx = numpy.array(rx)
>>> nx_nc = numpy.asarray(rx)


.. note::
   when using :meth:`numpy.asarray`, the data are not copied.

>>> rx[2]
3
>>> nx_nc[2] = 42
>>> rx[2]
42
>>>

