How to execute S4 class method in Python (using rpy2)?

我怕爱的太早我们不能终老 提交于 2020-12-13 11:17:09

问题


I have made the following S4 class in R, its constructor takes a variable called 'A', which is a string. The class has a method called 'test_method', which takes as input a string 'B' and returns whether 'A' equals 'B'.

test_class <- setRefClass(
  "test_class",
  fields=list(A="character"),
  methods = list(
    test_method = function(B) {
       if (A==B) {
           return('yes')
       } else {
           return('no')
       }
    }
  )
)

Now, I can make an instance of this class and execute 'test_method' on it:

object <- test_class(A='hello')
object$test_method('hi')

This returns 'no', because the strings do not equal.

Now I want to save this object I have made and execute 'test_method' from Python. I make an RDS:

saveRDS(object, 'object.rds')

Now I know how to read this object into Python using rpy2 (however I don't really care which package to use, as it works):

from rpy2.robjects import R
r = R()
r_object = r.readRDS("object.rds")

But how can I execute 'test_method' now? I tried this, but it doesn't work:

r_object.test_method('hi')

This throws: "AttributeError: 'RS4' object has no attribute 'test_method'"


回答1:


The rpy2 documentation about S4 classes does not cover well reference classes mostly because the rpy2 code predates the reference classes extension to S4.

In short, the S4 reference classes are not mapped to Python attributes, but writing a child Python class that does it is relatively easy.

import rpy2.robjects as ro
test_class = ro.r("""
  setRefClass(
    "test_class",
    fields=list(A="character"),
    methods = list(
      test_method = function(B) {
         if (A==B) {
             return('yes')
         } else {
             return('no')
         }
      }
    )
  )
""")
s4obj = test_class(A='hello')

We have a rather generic S4 object:

>>> type(s4obj)
rpy2.robjects.methods.RS4

Writing a child class that inherits from that generic class can look like this:

class TestClass(ro.methods.RS4): 
    def test_method(self, b): 
        return ro.baseenv['$'](self, 'test_method')(b) 

Casting is straightforward and computationally cheap (the parent class's constructor will just pass through an R object that is already an S4 object):

s4testobj = TestClass(s4obj)

Now we can do:

>>> tuple(s4testobj.test_method('hi'))  
 ('no',)                                         

For additional automagical creation of methods and attributes in Python from the R class definition, and conversion to the custom child, you'll need to use metaprogramming on the Python side, and define custom conversion (there is a link to the latter in the link to the documentation provided earlier).

Note: If not tied to S4 Reference Classes, the R6 class system for R is worth a look. An rpy2's mapping for that class system is here: https://github.com/rpy2/rpy2-R6.



来源:https://stackoverflow.com/questions/63069677/how-to-execute-s4-class-method-in-python-using-rpy2

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!