Platforms: Unix, Windows
This module should be the right pick for casual and general use. Its aim is to abstract some of the details and provide an intuitive interface to both Python and R programmers.
>>> import rpy2.robjects as robjects
rpy2.robjects is written on the top of rpy2.rinterface, and one not satisfied with it could easily build one’s own flavor of a Python-R interface by modifying it (rpy2.rpy_classic is an other example of a Python interface built on the top of rpy2.rinterface).
Visible differences with RPy-1.x are:
This class is currently a singleton, with its one representation instanciated when the module is loaded:
>>> robjects.r
>>> print(robjects.r)
The instance can be seen as the entry point to an embedded R process.
Being a singleton means that each time the constructor for R is called the same instance is returned; this is required by the fact that the embedded R is stateful.
The elements that would be accessible from an equivalent R environment are accessible as attributes of the instance. Readers familiar with the ctypes module for Python will note the similarity with it.
R vectors:
>>> pi = robjects.r.pi
>>> letters = robjects.r.letters
R functions:
>>> plot = robjects.r.plot
>>> dir = robjects.r.dir
This approach has limitation as:
The actual Python attributes for the object masks the R elements
python objects.
That last limitation can partly be removed by using rpy2.rpy_classic if this feature matters most to you.
>>> robjects.r.as_null
# AttributeError raised
>>> import rpy2.rpy_classic as rpy
>>> rpy.set_default_mode(NO_CONVERSION)
>>> rpy.r.as_null
# R function as.null() returned
Note
The section Partial use of rpy_classic outlines how to integrate rpy2.rpy_classic code.
Behind the scene, the steps for getting an attribute of r are rather straightforward:
- Check if the attribute is defined as such in the python definition for r
- Check if the attribute is can be accessed in R, starting from globalenv
When safety matters most, we recommend using __getitem__() to get a given R object.
>>> as_null = robjects.r['as.null']
Storing the object in a python variable will protect it from garbage collection, even if deleted from the objects visible to an R user.
>>> robjects.globalenv['foo'] = 1.2
>>> foo = robjects.r['foo']
>>> foo[0]
1.2
Here we remove the symbol foo from the R Global Environment.
>>> robjects.r['rm']('foo')
>>> robjects.r['foo']
LookupError: 'foo' not found
The object itself remains available, and protected from R’s garbage collection until foo is deleted from Python
>>> foo[0]
1.2
Just like it is the case with RPy-1.x, on-the-fly evaluation of R code contained in a string can be performed by calling the r instance:
>>> print(robjects.r('1+2'))
[1] 3
>>> sqr = robjects.r('function(x) x^2')
>>> print(sqr)
function (x)
x^2
>>> print(sqr(2))
[1] 4
The astute reader will quickly realize that R objects named by python variables can be plugged into code through their R representation:
>>> x = robjects.r.rnorm(100)
>>> robjects.r('hist(%s, xlab="x", main="hist(x)")' %x.r_repr())
Warning
Doing this with large objects might not be the best use of your computing power.
The class rpy2.robjects.RObject can represent any arbitray R object, although it will often be used for objects without any more specific representation in Python/rpy2 (such as Vector, Function, Environment).
The class inherits from the lower-level rpy2.rinterface.Sexp and from rpy2.robjects.robject.RObjectMixin, the later defining higher-level methods for R objects to be shared by other higher-level representations of R objects.
Bases: object
Class to provide methods common to all RObject instances
Bases: rpy2.robjects.robject.RObjectMixin, rpy2.rinterface.Sexp
Base class for all R objects.
Beside functions, and environemnts, most of the objects an R user is interacting with are vector-like. For example, this means that any scalar is in fact a vector of length one.
The class Vector has a constructor:
>>> x = robjects.Vector(3)
Bases: rpy2.robjects.robject.RObjectMixin, rpy2.rinterface.SexpVector
R vector-like object. Items can be accessed with: - the method “__getitem__” (“[” operator) - the delegators rx or rx2
Creating vectors can be achieved either from R or from Python.
When the vectors are created from R, one should not worry much as they will be exposed as they should by rpy2.robjects.
When one wants to create a vector from Python, either the class Vector or the convenience classes IntVector, FloatVector, BoolVector, StrVector can be used.
Bases: rpy2.robjects.vectors.Vector
Vector of boolean (logical) elements
Bases: rpy2.robjects.vectors.Vector
Vector of integer elements
Bases: rpy2.robjects.vectors.Vector
Vector of float (double) elements
Bases: rpy2.robjects.vectors.Vector
Vector of string elements
R’s factors are somewhat peculiar: they aim at representing a memory-efficient vector of labels, and in order to achieve it are implemented as vectors of integers, to which are associated a (presumably shorter) vector of labels. Each integer represents the position of the label in the associated vector of labels.
>>> sv = ro.StrVector('ababbc')
>>> fac = ro.FactorVector(sv)
>>> print(fac)
[1] a b a b b c
Levels: a b c
>>> tuple(fac)
(1, 2, 1, 2, 2, 3)
>>> tuple(fac.levels)
('a', 'b', 'c')
Since a FactorVector is an IntVector with attached metadata (the levels), getting items Python-style was not changed from what happens when gettings items from a IntVector. A consequence to that is that information about the levels is then lost.
>>> item_i = 0
>>> fac[item_i]
1
Getting the level corresponding to an item requires using the levels,:
>>> fac.levels[fac[item_i] - 1]
'a'
Warning
Do not forget to subtract one to the value in the FactorVector. Indexing in Python starts at zero while indexing R starts at one, and recovering the level for an item requires an adjustment between the two.
When extracting elements from a FactorVector a sensible default might be to use R-style extracting (see Extracting items), as it preserves the integer/string duality.
Bases: rpy2.robjects.vectors.IntVector
Vector of ‘factors’
Extracting elements of sequence/vector can become a thorny issue as Python and R differ on a number of points (index numbers starting at zero / starting at one, negative index number meaning index from the end / everything except, names cannot / can be used for subsettting).
In order to solve this, the Python way and the R way were made available through two different routes.
The python __getitem__() method behaves like a Python user would expect it for a vector (and indexing starts at zero).
>>> x = robjects.r.seq(1, 5)
>>> tuple(x)
(1, 2, 3, 4, 5)
>>> x.names = robjects.StrVector('abcde')
>>> print(x)
a b c d e
1 2 3 4 5
>>> x[0]
1
>>> x[4]
5
>>> x[-1]
5
Access to R-style extracting/subsetting is granted though the two delegators rx and rx2, representing the R functions [ and [[ respectively.
In short, R-style extracting has the following characteristics:
>>> print(x.rx(1))
[1] 1
>>> print(x.rx(robjects.IntVector((1, 3))))
[1] 1 3
R/S have particularities, in which some see consistency issues. For example although the indexing starts at 1, indexing on 0 does not return an index out of bounds error but a vector of length 0:
>>> print(x.rx(0))
integer(0)
The two next examples demonstrate some of R‘s features (such as element exclusion and the recycling rule):
>>> print(x.rx(-1))
2:5
>>> print(x.rx(robjects.IntVector((-1, -3))))
[1] 2 4 5
>>> print(x.rx(True))
1:5
>>> print(x.rx(robjects.BoolVector((False, True, False, True, True))))
[1] 2 4 5
>>> print(x.rx('a'))
a
1
Anyone with experience in the analysis of real data knows that, well, some of the data might be missing. In S/Splus/R special NA values can be used in a data vector to indicate that, and rpy2.robjects makes aliases for those available as data objects NA_bool, NA_real, NA_integer, NA_character, NA_complex.
>>> x = robjects.IntVector(range(3))
>>> x[0] <- robjects.NA_integer
>>> print(x)
[1] NA 1 2
Note
NA_bool is the alias for R’s NA.
Mathematical operations on two vectors: the following operations are performed element-wise in R, recycling the shortest vector if, and as much as, necessary.
To expose that to Python, a delegating attribute ro is provided for vector-like objects.
| operator | R (.ro) |
|---|---|
| + | Add |
| - | Subtract |
| * | Multiply |
| / | Divide |
| ** | Power |
| or | Or |
| and | And |
>>> x = robjects.r.seq(1, 10)
>>> print(x.ro + 1)
2:11
Note
In Python, the operator + concatenates sequence objects, and this behavior has been conserved.
Note
The boolean operator not cannot be redefined in Python (at least up to version 2.5), and its behavior could not be made to mimic R’s behavior
R vectors can have a name given to all or some of the elements. The property names can be used to get, or set, those names.
>>> x = robjects.r.seq(1, 5)
>>> x.names = robjects.StrVector('abcde')
>>> x.names[0]
'a'
>>> x.names[0] = 'z'
>>> tuple(x.names)
('z', 'b', 'c', 'd', 'e')
In R, arrays are simply vectors with a dimension attribute. That fact was reflected in the class hierarchy with robjects.Array inheriting from robjects.Vector.
A Matrix is a special case of Array. As with arrays, one must remember that this is just a vector with dimension attributes (number of rows, number of columns).
>>> m = robjects.r.matrix(robjects.IntVector(range(10)), nrow=5)
>>> print(m)
[,1] [,2]
[1,] 0 5
[2,] 1 6
[3,] 2 7
[4,] 3 8
[5,] 4 9
Note
In R, matrices are column-major ordered, although the constructor matrix() accepts a boolean parameter byrow that, when true, will build the matrix as if row-major ordered.
Regular operators work element-wise on the underlying vector.
>>> m = robjects.r.matrix(robjects.IntVector(range(4)), nrow=2)
>>> print(m.ro + 1)
[,1] [,2]
[1,] 1 3
[2,] 2 4
For more on operators, see Operators.
Matrix multiplication is available as Matrix.dot(), transposition as Matrix.transpose(). Common operations such as cross-product, eigen values computation , and singular value decomposition are also available through method with explicit names.
>>> print( m.crossprod(m) )
[,1] [,2]
[1,] 1 3
[2,] 3 13
>>> print( m.transpose().dot(m) )
[,1] [,2]
[1,] 1 3
[2,] 3 13
Bases: rpy2.robjects.vectors.Array
An R matrix
Extracting can still be performed Python-style or R-style.
>>> m = ro.r.matrix(ro.IntVector(range(2, 8)), nrow=3)
>>> print(m)
[,1] [,2]
[1,] 2 5
[2,] 3 6
[3,] 4 7
>>> m[0]
2
>>> m[5]
7
>>> print(m.rx(1))
[1] 2
>>> print(m.rx(6))
[1] 7
Matrixes are two-dimensional arrays, and elements can be extracted according to two indexes:
>>> print(m.rx(1, 1))
[1] 2
>>> print(m.rx(3, 2))
[1] 7
Data frames are important data structures in R, as they are used to represent a data to analyze in a study in a relatively large nunmber of cases.
A data frame can be thought of as a tabular representation of data, with one variable per column, and one data point per row. Each column is an R vector, which implies one type for all elements in one given column, and which allows for possibly different types across different columns.
If we take the example of data about the pharmacokinetics of theophylline in different subjects, the table of data could look like:
| Subject | Weight | Dose | Time | conc |
|---|---|---|---|---|
| 1 | 79.6 | 4.02 | 0.00 | 0.74 |
| 1 | 79.6 | 4.02 | 0.25 | 2.84 |
| 1 | 79.6 | 4.02 | 0.57 | 6.57 |
| 2 | 72.4 | 4.40 | 7.03 | 5.40 |
| ... | ... | ... | ... | ... |
Such representation of the data shares similarities with a table in a relational database: the structure between the variables, or columns, is given by other column. In the example above, the grouping of the measures by subject is given by the column Subject.
In rpy2.robjects, DataFrame represents the R class data.frame.
Creating an DataFrame can be done by:
The constructor for DataFrame accepts either a rinterface.SexpVector (with typeof equal to VECSXP, that is an R list) or any Python object implementing the method iteritems() (for example dict, or rpy2.rlike.container.OrdDict)
Empty data.frame:
>>> dataf = robjects.DataFrame()
data.frame with 2 two columns (not that the order of the columns in the resulting DataFrame can be different from the order in which they are declared):
>>> d = {'a': robjects.IntVector((1,2,3)), 'b': robjects.IntVector((4,5,6))}
>>> dataf = robject.DataFrame(d)
To create a DataFrame and be certain of the order in which the columns are an ordered dictionary can be used:
>>> import rpy2.rlike.container as rlc
>>> od = rlc.OrdDict(c(('value', robjects.IntVector((1,2,3))),
('letter', robjects.StrVector(('x', 'y', 'z')))))
>>> dataf = robjects.DataFrame(od)
>>> print(dataf.colnames)
[1] "letter" "value"
Creating the data.frame in R can otherwise be achieved in numerous ways, as many R functions do return a data.frame, such as the
function data.frame().
Here again, Python’s __getitem__() will work as a Python programmer will expect it to:
>>> len(dataf)
2
>>> dataf[0]
<Vector - Python:0x8a58c2c / R:0x8e7dd08>
The DataFrame is composed of columns, with each column being possibly of a different type:
>>> [column.rclass[0] for column in dataf]
['factor', 'integer']
Using R-style access to elements is a little more richer, with the rx2 accessor taking now more importance than earlier.
Like with Python’s __getitem__() above, extracting on one index selects columns:
>>> dataf.rx(1)
<DataFrame - Python:0x8a584ac / R:0x95a6fb8>
>>> print(dataf.rx(1))
letter
1 x
2 y
3 z
It is important to notice that the result is itself of class DataFrame. Getting the column as a vector is requires the use of rx2.
>>> dataf.rx2(1)
<Vector - Python:0x8a4bfcc / R:0x8e7dd08>
>>> print(dataf.rx2(1))
[1] x y z
Levels: x y z
Since data frames are table-like structure, they can be thought of as two-dimensional arrays and can therefore be extracted on two indexes.
>>> subdataf = dataf.rx(1, True)
>>> print(subdataf)
letter value
1 x 1
>>> subdataf = dataf.rx(robjects.IntVector((1,3)), True)
>>> print(subdataf)
letter value
1 x 1
3 z 3
Bases: rpy2.robjects.vectors.Vector
R ‘data.frame’.
Column names
| Return type: | SexpVector |
|---|
Row names
| Return type: | SexpVector |
|---|
R environments can be described to the Python user as an hybrid of a dictionary and a scope.
The first of all environments is called the Global Environment, that can also be referred to as the R workspace.
An R environment in RPy2 can be seen as a kind of Python dictionnary.
Assigning a value to a symbol in an environment has been made as simple as assigning a value to a key in a Python dictionary:
>>> robjects.r.ls(globalenv)
>>> robjects.globalenv["a"] = 123
>>> print(robjects.r.ls(globalenv))
Care must be taken when assigning objects into an environment such as the Global Environment, as this can hide other objects with an identical name. The following example should make one measure that this can mean trouble if no care is taken:
>>> globalenv["pi"] = 123
>>> print(robjects.r.pi)
[1] 123
>>>
>>> robjects.r.rm("pi")
>>> print(robjects.r.pi)
[1] 3.1415926535897931
The class inherits from the class rpy2.rinterface.SexpEnvironment.
An environment is also iter-able, returning all the symbols (keys) it contains:
>>> env = robjects.r.baseenv()
>>> [x for x in env]
<a long list returned>
Note
Although there is a natural link between environment and R packages, one should consider using the convenience wrapper dedicated to model R packages (see R packages).
Bases: rpy2.robjects.robject.RObjectMixin, rpy2.rinterface.SexpEnvironment
An R environement.
R functions are callable objects, and be called almost like any regular Python function:
>>> plot = robjects.r.plot
>>> rnorm = robjects.r.rnorm
>>> plot(rnorm(100), ylab="random")
This is all looking fine and simple until R parameters with names such as na.rm are encountered. By default, this is addressed by having a translation of ‘.’ in the R parameter name into a ‘_’ in the Python parameter name.
Let’s take an example in R:
sum(0, na.rm = TRUE)
In Python it can then write:
from rpy2 import robjects
robjects.r.sum(0, na_rm = True)
Note
The object robjects.r.sum is an instance of SignatureTranslatedFunction, a child class of Function, and the translation of the parameter within its constructor. This saves on having to translate parameters at each function call, and allow to perform sanity check regarding possible ambiguous translation.
If translation is not desired, the class Function can be used. With that class, using the special Python syntax **kwargs is one way to go specify named parameters with a dot ‘.’
Things are also not always that simple, as the use of a dictionary does not ensure that the order in which the parameters are passed is conserved.
R is capable of introspection, and can return the arguments accepted by a function through the function formals(), modelled as a method of Function.
>>> from rpy2.robjects.packages import importr
>>> stats = importr('stats')
>>> rnorm = stats.rnorm
>>> rnorm.formals()
<Vector - Python:0x8790bcc / R:0x93db250>
>>> tuple(rnorm.formals().names)
('n', 'mean', 'sd')
The R functions as defined in rpy2.robjects inherit from the class rpy2.rinterface.SexpClosure, and further documentation on the behavior of function can be found in Section Functions.
Bases: rpy2.robjects.robject.RObjectMixin, rpy2.rinterface.SexpClosure
Python representation of an R function.
Bases: rpy2.robjects.functions.Function
Python representation of an R function such as the character ‘.’ is replaced with ‘_’ whenever present in the R argument name.
For tasks such as modelling and plotting, an R formula can be a terse, yet readable, way of expressing what is wanted.
In R, it generally looks like:
x <- 1:10
y <- x + rnorm(10, sd=0.2)
fit <- lm(y ~ x)
In the call to lm, the argument is a formula, and it can read like model y using x. A formula is a R language object, and the terms in the formula are evaluated in the environment it was defined in. Without further specification, that environment is the environment in which the the formula is created.
The class robjects.Formula is representing an R formula.
x = robjects.Vector(array.array('i', range(1, 11)))
y = x.r + rnorm(10, sd=0.2)
fmla = robjects.Formula('y ~ x')
env = fmla.environment
env['x'] = x
env['y'] = y
stats = importr('lm')
fit = stats.lm(fmla)
One drawback with that approach is that pretty printing of the fit object is note quite as clear as what one would expect when working in R. However, by evaluating R code on the fly, we can obtain a fit object that will display nicely:
fit = robjects.r('lm(%s)' %fmla.r_repr())
Bases: rpy2.robjects.robject.RObjectMixin, rpy2.rinterface.Sexp
In R, objects can be bundled into packages for distribution. In similar fashion to Python modules, the packages can be installed, and then loaded when their are needed. This is achieved by the R functions library() and require() (attaching the namespace of the package to the R search path).
from rpy2.robjects.packages import importr
utils = importr("utils")
The object utils is now a module-like object, in the sense that its __dict__ contains keys corresponding to the R symbols. For example the R function data() can be accessed like:
>>> utils.data
<SignatureTranslatedFunction - Python:0x913754c / R:0x943bdf8>
Unfortunately, accessing an R symbol can be a little less straightforward as R symbols can contain characters that are invalid in Python symbols. Anyone with experience in R can even add there is a predilection for the dot (.).
In an attempt to address this, during the import of the package a translation of the R symbols is attempted, with dots becoming underscores. This is not unlike what could be found in rpy, but with distinctive differences:
Note
The translation of ‘.’ into ‘_’ is clearly not sufficient, as R symbols can use a lot more character illegal in Python symbols. Those more exotic symbols can be accessed through __dict__.
Example:
>>> utils.__dict__['?']
<Function - Python:0x913796c / R:0x9366fac>
In addition to the translation of robjects symbols, objects that are R functions see their named arguments translated as similar way (with ‘.’ becoming ‘_’ in Python).
>>> base = importr('base')
>>> base.scan._prm_translate
{'blank_lines_skip': 'blank.lines.skip',
'comment_char': 'comment.char',
'multi_line': 'multi.line',
'na_strings': 'na.strings',
'strip_white': 'strip.white'}
Knowning which object is effectively considered when a given symbol is resolved can of much importance in R, as the number of packages attached grows and the use of the namespace accessors “::” and “:::” is not so frequent.
The function wherefrom() offers a way to find it:
>>> import rpy2.robjects.packages as rpacks
>>> env = rpacks.wherefrom('lm')
>>> env.do_slot('name')[0]
'package:stats'
Note
This does not generalize completely, and more details regarding environment, and packages as environment should be checked Section SexpEnvironment.
R is shipped with a set of recommended packages (the equivalent of a standard library), but there is a large (and growing) number of other packages available.
Installing those packages can done from within R, and one will consult an R-related documentation if unsure of how to do so.
Object-Oriented Programming can a achieved in R, but in more than one way. Beside the official S3 and S4 systems, there is a rich ecosystem of alternative implementations of objects (aroma, or proto are two such systems).
S3 objects are default R objects (i.e., not S4 instances) for which an attribute “class” has been added.
>>> x = robjects.IntVector((1, 3))
>>> tuple(x.rclass)
('integer',)
Making the object x an instance of a class pair, itself inheriting from integer is only a matter of setting the attribute:
>>> x.rclass = robjects.StrVector(("pair", "integer"))
>>> tuple(x.rclass)
('pair', 'integer')
Methods for S3 classes are simply R functions with a name such as name.<class_name>, the dispatch being made at run-time from the first argument in the function call.
For example, the function plot.lm plots objects of class lm. The call plot(something) will see R extract the class name of the object something, and see if a function plot.<class_of_something> is in the search path.
Note
This rule is not strict as there can exist functions with a dot in their name and the part after the dot not correspond to an S3 class name.
S4 objects are a little more formal regarding their class definition, and all instances belong to the low-level R type SEXPS4.
The definition of methods for a class can happen anytime after the class has been defined (a practice something referred to as monkey patching or duck punching in the Python world).
There are obviously many ways to try having a mapping between R classes and Python classes, and the one proposed here is to make Python classes that inherit rpy2.rinterface.methods.RS4.
Since the S4 system allows polymorphic definition of methods, that is for a given method name there can exist several list of possible arguments and type for the arguments, it currently appears trickly to have an simple, automatic, and robust mapping of R methods to Python methods. For the time being, one will rely on human-written mappings, although some helpers are provided by rpy2.
Note
More automation for reflecting S4 class definitions into Python is on the list of items to be worked on, so one may hope for more in a following release.
To make this a little more concrete, we take the R class lmList in the package lme4 and show how to write a Python wrapper for it.
Warning
The R package lme4 is not distributed with R, and will have to be installed for the example to work.
First, a bit of boilerplate code is needed. We obviously import the higher-level interface, as well the function rpy2.robjects.packages.importr(). The R class we want to represent is defined in the rpy2 modules and utilities.
import rpy2.robjects as robjects
import rpy2.rinterface as rinterface
from rpy2.robjects.packages import importr
lme4 = importr("lme4")
getmethod = robjects.baseenv.get("getMethod")
StrVector = robjects.StrVector
Once done, the Python class definition can be written. In the first part of that code, we choose a static mapping of the R-defined methods. The advantage for doing so is a bit of speed (as the S4 dispatch mechanism has a cost), and the disadvantage is that the a modification of the method at the R level would require a refresh of the mappings concerned. The second part of the code is wrapper to those mappings, where Python-to-R operations prior to calling the R method can be performed. In the last part of the class definition, a static methods is defined. This is one way to have polymorphic constructors implemented.
class LmList(robjects.methods.RS4):
""" Reflection of the S4 class 'lmList'. """
_coef = getmethod("coef",
signature = StrVector(["lmList", ]),
where = "package:lme4")
_confint = getmethod("confint",
signature = StrVector(["lmList", ]),
where = "package:lme4")
_formula = getmethod("formula",
signature = StrVector(["lmList", ]),
where = "package:lme4")
_lmfit_from_formula = getmethod("lmList",
signature = StrVector(["formula", "data.frame"]),
where = "package:lme4")
def _call_get(self):
return self.do_slot("call")
def _call_set(self, value):
return self.do_slot("call", value)
call = property(_call_get, _call_set, None, "Get or set the RS4 slot 'call'.")
def coef(self):
""" fitted coefficients """
return self._coef(self)
def confint(self):
""" confidence interval """
return self._confint(self)
def formula(self):
""" formula used to fit the model """
return self._formula(self)
@staticmethod
def from_formula(formula,
data = rinterface.R_MissingArg,
family = rinterface.R_MissingArg,
subset = rinterface.R_MissingArg,
weights = rinterface.R_MissingArg):
""" Build an LmList from a formula """
res = LmList._lmfit_from_formula(formula, data,
family = family,
subset = subset,
weights = weights)
res = LmList(res)
return res
Creating a instance of LmList can now be achieved by specifying a model as a Formula and a dataset.
sleepstudy = lme4.sleepstudy
formula = robjects.Formula('Reaction ~ Days | Subject')
lml1 = LmList.from_formula(formula,
sleepstudy)
A drawback of the approach above is that the R “call” is stored, and as we are passing the DataFrame sleepstudy (and as it is believed to to be an anonymous structure by R) the call is lengthy as it comprise the explicit structure description of the data frame. (try to print lml1). This becomes hardly acceptable as datasets grow bigger. An alternative to that is to store the columns of the data frame into the environment for the Formula, as shown below:
sleepstudy = lme4.sleepstudy
formula = robjects.Formula('Reaction ~ Days | Subject')
for varname in ('Reaction', 'Days', 'Subject'):
formula.environment[varname] = sleepstudy.rx2(varname)
lml1 = LmList.from_formula(formula)
Bases: rpy2.robjects.robject.RObjectMixin, rpy2.rinterface.SexpS4
Python representation of an R instance of class ‘S4’.
Once a Python class mirroring an R classis defined, the mapping can be made automatic by adding new rules to the conversion system (see Section Mapping rpy2 objects to arbitrary python objects).
The python pickling system can be used to serialize objects to disk, and restore them from their serialized form.
import pickle
import rpy2.robjects as ro
x = ro.StrVector(('a', 'b', 'c'))
f = file('/tmp/foo.pso', 'w')
pickle.dump(x, f)
f.close()
f = file('/tmp/foo.pso', 'r')
x_again = pickle.load(f)
f.close()
Warning
Currently loading an object from a serialized form restores the object in its low-level form (as in rpy2.rinterface). Higher-level objects can be restored by calling the higher-level casting function rpy2.robjects.conversion.ri2py() (see Mapping rpy2 objects to arbitrary python objects).