Numpy

A popular solution for scientific computing with Python is numpy.

rpy2 has features to ease bidirectional communication with numpy.

High-level interface

From rpy2 to numpy:

R vectors or arrays can be converted to numpy arrays using numpy.array() or 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 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, 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 rpy2 and numpy code, with the rpy2 objects or the numpy view passed to functions, or for interactive users much more familiar with the 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:

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 rpy2.robjects.numpy2ri.activate().

Note

Why make this an optional import, while it could have been included in the function 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 numpy2ri is an example of how custom conversion to and from rpy2.robjects can be performed.

Low-level interface

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

The functions numpy.array() and 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 numpy.asarray(), the data are not copied.

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