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`.
R vectors or arrays can be converted to :mod:`numpy` arrays using :meth:`numpy.array` or :meth:`numpy.asarray`:
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.
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.
The activation (and deactivation) of the automatic conversion of numpy objects into rpy2 objects can be made with:
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.
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
>>>