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 rpy-list@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/rpy-list