Revision: 700 http://rpy.svn.sourceforge.net/rpy/?rev=700&view=rev Author: lgautier Date: 2008-11-22 21:06:58 +0000 (Sat, 22 Nov 2008)
Log Message: ----------- Merging recent addition to the 2.0.x branch into the trunk Bit of editing of the documentation Modified Paths: -------------- rpy2/trunk/NEWS rpy2/trunk/doc/source/overview.rst rpy2/trunk/doc/source/robjects.rst rpy2/trunk/doc/source/rpy_classic.rst rpy2/trunk/rpy/__init__.py rpy2/trunk/rpy/rinterface/__init__.py rpy2/trunk/rpy/robjects/__init__.py rpy2/trunk/rpy/robjects/tests/__init__.py rpy2/trunk/rpy/robjects/tests/testRobjects.py rpy2/trunk/rpy/rpy_classic.py rpy2/trunk/rpy/tests_rpy_classic.py rpy2/trunk/setup.py Added Paths: ----------- rpy2/trunk/rpy/robjects/conversion.py rpy2/trunk/rpy/robjects/numpy2ri.py rpy2/trunk/rpy/robjects/tests/testNumpyConversions.py Property Changed: ---------------- rpy2/trunk/ Property changes on: rpy2/trunk ___________________________________________________________________ Added: svn:mergeinfo + /rpy2/branches/version_2.0.x:679-699 Modified: rpy2/trunk/NEWS =================================================================== --- rpy2/trunk/NEWS 2008-11-22 19:50:46 UTC (rev 699) +++ rpy2/trunk/NEWS 2008-11-22 21:06:58 UTC (rev 700) @@ -2,14 +2,19 @@ === - - Release 2.0.0 ============= -Bugs fixed ----------- +New features +------------ +- New module :mod:`rpy2.robjects.conversion`. + +- New module :mod:`rpy2.robjects.numpy2ri` to convert :mod:`numpy` objects + into :mod:`rpy2` objects. + # adapted from a patch contributed by Nathaniel Smith + + Changes ------- @@ -28,6 +33,32 @@ Release 2.0.0rc1 ================ +:mod:`rpy2.rpy_classic`: + +- :meth:`rpy_classic.RObj.getSexp` moved to a + property :attr:`rpy_classic.Robj.sexp`. + +:mod:`rpy2.robjects`: + +- :meth:`RObject.__repr__` moved to :meth:`RObject.r_repr` + +- :meth:`ri2py`, :meth:`ro2py`, and :meth:`py2ri` moved to the new module + :mod:`conversion`. Adding the prefix `conversion.` to calls + to those functions will be enough to update existing code + + +Bugs fixed +---------- + +- Informative message returned as RuntimeError when failing to find R's HOME + +- Use the registry to find the R's HOME on win32 + # snatched from Peter's earlier contribution to rpy-1.x + + +Release 2.0.0rc1 +================ + New features ------------ Modified: rpy2/trunk/doc/source/overview.rst =================================================================== --- rpy2/trunk/doc/source/overview.rst 2008-11-22 19:50:46 UTC (rev 699) +++ rpy2/trunk/doc/source/overview.rst 2008-11-22 21:06:58 UTC (rev 700) @@ -124,7 +124,7 @@ .. note:: At the time of writing, 2 unit tests will fail. Their failure - is forced, because the terminating then starting again an + is forced, because terminating then starting again an embbeded R is causing problems. .. warning:: @@ -200,6 +200,12 @@ For the original RPy and its maintainance through the years. Alexander Belopolsky. - His code contribution to RPy is acknowledged. I have found great - inspiration in reading that code. + His code contribution of an alternative RPy is acknowledged. + I have found great inspiration in reading that code. +JRI + The Java-R Interface, and its authors, as answers to some + of the implementation questions were found there. + +Contributors + Their names are in Section Changes. Modified: rpy2/trunk/doc/source/robjects.rst =================================================================== --- rpy2/trunk/doc/source/robjects.rst 2008-11-22 19:50:46 UTC (rev 699) +++ rpy2/trunk/doc/source/robjects.rst 2008-11-22 21:06:58 UTC (rev 700) @@ -70,7 +70,7 @@ * '.' (dot) is syntactically valid in names for R objects, but not for python objects. -That last limitation can partly be removed by using :mod:`rpy.rpy_classic` if +That last limitation can partly be removed by using :mod:`rpy2.rpy_classic` if this feature matters most to you. >>> robjects.r.as_null @@ -80,11 +80,12 @@ >>> rpy.r.as_null # R function as.null() returned -.. warning:: - In the case there are R objects which name only differ by '.' and '_' - (e.g., 'my_variable' and 'my.variable'), setting :attr:`_dotter` to True - can result in confusing results at runtime. +.. note:: + The section :ref:`rpy_classic-mix` outlines how to integrate + :mod:`rpy2.rpy_classic` code. + + Behind the scene, the steps for getting an attribute of `r` are rather straightforward: @@ -93,13 +94,33 @@ 2. Check if the attribute is can be accessed in R, starting from `globalEnv` -When safety matters most, or when getting extraordinary funds for a bailout -is unlikely, we recommed using :meth:`__getitem__` to get -a given R object (and store it in a python variable if wanted): +When safety matters most, we recommend using :meth:`__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 + + + Strings as R code ----------------- @@ -295,6 +316,17 @@ Numpy ----- +A popular solution for scientific computing with Python is :mod:`numpy` +(previous instances were :mod:`Numpy` and :mod:`numarray`). + +:mod:`rpy2` has features for facilitating the integration with code using +:mod:`numpy` in both directions: from `rpy2` to `numpy`, and from `numpy` +to `rpy2`. + + +From `rpy2` to `numpy`: +^^^^^^^^^^^^^^^^^^^^^^^ + Vectors can be converted to :mod:`numpy` arrays using :meth:`array` or :meth:`asarray`:: @@ -305,6 +337,31 @@ Refer to the documentation for :class:`rinterface.SexpVector` for further details. +From `numpy` to `rpy2`: +^^^^^^^^^^^^^^^^^^^^^^^ + +The conversion of `numpy` objects to `rpy2` objects can be +activated by importing the module :mod:`numpy2ri`:: + + import rpy2.robjects.numpy2ri + +That import alone is sufficient to switch an automatic conversion +of `numpy` objects into `rpy2` objects. + + +.. 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. + + + .. index:: pair: robjects;REnvironment pair: robjects;globalEnv @@ -525,21 +582,25 @@ while an higher-level mapping is done between low-level objects and higher-level objects using the functions: -:meth:`ri2py` +:meth:`conversion.ri2py` :mod:`rpy2.rinterface` to Python. By default, this function is just an alias for the function :meth:`default_ri2py`. -:meth:`py2ri` +:meth:`conversion.py2ri` Python to :mod:`rpy2.rinterface`. By default, this function is just an alias for the function :meth:`default_py2ri`. -:meth:`py2ro` - Python to :mod:`rpy2.robjects`. That one function - is merely a call to :meth:`py2ri` followed by a call to :meth:`ri2py`. +:meth:`conversion.py2ro` + Python to :mod:`rpy2.robjects`. By default, that one function + is merely a call to :meth:`conversion.py2ri` + followed by a call to :meth:`conversion.ri2py`. -Those functions can be modifyied to satisfy all requirements, with +Those functions can be re-routed to satisfy all requirements, with the easiest option being to write a custom function calling itself -the default function. +the default functions. + +Switching to `numpy`-to-`rpy2` is using this mechanism. + As an example, let's assume that one want to return atomic values whenever an R numerical vector is of length one. This is only a matter of writing a new function `ri2py` that handles this, as shown below: @@ -554,7 +615,7 @@ res = res[0] return res - robjects.ri2py = my_ri2py + robjects.conversion.ri2py = my_ri2py Once this is done, we can verify immediately that this is working with: @@ -563,9 +624,9 @@ <type 'float'> >>> -The default behavoir can be restored with: +The default behavior can be restored with: ->>> robjects.ri2py = default_ri2py +>>> robjects.conversion.ri2py = default_ri2py The docstrings for :meth:`default_ri2py`, :meth:`default_py2ri`, and :meth:`py2ro` are: Modified: rpy2/trunk/doc/source/rpy_classic.rst =================================================================== --- rpy2/trunk/doc/source/rpy_classic.rst 2008-11-22 19:50:46 UTC (rev 699) +++ rpy2/trunk/doc/source/rpy_classic.rst 2008-11-22 21:06:58 UTC (rev 700) @@ -93,3 +93,56 @@ >>> pca = rpy.r.princomp(m) >>> rpy.r.plot(pca, main = "PCA") >>> + +.. _rpy_classic-mix: + +Partial use of :mod:`rpy_classic` +================================== + +The use of rpy_classic does not need to be +exclusive of the other interface(s) proposed +in rpy2. + +Chaining code designed for either of the interfaces +is rather easy and, among other possible use-cases, +should make the inclusion of legacy rpy code into newly +written rpy2 code a simple take. + +The link between :mod:`rpy_classic` and the rest +of :mod:`rpy2` is the property :attr:`RObj.sexp`, +that give the representation of the underlying R object +in the low-level :mod:`rpy2.rinterface` definition. +This representation can then be used in function calls +with :mod:`rpy2.rinterface` and :mod:`rpy2.robjects`. +With :mod:`rpy2.robjects`, a conversion using +:func:`rpy2.robjects.default_ri2py` can be considered. + +.. note:: + + Obviously, that property `sexp` is not part of the original + `Robj` in rpy. + + +An example: + +.. code-block:: python + + import rpy2.robjects as ro + import rpy2.rpy_classic as rpy + rpy.set_default_mode(rpy.NO_CONVERSION) + + + def legacy_paste(v): + # legacy rpy code + res = rpy.r.paste(v, collapse = '-') + return res + + + rletters = ro.r['letters'] + + # the legaxy code is called using an rpy2.robjects object + alphabet_rpy = legacy_paste(rletters) + + # convert the resulting rpy2.rpy_classic object to + # an rpy2.robjects object + alphabet = ro.default_ri2py(alphabet_rpy.sexp) Modified: rpy2/trunk/rpy/__init__.py =================================================================== --- rpy2/trunk/rpy/__init__.py 2008-11-22 19:50:46 UTC (rev 699) +++ rpy2/trunk/rpy/__init__.py 2008-11-22 21:06:58 UTC (rev 700) @@ -1 +1,2 @@ __version__ = '2.1.0dev' + Modified: rpy2/trunk/rpy/rinterface/__init__.py =================================================================== --- rpy2/trunk/rpy/rinterface/__init__.py 2008-11-22 19:50:46 UTC (rev 699) +++ rpy2/trunk/rpy/rinterface/__init__.py 2008-11-22 21:06:58 UTC (rev 700) @@ -22,7 +22,7 @@ "This might be because R.exe is nowhere in your Path.") else: raise RuntimeError( - "R_HOME define, and no R command in the PATH." + "R_HOME not defined, and no R command in the PATH." ) else: #Twist if 'R RHOME' spits out a warning Modified: rpy2/trunk/rpy/robjects/__init__.py =================================================================== --- rpy2/trunk/rpy/robjects/__init__.py 2008-11-22 19:50:46 UTC (rev 699) +++ rpy2/trunk/rpy/robjects/__init__.py 2008-11-22 21:06:58 UTC (rev 700) @@ -11,9 +11,11 @@ import itertools import rpy2.rinterface as rinterface import rpy2.rlike.container as rlc +import rpy2.robjects.conversion #FIXME: close everything when leaving (check RPy for that). + def default_ri2py(o): """ Convert :class:`rpy2.rinterface.Sexp` to higher-level objects, without copying the R objects. @@ -50,7 +52,7 @@ res = RObject(o) return res -ri2py = default_ri2py +conversion.ri2py = default_ri2py def default_py2ri(o): @@ -84,14 +86,14 @@ elif isinstance(o, unicode): res = rinterface.SexpVector([o, ], rinterface.STRSXP) elif isinstance(o, list): - res = r.list(*[ri2py(py2ri(x)) for x in o]) + res = r.list(*[conversion.ri2py(conversion.py2ri(x)) for x in o]) elif isinstance(o, complex): res = rinterface.SexpVector([o, ], rinterface.CPLXSXP) else: raise(ValueError("Nothing can be done for the type %s at the moment." %(type(o)))) return res -py2ri = default_py2ri +conversion.py2ri = default_py2ri def default_py2ro(o): @@ -102,7 +104,7 @@ res = default_py2ri(o) return default_ri2py(res) -py2ro = default_py2ro +conversion.py2ro = default_py2ro def repr_robject(o, linesep=os.linesep): @@ -211,7 +213,7 @@ def __init__(self, o): if not isinstance(o, rinterface.SexpVector): - o = py2ri(o) + o = conversion.py2ri(o) super(RVector, self).__init__(o) self.r = RVectorDelegator(self) @@ -231,9 +233,9 @@ - an index is itself a vector of elements to select """ - args = [py2ro(x) for x in args] + args = [conversion.py2ro(x) for x in args] for k, v in kwargs.itervalues(): - args[k] = py2ro(v) + args[k] = conversion.py2ro(v) res = r["["](*([self, ] + [x for x in args]), **kwargs) return res @@ -241,15 +243,15 @@ def assign(self, index, value): if not (isinstance(index, rlc.TaggedList) | \ isinstance(index, rlc.ArgsDict)): - args = rlc.TaggedList([py2ro(index), ]) + args = rlc.TaggedList([conversion.py2ro(index), ]) else: for i in xrange(len(index)): - index[i] = py2ro(index[i]) + index[i] = conversion.py2ro(index[i]) args = index - args.append(py2ro(value)) + args.append(conversion.py2ro(value)) args.insert(0, self) res = r["[<-"].rcall(args.items()) - res = ri2py(res) + res = conversion.ri2py(res) return res def __add__(self, x): @@ -259,11 +261,11 @@ def __getitem__(self, i): res = super(RVector, self).__getitem__(i) if isinstance(res, rinterface.Sexp): - res = ri2py(res) + res = conversion.ri2py(res) return res def __setitem__(self, i, value): - value = py2ri(value) + value = conversion.py2ri(value) res = super(RVector, self).__setitem__(i, value) def getnames(self): @@ -312,11 +314,11 @@ def getdim(self): res = r.dim(self) - res = ri2py(res) + res = conversion.ri2py(res) return res def setdim(self, value): - value = py2ro(value) + value = conversion.py2ro(value) res = r["dim<-"](self, value) #FIXME: not properly done raise(Exception("Not yet implemented")) @@ -387,7 +389,7 @@ :rtype: SexpVector """ res = baseNameSpaceEnv["rownames"](self) - return ri2py(res) + return conversion.ri2py(res) def colnames(self): """ Column names @@ -395,7 +397,7 @@ :rtype: SexpVector """ res = baseNameSpaceEnv["colnames"](self) - return ri2py(res) + return conversion.ri2py(res) class RFunction(RObjectMixin, rinterface.SexpClosure): @@ -404,12 +406,12 @@ """ def __call__(self, *args, **kwargs): - new_args = [py2ri(a) for a in args] + new_args = [conversion.py2ri(a) for a in args] new_kwargs = {} for k, v in kwargs.iteritems(): - new_kwargs[k] = py2ri(v) + new_kwargs[k] = conversion.py2ri(v) res = super(RFunction, self).__call__(*new_args, **new_kwargs) - res = ri2py(res) + res = conversion.ri2py(res) return res @@ -423,20 +425,20 @@ def __getitem__(self, item): res = super(REnvironment, self).__getitem__(item) - res = ri2py(res) + res = conversion.ri2py(res) return res def __setitem__(self, item, value): - robj = py2ro(value) + robj = conversion.py2ro(value) super(REnvironment, self).__setitem__(item, robj) def get(self, item): """ Get a object from its R name/symol :param item: string (name/symbol) - :rtype: object (as returned by :func:`ri2py`) + :rtype: object (as returned by :func:`conversion.ri2py`) """ res = super(REnvironment, self).get(item) - res = ri2py(res) + res = conversion.ri2py(res) return res @@ -444,7 +446,7 @@ def __getattr__(self, attr): res = self.do_slot(attr) - res = ri2py(res) + res = conversion.ri2py(res) return res @@ -461,7 +463,7 @@ def getenvironment(self): res = self.do_slot(".Environment") - res = ri2py(res) + res = conversion.ri2py(res) return res def setenvironment(self, val): @@ -500,7 +502,7 @@ def __getitem__(self, item): res = rinterface.globalEnv.get(item) - res = ri2py(res) + res = conversion.ri2py(res) return res #FIXME: check that this is properly working @@ -523,6 +525,6 @@ r = R() -globalEnv = ri2py(rinterface.globalEnv) -baseNameSpaceEnv = ri2py(rinterface.baseNameSpaceEnv) -emptyEnv = ri2py(rinterface.emptyEnv) +globalEnv = conversion.ri2py(rinterface.globalEnv) +baseNameSpaceEnv = conversion.ri2py(rinterface.baseNameSpaceEnv) +emptyEnv = conversion.ri2py(rinterface.emptyEnv) Copied: rpy2/trunk/rpy/robjects/conversion.py (from rev 699, rpy2/branches/version_2.0.x/rpy/robjects/conversion.py) =================================================================== --- rpy2/trunk/rpy/robjects/conversion.py (rev 0) +++ rpy2/trunk/rpy/robjects/conversion.py 2008-11-22 21:06:58 UTC (rev 700) @@ -0,0 +1,12 @@ + + +def ri2py(obj): + raise RuntimeError("Conversion function undefined") + +def py2ri(obj): + raise RuntimeError("Conversion function undefined") + +def py2ro(obj): + raise RuntimeError("Conversion function undefined") + + Property changes on: rpy2/trunk/rpy/robjects/conversion.py ___________________________________________________________________ Added: svn:mergeinfo + /rpy2/branches/version_2.0.x/rpy/robjects/conversion.py:679-696 Added: svn:eol-style + native Copied: rpy2/trunk/rpy/robjects/numpy2ri.py (from rev 699, rpy2/branches/version_2.0.x/rpy/robjects/numpy2ri.py) =================================================================== --- rpy2/trunk/rpy/robjects/numpy2ri.py (rev 0) +++ rpy2/trunk/rpy/robjects/numpy2ri.py 2008-11-22 21:06:58 UTC (rev 700) @@ -0,0 +1,53 @@ +import rpy2.robjects as ro +import rpy2.rinterface as rinterface +import numpy + +def numpy2ri(o): + if isinstance(o, numpy.ndarray): + if not o.dtype.isnative: + raise(ValueError("Cannot pass numpy arrays with non-native byte orders at the moment.")) + + # The possible kind codes are listed at + # http://numpy.scipy.org/array_interface.shtml + kinds = { + # "t" -> not really supported by numpy + "b": rinterface.LGLSXP, + "i": rinterface.INTSXP, + # "u" -> special-cased below + "f": rinterface.REALSXP, + "c": rinterface.CPLXSXP, + # "O" -> special-cased below + "S": rinterface.STRSXP, + "U": rinterface.STRSXP, + # "V" -> special-cased below + } + # Most types map onto R arrays: + if o.dtype.kind in kinds: + # "F" means "use column-major order" + vec = rinterface.SexpVector(o.ravel("F"), kinds[o.dtype.kind]) + dim = rinterface.SexpVector(o.shape, rinterface.INTSXP) + res = ro.r.array(vec, dim=dim) + # R does not support unsigned types: + elif o.dtype.kind == "u": + raise(ValueError("Cannot convert numpy array of unsigned values -- R does not have unsigned integers.")) + # Array-of-PyObject is treated like a Python list: + elif o.dtype.kind == "O": + res = ro.conversion.py2ri(list(o)) + # Record arrays map onto R data frames: + elif o.dtype.kind == "V": + if o.dtype.names is None: + raise(ValueError("Nothing can be done for this numpy array type %s at the moment." % (o.dtype,))) + df_args = [] + for field_name in o.dtype.names: + df_args.append((field_name, + ro.conversion.py2ri(o[field_name]))) + res = ro.baseNameSpaceEnv["data.frame"].rcall(tuple(df_args)) + # It should be impossible to get here: + else: + raise(ValueError("Unknown numpy array type.")) + else: + res = ro.default_py2ri(o) + return res + + +ro.conversion.py2ri = numpy2ri Property changes on: rpy2/trunk/rpy/robjects/numpy2ri.py ___________________________________________________________________ Added: svn:mergeinfo + /rpy2/branches/version_2.0.x/rpy/robjects/numpy2ri.py:679-696 Added: svn:eol-style + native Modified: rpy2/trunk/rpy/robjects/tests/__init__.py =================================================================== --- rpy2/trunk/rpy/robjects/tests/__init__.py 2008-11-22 19:50:46 UTC (rev 699) +++ rpy2/trunk/rpy/robjects/tests/__init__.py 2008-11-22 21:06:58 UTC (rev 700) @@ -9,6 +9,9 @@ import testREnvironment import testRobjects +# wrap this nicely so a warning is issued if no numpy present +import testNumpyConversions + def suite(): suite_RObject = testRObject.suite() suite_RVector = testRVector.suite() @@ -18,6 +21,7 @@ suite_REnvironment = testREnvironment.suite() suite_RFormula = testRFormula.suite() suite_Robjects = testRobjects.suite() + suite_NumpyConversions = testNumpyConversions.suite() alltests = unittest.TestSuite([suite_RObject, suite_RVector, suite_RArray, @@ -25,7 +29,9 @@ suite_RFunction, suite_REnvironment, suite_RFormula, - suite_Robjects ]) + suite_Robjects, + suite_NumpyConversions + ]) return alltests def main(): Copied: rpy2/trunk/rpy/robjects/tests/testNumpyConversions.py (from rev 699, rpy2/branches/version_2.0.x/rpy/robjects/tests/testNumpyConversions.py) =================================================================== --- rpy2/trunk/rpy/robjects/tests/testNumpyConversions.py (rev 0) +++ rpy2/trunk/rpy/robjects/tests/testNumpyConversions.py 2008-11-22 21:06:58 UTC (rev 700) @@ -0,0 +1,94 @@ +import unittest +import rpy2.robjects as robjects +r = robjects.r + +import numpy +import rpy2.robjects.numpy2ri as rpyn + + +class NumpyConversionsTestCase(unittest.TestCase): + + def setUp(self): + robjects.conversion.py2ri = rpyn.numpy2ri + + def tearDown(self): + robjects.conversion.py2ri = robjects.default_py2ri + + def checkHomogeneous(self, obj, mode, storage_mode): + converted = robjects.conversion.py2ri(obj) + self.assertEquals(r["mode"](converted)[0], mode) + self.assertEquals(r["storage.mode"](converted)[0], storage_mode) + self.assertEquals(list(obj), list(converted)) + self.assertTrue(r["is.array"](converted)[0]) + + def testVector(self): + + b = numpy.array([True, False, True], dtype=numpy.bool_) + self.checkHomogeneous(b, "logical", "logical") + + i = numpy.array([1, 2, 3], dtype="i") + self.checkHomogeneous(i, "numeric", "integer") + + f = numpy.array([1, 2, 3], dtype="f") + self.checkHomogeneous(f, "numeric", "double") + + c = numpy.array([1j, 2j, 3j], dtype=numpy.complex_) + self.checkHomogeneous(c, "complex", "complex") + + s = numpy.array(["a", "b", "c"], dtype="S") + self.checkHomogeneous(s, "character", "character") + + u = numpy.array([u"a", u"b", u"c"], dtype="U") + self.checkHomogeneous(u, "character", "character") + + def testArray(self): + + i2d = numpy.array([[1, 2, 3], [4, 5, 6]], dtype="i") + i2d_r = robjects.conversion.py2ri(i2d) + + self.assertEquals(r["storage.mode"](i2d_r)[0], "integer") + self.assertEquals(tuple(r["dim"](i2d_r)), (2, 3)) + + # Make sure we got the row/column swap right: + self.assertEquals(i2d_r.subset(1, 2)[0], i2d[0, 1]) + + f3d = numpy.arange(24, dtype="f").reshape((2, 3, 4)) + f3d_r = robjects.conversion.py2ri(f3d) + + self.assertEquals(r["storage.mode"](f3d_r)[0], "double") + self.assertEquals(tuple(r["dim"](f3d_r)), (2, 3, 4)) + + # Make sure we got the row/column swap right: + self.assertEquals(f3d_r.subset(1, 2, 3)[0], f3d[0, 1, 2]) + + def testObjectArray(self): + o = numpy.array([1, "a", 3.2], dtype=numpy.object_) + o_r = robjects.conversion.py2ri(o) + self.assertEquals(r["mode"](o_r)[0], "list") + self.assertEquals(r["[["](o_r, 1)[0], 1) + self.assertEquals(r["[["](o_r, 2)[0], "a") + self.assertEquals(r["[["](o_r, 3)[0], 3.2) + + def testRecordArray(self): + rec = numpy.array([(1, 2.3), (2, -0.7), (3, 12.1)], + dtype=[("count", "i"), ("value", numpy.double)]) + rec_r = robjects.conversion.py2ri(rec) + self.assertTrue(r["is.data.frame"](rec_r)[0]) + self.assertEquals(tuple(r["names"](rec_r)), ("count", "value")) + count_r = r["$"](rec_r, "count") + value_r = r["$"](rec_r, "value") + self.assertEquals(r["storage.mode"](count_r)[0], "integer") + self.assertEquals(r["storage.mode"](value_r)[0], "double") + self.assertEquals(count_r[1], 2) + self.assertEquals(value_r[2], 12.1) + + def testBadArray(self): + u = numpy.array([1, 2, 3], dtype=numpy.uint32) + self.assertRaises(ValueError, robjects.conversion.py2ri, u) + +def suite(): + return unittest.TestLoader().loadTestsFromTestCase(NumpyConversionsTestCase) + +if __name__ == '__main__': + unittest.main() + Modified: rpy2/trunk/rpy/robjects/tests/testRobjects.py =================================================================== --- rpy2/trunk/rpy/robjects/tests/testRobjects.py 2008-11-22 19:50:46 UTC (rev 699) +++ rpy2/trunk/rpy/robjects/tests/testRobjects.py 2008-11-22 21:06:58 UTC (rev 700) @@ -119,7 +119,7 @@ if inherits(pyobj, classname)[0]: pyobj = Density(pyobj) return pyobj - robjects.ri2py = f + robjects.conversion.ri2py = f x = robjects.r.rnorm(100) d = robjects.r.density(x) Modified: rpy2/trunk/rpy/rpy_classic.py =================================================================== --- rpy2/trunk/rpy/rpy_classic.py 2008-11-22 19:50:46 UTC (rev 699) +++ rpy2/trunk/rpy/rpy_classic.py 2008-11-22 21:06:58 UTC (rev 700) @@ -221,8 +221,10 @@ return res ##FIXME: not part of RPy-1.x. - def getSexp(self): + def get_sexp(self): return self.__sexp + + sexp = property(fget = get_sexp) #def __repr__(self): # res = rpy2py(self) Modified: rpy2/trunk/rpy/tests_rpy_classic.py =================================================================== --- rpy2/trunk/rpy/tests_rpy_classic.py 2008-11-22 19:50:46 UTC (rev 699) +++ rpy2/trunk/rpy/tests_rpy_classic.py 2008-11-22 21:06:58 UTC (rev 700) @@ -1,6 +1,7 @@ import unittest import rpy2.rpy_classic as rpy +import rpy2.rinterface class RpyClassicTestCase(unittest.TestCase): @@ -27,6 +28,12 @@ self.assertTrue(callable(rpy.r.seq)) self.assertTrue(callable(rpy.r.pi)) + def testSexp(self): + rpy.set_default_mode(rpy.NO_CONVERSION) + pi = rpy.r.pi + self.assertTrue(isinstance(pi.sexp, rpy2.rinterface.Sexp)) + self.assertRaises(AttributeError, setattr, pi, 'sexp', None) + def suite(): suite = unittest.TestLoader().loadTestsFromTestCase(RpyClassicTestCase) return suite Modified: rpy2/trunk/setup.py =================================================================== --- rpy2/trunk/setup.py 2008-11-22 19:50:46 UTC (rev 699) +++ rpy2/trunk/setup.py 2008-11-22 21:06:58 UTC (rev 700) @@ -11,6 +11,11 @@ if RHOMES is None: RHOMES = os.popen("R RHOME").readlines() + if len(RHOMES) == 0: + raise RuntimeError( + "R_HOME not defined, and no R command in the PATH." + ) + #Twist if 'R RHOME' spits out a warning if RHOMES[0].startswith("WARNING"): RHOMES = RHOMES[1] This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. ------------------------------------------------------------------------- This SF.Net email is sponsored by the Moblin Your Move Developer's challenge Build the coolest Linux based applications with Moblin SDK & win great prizes Grand prize is a trip for two to an Open Source event anywhere in the world http://moblin-contest.org/redirect.php?banner_id=100&url=/ _______________________________________________ rpy-list mailing list rpy-list@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/rpy-list