Revision: 562
http://rpy.svn.sourceforge.net/rpy/?rev=562&view=rev
Author: lgautier
Date: 2008-06-12 02:06:37 -0700 (Thu, 12 Jun 2008)
Log Message:
-----------
rinterface:
- functions evaluated in R_GlobalEnv
robjects:
- methods __setitem__, getNames and setNames for RVector
- method getNames for RArray
- docstring for "RObjectMixin.rclass()"
doc:
- robjects: minor editing, list the classes RArray and RMatrix
- rinterface: more about vector element names
- notes on the design
demos:
- improved the R GTK console
- fixed listing of graphical devices
Modified Paths:
--------------
branches/rpy_nextgen/demos/radmin.py
branches/rpy_nextgen/doc/source/overview.rst
branches/rpy_nextgen/doc/source/rinterface.rst
branches/rpy_nextgen/doc/source/robjects.rst
branches/rpy_nextgen/rpy/rinterface/rinterface.c
branches/rpy_nextgen/rpy/robjects/__init__.py
branches/rpy_nextgen/rpy/robjects/tests/testRArray.py
branches/rpy_nextgen/rpy/robjects/tests/testRFunction.py
branches/rpy_nextgen/rpy/robjects/tests/testRVector.py
Modified: branches/rpy_nextgen/demos/radmin.py
===================================================================
--- branches/rpy_nextgen/demos/radmin.py 2008-06-10 17:35:25 UTC (rev
561)
+++ branches/rpy_nextgen/demos/radmin.py 2008-06-12 09:06:37 UTC (rev
562)
@@ -252,11 +252,11 @@
except:
return
for dev, name in itertools.izip(devices, names):
- if current_device == dev[0]:
+ if current_device == dev:
cur = "X"
else:
cur = ""
- row = [cur, dev[0], name[0], ""]
+ row = [cur, dev, name, ""]
self._table.append(row)
def searchOpenedDevices(self, widget, data = None):
@@ -354,7 +354,7 @@
self._rpad.show()
s_window.add(self._rpad)
self.add(s_window)
- evalButton = gtk.Button("Evaluate")
+ evalButton = gtk.Button("Evaluate highlighted code")
evalButton.connect("clicked", self.evaluateAction, "evaluate")
evalButton.show()
self.pack_start(evalButton, False, False, 0)
@@ -422,6 +422,12 @@
class ConsolePanel(gtk.VBox):
+ tag_table = None
+ _buffer = None
+ _view = None
+ _evalButton = None
+ _start_mark = None
+
def __init__(self):
super(ConsolePanel, self).__init__()
s_window = gtk.ScrolledWindow()
@@ -452,7 +458,7 @@
self.add(s_window)
evalButton = gtk.Button("Evaluate")
evalButton.connect("clicked", self.evaluateAction, "evaluate")
- evalButton.show()
+ #evalButton.show()
self.pack_start(evalButton, False, False, 0)
self._evalButton = evalButton
self.append("> ", "input")
@@ -461,8 +467,13 @@
self._start_mark = self._buffer.create_mark("beginCode",
location,
left_gravity=True)
+ self._firstEnter = False
+
def actionKeyPress(self, view, event):
- pass
+ if (event.keyval == gtk.gdk.keyval_from_name("Return")):
+ self.append("\n", "input")
+ self.evaluateAction(self._evalButton)
+ return True
def append(self, text, tag="input"):
tag = self.tag_table.lookup(tag)
@@ -477,6 +488,7 @@
def evaluateAction(self, widget, data=None):
buffer = self._buffer
start_iter = buffer.get_iter_at_mark(self._start_mark)
+
stop_iter = buffer.get_iter_at_offset(buffer.get_char_count())
rcode = buffer.get_text(start_iter, stop_iter)
@@ -496,10 +508,13 @@
self.append("\n> ", "input")
- buffer.move_mark(self._start_mark,
- buffer.get_iter_at_offset(buffer.get_char_count()))
+ textIter = buffer.get_iter_at_offset(buffer.get_char_count())
+ buffer.move_mark(self._start_mark, textIter)
+ buffer.move_mark_by_name("insert",
+ textIter)
+ buffer.move_mark_by_name("selection_bound",
+ textIter)
-
class Main(object):
def __init__(self):
Modified: branches/rpy_nextgen/doc/source/overview.rst
===================================================================
--- branches/rpy_nextgen/doc/source/overview.rst 2008-06-10 17:35:25 UTC
(rev 561)
+++ branches/rpy_nextgen/doc/source/overview.rst 2008-06-12 09:06:37 UTC
(rev 562)
@@ -58,3 +58,21 @@
API.
+
+Design notes
+------------
+
+:mod:`rpy2.robjects` implements an extension to the interface in
+:mod:`rpy2.rinterface` by extending the classes for R
+objects defined there with child classes.
+
+The choice of inheritance was made to facilitate the implementation
+of mostly inter-exchangeable classes between :mod:`rpy2.rinterface`
+and :mod:`rpy2.robjects`: an :class:`rpy2.rinterface.SexpClosure`
+can be given any :class:`rpy2.robjects.RObject` as a parameter while
+any :class:`rpy2.robjects.RFunction` can be given any
+:class:`rpy2.rinterface.Sexp`.
+
+The module :mod:`rpy2.rpy_classic` is using delegation, letting us
+demonstrate how to extend :mod:`rpy2.rinterface` with an alternative
+to inheritance.
\ No newline at end of file
Modified: branches/rpy_nextgen/doc/source/rinterface.rst
===================================================================
--- branches/rpy_nextgen/doc/source/rinterface.rst 2008-06-10 17:35:25 UTC
(rev 561)
+++ branches/rpy_nextgen/doc/source/rinterface.rst 2008-06-12 09:06:37 UTC
(rev 562)
@@ -185,31 +185,54 @@
.. note::
The *__getitem__* operator *[*
is returning a Python scalar. Casting
- an *SexpVector* into a list is only a matter of calling
- the constructor *list*.
+ an *SexpVector* into a list is only a matter
+ either iterating through it, or simply calling
+ the constructor :func:`list`.
+Common attributes
+-----------------
+
+.. index::
+ single: names
+
Names
------
+^^^^^
In R, vectors can be named, that is each value in the vector
can be given a name (that is be associated a string).
The names are added to the other as an attribute (conveniently
-called names), and can be accessed as such:
+called `names`), and can be accessed as such:
>>> options = rinterface.globalEnv.get("options")()
>>> option_names = options.do_slot("names")
>>> [x for x in options_names]
+.. note::
+ Elements in a vector of names do not have to be unique.
.. index::
+ single: dim
+ single: dimnames
+
+
+Dim and dimnames
+^^^^^^^^^^^^^^^^
+
+In the case of an `array`, the names across the
+respective dimensions of the object are accessible
+through the slot named `dimnames`.
+
+
+
+.. index::
pair: SexpVector; numpy
Numpy
-----
-The SexpVector objects are made to behave like arrays as defined
-in the Python package numpy.
+The :class:`SexpVector` objects are made to behave like arrays as defined
+in the Python package :mod:`numpy`.
The functions *array* and *asarray* is all that is needed:
@@ -220,7 +243,7 @@
.. note::
- when using *asarray*, the data are not copied.
+ when using :meth:`asarray`, the data are not copied.
>>> nx_nc[2] = 42
>>> rx[2]
@@ -325,6 +348,9 @@
>>>
+
+
+
Misc. variables
===============
Modified: branches/rpy_nextgen/doc/source/robjects.rst
===================================================================
--- branches/rpy_nextgen/doc/source/robjects.rst 2008-06-10 17:35:25 UTC
(rev 561)
+++ branches/rpy_nextgen/doc/source/robjects.rst 2008-06-12 09:06:37 UTC
(rev 562)
@@ -31,6 +31,8 @@
- no CONVERSION mode in :mod:`rpy2`, the design has made this unnecessary
+
+
`r`: the instance of `R`
==============================
@@ -45,8 +47,8 @@
embedded R process, and 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 ctypes.
+Readers familiar with the :mod:`ctypes` module for Python will note
+the similarity with it.
R vectors:
@@ -59,19 +61,33 @@
>>> plot = robjects.r.plot
>>> dir = robjects.r.dir
-Just like it was the case with RPy-1.x, on-the-fly
+
+Strings as R code
+-----------------
+
+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:
+by calling the `r` instance:
->>> robjects.r('1:2')
+>>> robjects.r('1+2')
3
>>> sqr = ro.r('function(x) x^2)
+
>>> sqr
function (x)
x^2
>>> sqr(2)
4
+The astute reader will quickly realize that R objects named
+by python variables can
+be plugged into code by their string representation:
+
+>>> x = robjects.r.rnorm(100)
+>>> robjects.r('hist(%s, xlab="x", main="hist(x)")' %x.__repr__())
+
+
+
.. index::
pair: robjects;RObject
@@ -107,9 +123,9 @@
Operators
---------
-Mathematical operations on vectors: the following operations
-are performed element-wise, recycling the shortest vector if
-necessary.
+Mathematical operations on two vectors: the following operations
+are performed element-wise, recycling the shortest vector if, and
+as much as, necessary.
+--------+---------+
| ``+`` | Add |
@@ -122,6 +138,10 @@
+--------+---------+
| ``**`` | Power |
+--------+---------+
+| ``or`` | Or |
++--------+---------+
+| ``and``| And |
++--------+---------+
.. index::
pair: RVector;indexing
@@ -145,6 +165,9 @@
integer(0)
>>> x.subset(1)
1L
+
+The two next examples demonstrate features of `R` regarding indexing,
+respectively element exclusion and recycling rule:
>>> x.subset(-1)
2:10
>>> x.subset(True)
@@ -175,6 +198,19 @@
pair: robjects;REnvironment
pair: robjects;globalEnv
+:class:`RArray`
+---------------
+
+In `R`, arrays are simply vectors with a dimension attribute. That fact
+was reflected in the class hierarchy with :class:`robjects.RArray` inheriting
+from :class:`robjects.RVector`.
+
+:class:`RMatrix`
+----------------
+
+A :class:`RMatrix` is a special case of :class:`RArray`.
+
+
R environments
==============
@@ -200,15 +236,15 @@
Care must be taken when assigning objects into an environment
-such as the Global Environment, as it can hide other objects
+such as the Global Environment, as this can hide other objects
with an identical name.
-For example:
+The following example should make one measure that this can mean
+trouble if no care is taken:
>>> globalEnv["pi"] = 123
>>> robjects.r.pi
123L
>>>
-
>>> robjects.r.rm("pi")
>>> robjects.r.pi
3.1415926535897931
Modified: branches/rpy_nextgen/rpy/rinterface/rinterface.c
===================================================================
--- branches/rpy_nextgen/rpy/rinterface/rinterface.c 2008-06-10 17:35:25 UTC
(rev 561)
+++ branches/rpy_nextgen/rpy/rinterface/rinterface.c 2008-06-12 09:06:37 UTC
(rev 562)
@@ -771,8 +771,8 @@
}
//FIXME: R_GlobalContext ?
- //PROTECT(res_R = do_eval_expr(call_R, R_GlobalEnv));
- PROTECT(res_R = do_eval_expr(call_R, CLOENV(fun_R)));
+ PROTECT(res_R = do_eval_expr(call_R, R_GlobalEnv));
+ //PROTECT(res_R = do_eval_expr(call_R, CLOENV(fun_R)));
/* if (!res) { */
/* UNPROTECT(2); */
Modified: branches/rpy_nextgen/rpy/robjects/__init__.py
===================================================================
--- branches/rpy_nextgen/rpy/robjects/__init__.py 2008-06-10 17:35:25 UTC
(rev 561)
+++ branches/rpy_nextgen/rpy/robjects/__init__.py 2008-06-12 09:06:37 UTC
(rev 562)
@@ -105,6 +105,7 @@
return repr_robject(self)
def rclass(self):
+ """ Return the name of the R class for the object. """
return baseNameSpaceEnv["class"](self)
@@ -150,12 +151,24 @@
res = r["["](*([self, ] + [x for x in args]), **kwargs)
return res
+ def assign(self, *args):
+ #FIXME: value must be the last argument, but this can be
+ # challenging in since python kwargs do not enforce any order
+ args = [py2ro(x) for x in args]
+ res = r["[<-"](*([self, ] + [x for x in args]))
+
+ return res
+
def __getitem__(self, i):
res = super(RVector, self).__getitem__(i)
if isinstance(res, rinterface.Sexp):
res = ri2py(res)
return res
+ def __setitem__(self, i, value):
+ value = py2ri(value)
+ res = super(RVector, self).__setitem__(i, value)
+
def __add__(self, x):
res = r.get("+")(self, x)
return res
@@ -188,13 +201,20 @@
res = r.names(self)
return res
+ def setNames(self, value):
+ """ Return a vector of names
+ (like the R function 'names' does it)."""
+
+ res = r["names<-"](self, value)
+ return res
+
class RArray(RVector):
""" An R array """
def __init__(self, o):
super(RArray, self).__init__(o)
#import pdb; pdb.set_trace()
if not r["is.array"](self)[0]:
- raise(TypeError("The object must be reflecting an R array"))
+ raise(TypeError("The object must be representing an R array"))
def __getattr__(self, name):
if name == 'dim':
@@ -206,6 +226,13 @@
if name == 'dim':
value = py2ro(value)
res = r["dim<-"](value)
+
+ def getNames(self):
+ """ Return a list of name vectors
+ (like the R function 'dimnames' does it)."""
+
+ res = r.dimnames(self)
+ return res
class RMatrix(RArray):
Modified: branches/rpy_nextgen/rpy/robjects/tests/testRArray.py
===================================================================
--- branches/rpy_nextgen/rpy/robjects/tests/testRArray.py 2008-06-10
17:35:25 UTC (rev 561)
+++ branches/rpy_nextgen/rpy/robjects/tests/testRArray.py 2008-06-12
09:06:37 UTC (rev 562)
@@ -20,8 +20,17 @@
self.assertEquals(5, d[0])
self.assertEquals(3, d[1])
+ def testGetNames(self):
+ dimnames = robjects.r.list(['a', 'b', 'c'],
+ ['d', 'e'])
+ m = robjects.r.matrix(1, nrow=3, ncol=2,
+ dimnames = dimnames)
+ a = robjects.RArray(m)
+ res = a.getNames()
+ r_identical = robjects.r.identical
+ self.assertTrue(r_identical(dimnames[0], res[0]))
+ self.assertTrue(r_identical(dimnames[1], res[1]))
-
def suite():
suite = unittest.TestLoader().loadTestsFromTestCase(RArrayTestCase)
return suite
Modified: branches/rpy_nextgen/rpy/robjects/tests/testRFunction.py
===================================================================
--- branches/rpy_nextgen/rpy/robjects/tests/testRFunction.py 2008-06-10
17:35:25 UTC (rev 561)
+++ branches/rpy_nextgen/rpy/robjects/tests/testRFunction.py 2008-06-12
09:06:37 UTC (rev 562)
@@ -22,7 +22,21 @@
s = ro_f(ro_v)
+ def testCallWithSexp(self):
+ ro_f = robjects.baseNameSpaceEnv["sum"]
+ ri_vec = robjects.rinterface.SexpVector([1,2,3],
+ robjects.rinterface.INTSXP)
+ res = ro_f(ri_vec)
+ self.assertEquals(6, res[0])
+ def testCallClosureWithRObject(self):
+ ri_f = rinterface.baseNameSpaceEnv["sum"]
+ ro_vec = robjects.RVector(array.array('i', [1,2,3]))
+ res = ri_f(ro_vec)
+ self.assertEquals(6, res[0])
+
+
+
def suite():
suite = unittest.TestLoader().loadTestsFromTestCase(RFunctionTestCase)
return suite
Modified: branches/rpy_nextgen/rpy/robjects/tests/testRVector.py
===================================================================
--- branches/rpy_nextgen/rpy/robjects/tests/testRVector.py 2008-06-10
17:35:25 UTC (rev 561)
+++ branches/rpy_nextgen/rpy/robjects/tests/testRVector.py 2008-06-12
09:06:37 UTC (rev 562)
@@ -33,7 +33,7 @@
self.assertEquals(mySeq[i] * 2, mySeqAdd[i])
- def testSubset(self):
+ def testSubsetByIndex(self):
seq_R = robjects.baseNameSpaceEnv["seq"]
mySeq = seq_R(0, 10)
# R indexing starts at one
@@ -43,6 +43,39 @@
for i, si in enumerate(myIndex):
self.assertEquals(mySeq[si-1], mySubset[i])
+ def testSubsetByName(self):
+ seq_R = robjects.baseNameSpaceEnv["seq"]
+ mySeq = seq_R(0, 25)
+
+ letters = robjects.baseNameSpaceEnv["letters"]
+ mySeq = robjects.baseNameSpaceEnv["names<-"](mySeq,
+ letters)
+
+ # R indexing starts at one
+ myIndex = robjects.RVector(letters[2])
+
+ mySubset = mySeq.subset(myIndex)
+
+ for i, si in enumerate(myIndex):
+ self.assertEquals(2, mySubset[i])
+
+ def testSubsetIndexError(self):
+ seq_R = robjects.baseNameSpaceEnv["seq"]
+ mySeq = seq_R(0, 10)
+ # R indexing starts at one
+ myIndex = robjects.RVector(['a', 'b', 'c'])
+
+ self.assertRaises(ri.RRuntimeError, mySeq.subset, myIndex)
+
+
+ def testAssign(self):
+ vec = robjects.r.seq(1, 10)
+ vec = vec.assign(array.array('i', [1, 3, 5]), 20)
+ self.assertEquals(20, vec[0])
+ self.assertEquals(20, vec[2])
+ self.assertEquals(20, vec[4])
+
+
def testSubsetRecyclingRule(self):
# recycling rule
v = robjects.RVector(array.array('i', range(1, 23)))
@@ -50,7 +83,7 @@
col = m.subset(True, 1)
self.assertEquals(11, len(col))
- def testSubsetLiet(self):
+ def testSubsetList(self):
# list
letters = robjects.baseNameSpaceEnv["letters"]
myList = rlist(l=letters, f="foo")
@@ -67,6 +100,15 @@
letters = robjects.baseNameSpaceEnv["letters"]
self.assertRaises(IndexError, letters.__getitem__, 26)
+ def testSetItem(self):
+ vec = robjects.r.seq(1, 10)
+ vec[0] = 20
+ self.assertEquals(20, vec[0])
+
+ def testSetItemOutOfBounds(self):
+ vec = robjects.r.seq(1, 10)
+ self.assertRaises(IndexError, vec.__setitem__, 20, 20)
+
def getItemList(self):
mylist = rlist(letters, "foo")
idem = robjects.baseNameSpaceEnv["identical"]
@@ -78,10 +120,20 @@
v_names = [robjects.baseNameSpaceEnv["letters"][x] for x in (0,1,2)]
#FIXME: simplify this
r_names = robjects.baseNameSpaceEnv["c"](*v_names)
- vec = robjects.r["names<-"](vec, r_names)
+ vec = robjects.baseNameSpaceEnv["names<-"](vec, r_names)
for i in xrange(len(vec)):
self.assertEquals(v_names[i], vec.getNames()[i])
+ vec.getNames()[0] = 'x'
+
+ def testSetNames(self):
+ vec = robjects.RVector(array.array('i', [1,2,3]))
+ names = ['x', 'y', 'z']
+ #FIXME: simplify this
+ vec = vec.setNames(names)
+ for i in xrange(len(vec)):
+ self.assertEquals(names[i], vec.getNames()[i])
+
def suite():
suite = unittest.TestLoader().loadTestsFromTestCase(RVectorTestCase)
return suite
This was sent by the SourceForge.net collaborative development platform, the
world's largest Open Source development site.
-------------------------------------------------------------------------
Check out the new SourceForge.net Marketplace.
It's the best place to buy or sell services for
just about anything Open Source.
http://sourceforge.net/services/buy/index.php
_______________________________________________
rpy-list mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/rpy-list