1""" 2R objects as Python objects. 3 4The module is structured around the singleton r of class R, 5that represents an embedded R. 6 7License: GPLv2+ 8 9""" 10 11import array 12import contextlib 13import os 14from functools import partial 15import types 16import rpy2.rinterface as rinterface 17import rpy2.rlike.container as rlc 18 19from rpy2.robjects.robject import RObjectMixin, RObject 20import rpy2.robjects.functions 21from rpy2.robjects.environments import (Environment, 22 local_context) 23from rpy2.robjects.methods import methods_env 24from rpy2.robjects.methods import RS4 25 26from . import conversion 27from . import vectors 28from . import language 29 30from rpy2.rinterface import (Sexp, 31 SexpVector, 32 SexpClosure, 33 SexpEnvironment, 34 SexpS4, 35 StrSexpVector, 36 SexpExtPtr) 37 38from rpy2.robjects.functions import Function 39from rpy2.robjects.functions import SignatureTranslatedFunction 40 41 42_globalenv = rinterface.globalenv 43_reval = rinterface.baseenv['eval'] 44 45BoolVector = vectors.BoolVector 46IntVector = vectors.IntVector 47FloatVector = vectors.FloatVector 48ComplexVector = vectors.ComplexVector 49StrVector = vectors.StrVector 50FactorVector = vectors.FactorVector 51Vector = vectors.Vector 52PairlistVector = vectors.PairlistVector 53ListVector = vectors.ListVector 54DateVector = vectors.DateVector 55POSIXct = vectors.POSIXct 56POSIXlt = vectors.POSIXlt 57Array = vectors.Array 58Matrix = vectors.Matrix 59DataFrame = vectors.DataFrame 60 61# Missing values. 62NA_Real = rinterface.NA_Real 63NA_Integer = rinterface.NA_Integer 64NA_Logical = rinterface.NA_Logical 65NA_Character = rinterface.NA_Character 66NA_Complex = rinterface.NA_Complex 67NULL = rinterface.NULL 68 69 70def reval(string, envir=_globalenv): 71 """ Evaluate a string as R code 72 - string: a string 73 - envir: an environment in which the environment should take place 74 (default: R's global environment) 75 """ 76 p = rinterface.parse(string) 77 res = _reval(p, envir=envir) 78 return res 79 80 81default_converter = conversion.Converter('base empty converter') 82 83 84@default_converter.rpy2py.register(RObject) 85def _rpy2py_robject(obj): 86 return obj 87 88 89def _vector_matrix_array(obj, vector_cls, matrix_cls, array_cls): 90 # Should it be promoted to array or matrix ? 91 try: 92 dim = obj.do_slot("dim") 93 if len(dim) == 2: 94 return matrix_cls 95 else: 96 return array_cls 97 except Exception: 98 return vector_cls 99 100 101@default_converter.rpy2py.register(rinterface.IntSexpVector) 102def _convert_rpy2py_intvector(obj): 103 clsmap = conversion.converter.rpy2py_nc_name[rinterface.IntSexpVector] 104 cls = clsmap.find(obj.rclass) 105 return cls(obj) 106 107 108@default_converter.rpy2py.register(rinterface.FloatSexpVector) 109def _convert_rpy2py_floatvector(obj): 110 clsmap = conversion.converter.rpy2py_nc_name[rinterface.FloatSexpVector] 111 cls = clsmap.find(obj.rclass) 112 return cls(obj) 113 114 115@default_converter.rpy2py.register(rinterface.ComplexSexpVector) 116def _convert_rpy2py_complexvector(obj): 117 clsmap = conversion.converter.rpy2py_nc_name[rinterface.ComplexSexpVector] 118 cls = clsmap.find(obj.rclass) 119 return cls(obj) 120 121 122@default_converter.rpy2py.register(rinterface.BoolSexpVector) 123def _convert_rpy2py_boolvector(obj): 124 clsmap = conversion.converter.rpy2py_nc_name[rinterface.BoolSexpVector] 125 cls = clsmap.find(obj.rclass) 126 return cls(obj) 127 128 129@default_converter.rpy2py.register(rinterface.StrSexpVector) 130def _convert_rpy2py_strvector(obj): 131 cls = _vector_matrix_array(obj, vectors.StrVector, 132 vectors.StrMatrix, vectors.StrArray) 133 return cls(obj) 134 135 136@default_converter.rpy2py.register(rinterface.ByteSexpVector) 137def _convert_rpy2py_bytevector(obj): 138 cls = _vector_matrix_array(obj, vectors.ByteVector, 139 vectors.ByteMatrix, vectors.ByteArray) 140 return cls(obj) 141 142 143default_converter.rpy2py.register(rinterface.PairlistSexpVector, PairlistVector) 144 145@default_converter.rpy2py.register(rinterface.LangSexpVector) 146def _convert_rpy2py_langvector(obj): 147 if 'formula' in obj.rclass: 148 cls = Formula 149 else: 150 cls = language.LangVector 151 return cls(obj) 152 153 154TYPEORDER = {bool: (0, BoolVector), 155 int: (1, IntVector), 156 float: (2, FloatVector), 157 complex: (3, ComplexVector), 158 str: (4, StrVector)} 159 160 161def sequence_to_vector(lst): 162 curr_typeorder = -1 163 i = None 164 for i, elt in enumerate(lst): 165 cls = type(elt) 166 if cls in TYPEORDER: 167 if TYPEORDER[cls][0] > curr_typeorder: 168 curr_typeorder, curr_type = TYPEORDER[cls] 169 else: 170 raise ValueError('The element %i in the list has a type ' 171 'that cannot be handled.' % i) 172 if i is None: 173 raise ValueError('The parameter "lst" is an empty sequence. ' 174 'The type of the corresponding R vector cannot ' 175 'be determined.') 176 res = curr_type(lst) 177 return res 178 179 180@default_converter.py2rpy.register(rinterface._MissingArgType) 181def _py2rpy_missingargtype(obj): 182 return obj 183 184 185@default_converter.py2rpy.register(bool) 186def _py2rpy_bool(obj): 187 return obj 188 189 190@default_converter.py2rpy.register(int) 191def _py2rpy_int(obj): 192 return obj 193 194 195@default_converter.py2rpy.register(float) 196def _py2rpy_float(obj): 197 return obj 198 199 200@default_converter.py2rpy.register(bytes) 201def _py2rpy_bytes(obj): 202 return obj 203 204 205@default_converter.py2rpy.register(str) 206def _py2rpy_str(obj): 207 return obj 208 209 210@default_converter.rpy2py.register(SexpClosure) 211def _rpy2py_sexpclosure(obj): 212 return SignatureTranslatedFunction(obj) 213 214 215@default_converter.rpy2py.register(SexpEnvironment) 216def _rpy2py_sexpenvironment(obj): 217 return Environment(obj) 218 219 220@default_converter.rpy2py.register(rinterface.ListSexpVector) 221def _rpy2py_listsexp(obj): 222 clsmap = conversion.converter.rpy2py_nc_name[rinterface.ListSexpVector] 223 cls = clsmap.find(obj.rclass) 224 return cls(obj) 225 226 227@default_converter.rpy2py.register(SexpS4) 228def _rpy2py_sexps4(obj): 229 clsmap = conversion.converter.rpy2py_nc_name[SexpS4] 230 cls = clsmap.find(methods_env['extends'](obj.rclass)) 231 return cls(obj) 232 233 234@default_converter.rpy2py.register(SexpExtPtr) 235def _rpy2py_sexpextptr(obj): 236 return obj 237 238 239@default_converter.rpy2py.register(object) 240def _rpy2py_object(obj): 241 return RObject(obj) 242 243 244@default_converter.rpy2py.register(type(NULL)) 245def _rpy2py_null(obj): 246 return obj 247 248 249# TODO: delete ? 250def default_py2ri(o): 251 """ Convert an arbitrary Python object to a 252 :class:`rpy2.rinterface.Sexp` object. 253 Creates an R object with the content of the Python object, 254 wich means data copying. 255 :param o: object 256 :rtype: :class:`rpy2.rinterface.Sexp` (and subclasses) 257 """ 258 pass 259 260 261@default_converter.py2rpy.register(RObject) 262def _py2rpy_robject(obj): 263 return rinterface.Sexp(obj) 264 265 266@default_converter.py2rpy.register(Sexp) 267def _py2rpy_sexp(obj): 268 return obj 269 270 271@default_converter.py2rpy.register(array.array) 272def _py2rpy_array(obj): 273 if obj.typecode in ('h', 'H', 'i', 'I'): 274 res = IntVector(obj) 275 elif obj.typecode in ('f', 'd'): 276 res = FloatVector(obj) 277 else: 278 raise( 279 ValueError('Nothing can be done for this array ' 280 'type at the moment.') 281 ) 282 return res 283 284 285default_converter.py2rpy.register(int, 286 lambda x: x) 287 288 289@default_converter.py2rpy.register(list) 290def _py2rpy_list(obj): 291 return vectors.ListVector( 292 rinterface.ListSexpVector( 293 [conversion.py2rpy(x) for x in obj] 294 ) 295 ) 296 297 298@default_converter.py2rpy.register(rlc.TaggedList) 299def _py2rpy_taggedlist(obj): 300 res = vectors.ListVector( 301 rinterface.ListSexpVector([conversion.py2rpy(x) for x in obj]) 302 ) 303 res.do_slot_assign('names', rinterface.StrSexpVector(obj.tags)) 304 return res 305 306 307@default_converter.py2rpy.register(complex) 308def _py2rpy_complex(obj): 309 return obj 310 311 312@default_converter.py2rpy.register(types.FunctionType) 313def _function_to_rpy(func): 314 def wrap(*args): 315 res = func(*args) 316 res = conversion.py2ro(res) 317 return res 318 rfunc = rinterface.rternalize(wrap) 319 return conversion.rpy2py(rfunc) 320 321 322@default_converter.rpy2py.register(object) 323def _(obj): 324 return obj 325 326 327default_converter._rpy2py_nc_map.update( 328 { 329 rinterface.SexpS4: conversion.NameClassMap(RS4), 330 rinterface.IntSexpVector: conversion.NameClassMap( 331 lambda obj: _vector_matrix_array(obj, vectors.IntVector, 332 vectors.IntMatrix, vectors.IntArray)(obj), 333 {'factor': FactorVector}), 334 rinterface.FloatSexpVector: conversion.NameClassMap( 335 lambda obj: _vector_matrix_array(obj, vectors.FloatVector, 336 vectors.FloatMatrix, vectors.FloatArray)(obj), 337 {'Date': DateVector, 338 'POSIXct': POSIXct}), 339 rinterface.BoolSexpVector: conversion.NameClassMap( 340 lambda obj: _vector_matrix_array(obj, vectors.BoolVector, 341 vectors.BoolMatrix, vectors.BoolArray)(obj) 342 ), 343 rinterface.ByteSexpVector: conversion.NameClassMap( 344 lambda obj: _vector_matrix_array(obj, vectors.ByteVector, 345 vectors.ByteMatrix, vectors.ByteArray)(obj) 346 ), 347 rinterface.StrSexpVector: conversion.NameClassMap( 348 lambda obj: _vector_matrix_array(obj, vectors.StrVector, 349 vectors.StrMatrix, vectors.StrArray)(obj) 350 ), 351 rinterface.ComplexSexpVector: conversion.NameClassMap( 352 lambda obj: _vector_matrix_array(obj, vectors.ComplexVector, 353 vectors.ComplexMatrix, vectors.ComplexArray)(obj) 354 ), 355 rinterface.ListSexpVector: conversion.NameClassMap( 356 ListVector, 357 {'data.frame': DataFrame}), 358 rinterface.SexpEnvironment: conversion.NameClassMap(Environment) 359 } 360) 361 362class Formula(RObjectMixin, rinterface.Sexp): 363 364 def __init__(self, formula, environment=_globalenv): 365 if isinstance(formula, str): 366 inpackage = rinterface.baseenv["::"] 367 asformula = inpackage(rinterface.StrSexpVector(['stats', ]), 368 rinterface.StrSexpVector(['as.formula', ])) 369 formula = rinterface.StrSexpVector([formula, ]) 370 robj = asformula(formula, 371 env=environment) 372 else: 373 robj = formula 374 super(Formula, self).__init__(robj) 375 376 def getenvironment(self): 377 """ Get the environment in which the formula is finding its symbols.""" 378 res = self.do_slot(".Environment") 379 res = conversion.rpy2py(res) 380 return res 381 382 def setenvironment(self, val): 383 """ Set the environment in which a formula will find its symbols.""" 384 if not isinstance(val, rinterface.SexpEnvironment): 385 raise TypeError('The environment must be an instance of' 386 ' rpy2.rinterface.Sexp.environment') 387 self.do_slot_assign('.Environment', val) 388 389 environment = property(getenvironment, setenvironment, None, 390 'R environment in which the formula will look for ' 391 'its variables.') 392 393 394class R(object): 395 """ 396 Singleton representing the embedded R running. 397 """ 398 _instance = None 399 400 def __new__(cls): 401 if cls._instance is None: 402 rinterface.initr_simple() 403 cls._instance = object.__new__(cls) 404 return cls._instance 405 406 def __getattribute__(self, attr): 407 try: 408 return super(R, self).__getattribute__(attr) 409 except AttributeError as ae: 410 orig_ae = str(ae) 411 412 try: 413 return self.__getitem__(attr) 414 except LookupError: 415 raise AttributeError(orig_ae) 416 417 def __getitem__(self, item): 418 res = _globalenv.find(item) 419 res = conversion.rpy2py(res) 420 if hasattr(res, '__rname__'): 421 res.__rname__ = item 422 return res 423 424 # TODO: check that this is properly working 425 def __cleanup__(self): 426 rinterface.embedded.endr(0) 427 del(self) 428 429 def __str__(self): 430 version = self['version'] 431 s = [super(R, self).__str__()] 432 s.extend('%s: %s' % (n, val[0]) 433 for n, val in zip(version.names, version)) 434 return os.linesep.join(s) 435 436 def __call__(self, string): 437 p = rinterface.parse(string) 438 res = self.eval(p) 439 return conversion.rpy2py(res) 440 441 442r = R() 443rl = language.LangVector.from_string 444 445conversion.set_conversion(default_converter) 446 447globalenv = conversion.converter.rpy2py(_globalenv) 448baseenv = conversion.converter.rpy2py(rinterface.baseenv) 449emptyenv = conversion.converter.rpy2py(rinterface.emptyenv) 450