1import abc 2from rpy2.robjects.robject import RObjectMixin 3import rpy2.rinterface as rinterface 4from rpy2.rinterface_lib import sexp 5from . import conversion 6 7import rpy2.rlike.container as rlc 8 9import copy 10import itertools 11import math 12import os 13import jinja2 14import time 15import pytz 16import tzlocal 17from datetime import date, datetime, timedelta, timezone 18from time import struct_time, mktime, tzname 19from operator import attrgetter 20import warnings 21 22from rpy2.rinterface import (Sexp, ListSexpVector, StrSexpVector, 23 IntSexpVector, ByteSexpVector, BoolSexpVector, 24 ComplexSexpVector, PairlistSexpVector, 25 FloatSexpVector, NA_Real, NA_Integer, 26 NA_Character, NA_Logical, NULL, MissingArg) 27 28 29globalenv_ri = rinterface.globalenv 30baseenv_ri = rinterface.baseenv 31r_concat = baseenv_ri['c'] 32as_character = baseenv_ri['as.character'] 33utils_ri = baseenv_ri['as.environment']( 34 rinterface.StrSexpVector(("package:utils", )) 35) 36 37# The default timezone can be used for time or datetime objects. 38default_timezone = None 39 40 41class ExtractDelegator(object): 42 """ Delegate the R 'extraction' ("[") and 'replacement' ("[<-") 43 of items in a vector 44 or vector-like object. This can help making syntactic 45 niceties possible.""" 46 47 _extractfunction = rinterface.baseenv['['] 48 _replacefunction = rinterface.baseenv['[<-'] 49 50 def __init__(self, parent): 51 self._parent = parent 52 53 def __call__(self, *args, **kwargs): 54 """ Subset the "R-way.", using R's "[" function. 55 In a nutshell, R indexing differs from Python indexing on: 56 57 - indexing can be done with integers or strings (that are 'names') 58 59 - an index equal to TRUE will mean everything selected 60 (because of the recycling rule) 61 62 - integer indexing starts at one 63 64 - negative integer indexing means exclusion of the given integers 65 66 - an index is itself a vector of elements to select 67 """ 68 conv_args = [None, ] * (len(args)+1) 69 conv_args[0] = self._parent 70 for i, x in enumerate(args, 1): 71 if x is MissingArg: 72 conv_args[i] = x 73 else: 74 conv_args[i] = conversion.py2rpy(x) 75 kwargs = copy.copy(kwargs) 76 for k, v in kwargs.values(): 77 kwargs[k] = conversion.py2rpy(v) 78 fun = self._extractfunction 79 res = fun(*conv_args, **kwargs) 80 res = conversion.rpy2py(res) 81 return res 82 83 def __getitem__(self, item): 84 res = self(item) 85 return res 86 87 def __setitem__(self, item, value): 88 """ Assign a given value to a given index position in the vector. 89 The index position can either be: 90 - an int: x[1] = y 91 - a tuple of ints: x[1, 2, 3] = y 92 - an item-able object (such as a dict): x[{'i': 1}] = y 93 """ 94 fun = self._replacefunction 95 if type(item) is tuple: 96 args = list([None, ] * (len(item)+2)) 97 for i, v in enumerate(item): 98 if v is MissingArg: 99 continue 100 args[i+1] = conversion.py2rpy(v) 101 args[-1] = conversion.py2rpy(value) 102 args[0] = self._parent 103 res = fun(*args) 104 elif (type(item) is dict) or (type(item) is rlc.TaggedList): 105 args = rlc.TaggedList.from_items(item) 106 for i, (k, v) in enumerate(args.items()): 107 args[i] = conversion.py2rpy(v) 108 args.append(conversion.py2rpy(value), tag=None) 109 args.insert(0, self._parent, tag=None) 110 res = fun.rcall(tuple(args.items()), 111 globalenv_ri) 112 else: 113 args = [self._parent, 114 conversion.py2rpy(item), 115 conversion.py2rpy(value)] 116 res = fun(*args) 117 # TODO: check refcount and copying 118 self._parent.__sexp__ = res.__sexp__ 119 120 121class DoubleExtractDelegator(ExtractDelegator): 122 """ Delegate the R 'extraction' ("[[") and "replacement" ("[[<-") 123 of items in a vector 124 or vector-like object. This can help making syntactic 125 niceties possible.""" 126 _extractfunction = rinterface.baseenv['[['] 127 _replacefunction = rinterface.baseenv['[[<-'] 128 129 130class VectorOperationsDelegator(object): 131 """ 132 Delegate operations such as __getitem__, __add__, etc... 133 to the corresponding R function. 134 This permits a convenient coexistence between 135 operators on Python sequence object with their R conterparts. 136 """ 137 138 def __init__(self, parent): 139 """ The parent in expected to inherit from Vector. """ 140 self._parent = parent 141 142 def __add__(self, x): 143 res = globalenv_ri.find('+')(self._parent, conversion.py2rpy(x)) 144 return conversion.rpy2py(res) 145 146 def __sub__(self, x): 147 res = globalenv_ri.find('-')(self._parent, conversion.py2rpy(x)) 148 return conversion.rpy2py(res) 149 150 def __matmul__(self, x): 151 res = globalenv_ri.find("%*%")(self._parent, conversion.py2rpy(x)) 152 return conversion.rpy2py(res) 153 154 def __mul__(self, x): 155 res = globalenv_ri.find('*')(self._parent, conversion.py2rpy(x)) 156 return conversion.rpy2py(res) 157 158 def __pow__(self, x): 159 res = globalenv_ri.find('^')(self._parent, conversion.py2rpy(x)) 160 return conversion.rpy2py(res) 161 162 def __floordiv__(self, x): 163 res = globalenv_ri.find('%/%')(self._parent, conversion.py2rpy(x)) 164 return conversion.rpy2py(res) 165 166 def __truediv__(self, x): 167 res = globalenv_ri.find('/')(self._parent, conversion.py2rpy(x)) 168 return conversion.rpy2py(res) 169 170 def __mod__(self, x): 171 res = globalenv_ri.find('%%')(self._parent, conversion.py2rpy(x)) 172 return conversion.rpy2py(res) 173 174 def __or__(self, x): 175 res = globalenv_ri.find('|')(self._parent, conversion.py2rpy(x)) 176 return conversion.rpy2py(res) 177 178 def __and__(self, x): 179 res = globalenv_ri.find('&')(self._parent, conversion.py2rpy(x)) 180 return conversion.rpy2py(res) 181 182 def __invert__(self): 183 res = globalenv_ri.find('!')(self._parent) 184 return conversion.rpy2py(res) 185 186 # Comparisons 187 188 def __lt__(self, x): 189 res = globalenv_ri.find('<')(self._parent, conversion.py2rpy(x)) 190 return conversion.rpy2py(res) 191 192 def __le__(self, x): 193 res = globalenv_ri.find('<=')(self._parent, conversion.py2rpy(x)) 194 return conversion.rpy2py(res) 195 196 def __eq__(self, x): 197 res = globalenv_ri.find('==')(self._parent, conversion.py2rpy(x)) 198 return conversion.rpy2py(res) 199 200 def __ne__(self, x): 201 res = globalenv_ri.find('!=')(self._parent, conversion.py2rpy(x)) 202 return conversion.rpy2py(res) 203 204 def __gt__(self, x): 205 res = globalenv_ri.find('>')(self._parent, conversion.py2rpy(x)) 206 return conversion.rpy2py(res) 207 208 def __ge__(self, x): 209 res = globalenv_ri.find('>=')(self._parent, conversion.py2rpy(x)) 210 return conversion.rpy2py(res) 211 212 def __neg__(self): 213 res = globalenv_ri.find('-')(self._parent) 214 return res 215 216 def __contains__(self, what): 217 res = globalenv_ri.find('%in%')(what, self._parent) 218 return res[0] 219 220 221class Vector(RObjectMixin): 222 """Vector(seq) -> Vector. 223 224 The parameter 'seq' can be an instance inheriting from 225 rinterface.SexpVector, or an arbitrary Python object. 226 In the later case, a conversion will be attempted using 227 conversion.py2rpy(). 228 229 R vector-like object. Items can be accessed with: 230 231 - the method "__getitem__" ("[" operator) 232 233 - the delegators rx or rx2 234 """ 235 236 _sample = rinterface.baseenv['sample'] 237 238 _html_template = jinja2.Template( 239 """ 240 <span>{{ classname }} with {{ nelements }} elements.</span> 241 <table> 242 <tbody> 243 <tr> 244 {% for elt in elements %} 245 <td> 246 {{ elt }} 247 </td> 248 {% endfor %} 249 </tr> 250 </tbody> 251 </table> 252 """) 253 254 def _add_rops(self): 255 self.ro = VectorOperationsDelegator(self) 256 self.rx = ExtractDelegator(self) 257 self.rx2 = DoubleExtractDelegator(self) 258 259 def __add__(self, x): 260 res = baseenv_ri.find("c")(self, conversion.py2rpy(x)) 261 res = conversion.rpy2py(res) 262 return res 263 264 def __getitem__(self, i): 265 res = super().__getitem__(i) 266 267 if isinstance(res, Sexp): 268 res = conversion.rpy2py(res) 269 return res 270 271 def __setitem__(self, i, value): 272 value = conversion.py2rpy(value) 273 super().__setitem__(i, value) 274 275 @property 276 def names(self): 277 """Names for the items in the vector.""" 278 res = super().names 279 res = conversion.rpy2py(res) 280 return res 281 282 @names.setter 283 def names(self, value): 284 res = globalenv_ri.find("names<-")(self, conversion.py2rpy(value)) 285 self.__sexp__ = res.__sexp__ 286 287 def items(self): 288 """ iterator on names and values """ 289 # TODO: should be a view ? 290 if super().names.rsame(rinterface.NULL): 291 it_names = itertools.cycle((None, )) 292 else: 293 it_names = iter(self.names) 294 it_self = iter(self) 295 for v, k in zip(it_self, it_names): 296 yield (k, v) 297 298 def sample(self, n, replace: bool = False, probabilities=None): 299 """ Draw a random sample of size n from the vector. 300 301 If 'replace' is True, the sampling is done with replacement. 302 The optional argument 'probabilities' can indicate sampling 303 probabilities.""" 304 305 assert isinstance(n, int) 306 assert isinstance(replace, bool) 307 if probabilities is not None: 308 if len(probabilities) != len(self): 309 raise ValueError('The sequence of probabilities must ' 310 'match the length of the vector.') 311 if not isinstance(probabilities, rinterface.FloatSexpVector): 312 probabilities = FloatVector(probabilities) 313 res = self._sample(self, IntVector((n,)), 314 replace=BoolVector((replace, )), 315 prob=probabilities) 316 res = conversion.rpy2py(res) 317 return res 318 319 def repr_format_elt(self, elt, max_width=12): 320 max_width = int(max_width) 321 if elt in (NA_Real, NA_Integer, NA_Character, NA_Logical): 322 res = repr(elt) 323 elif isinstance(elt, int): 324 res = '%8i' % elt 325 elif isinstance(elt, float): 326 res = '%8f' % elt 327 else: 328 if isinstance(elt, str): 329 elt = repr(elt) 330 else: 331 elt = type(elt).__name__ 332 if len(elt) < max_width: 333 res = elt 334 else: 335 res = "%s..." % (str(elt[:(max_width - 3)])) 336 return res 337 338 def _iter_formatted(self, max_items=9): 339 format_elt = self.repr_format_elt 340 ln = len(self) 341 half_items = max_items // 2 342 if ln == 0: 343 return 344 elif ln < max_items: 345 for elt in conversion.noconversion(self): 346 yield format_elt(elt, max_width=math.floor(52 / ln)) 347 else: 348 for elt in conversion.noconversion(self)[:half_items]: 349 yield format_elt(elt) 350 yield '...' 351 for elt in conversion.noconversion(self)[-half_items:]: 352 yield format_elt(elt) 353 354 def __repr_content__(self): 355 return ''.join(('[', ', '.join(self._iter_formatted()), ']')) 356 357 def __repr__(self): 358 return super().__repr__() + os.linesep + \ 359 self.__repr_content__() 360 361 def _repr_html_(self, max_items=7): 362 d = {'elements': self._iter_formatted(max_items=max_items), 363 'classname': type(self).__name__, 364 'nelements': len(self)} 365 html = self._html_template.render(d) 366 return html 367 368 369class StrVector(Vector, StrSexpVector): 370 """Vector of string elements 371 372 StrVector(seq) -> StrVector. 373 374 The parameter 'seq' can be an instance inheriting from 375 rinterface.SexpVector, or an arbitrary Python sequence. 376 In the later case, all elements in the sequence should be either 377 strings, or have a str() representation. 378 """ 379 380 _factorconstructor = rinterface.baseenv['factor'] 381 382 NAvalue = rinterface.NA_Character 383 384 def __init__(self, obj): 385 super().__init__(obj) 386 self._add_rops() 387 388 def factor(self): 389 """ 390 factor() -> FactorVector 391 392 Construct a factor vector from a vector of strings. 393 """ 394 395 res = self._factorconstructor(self) 396 return conversion.rpy2py(res) 397 398 399class IntVector(Vector, IntSexpVector): 400 """ Vector of integer elements 401 IntVector(seq) -> IntVector. 402 403 The parameter 'seq' can be an instance inheriting from 404 rinterface.SexpVector, or an arbitrary Python sequence. 405 In the later case, all elements in the sequence should be either 406 integers, or have an int() representation. 407 """ 408 409 _tabulate = rinterface.baseenv['tabulate'] 410 411 NAvalue = rinterface.NA_Integer 412 413 def __init__(self, obj): 414 super().__init__(obj) 415 self._add_rops() 416 417 def repr_format_elt(self, elt, max_width=8): 418 max_width = int(max_width) 419 if elt == NA_Integer: 420 res = repr(NA_Integer) 421 else: 422 res = '{:,}'.format(elt) 423 return res 424 425 def tabulate(self, nbins=None): 426 """ Like the R function tabulate, 427 count the number of times integer values are found """ 428 if nbins is None: 429 nbins = max(1, max(self)) 430 res = self._tabulate(self) 431 return conversion.rpy2py(res) 432 433 434class BoolVector(Vector, BoolSexpVector): 435 """ Vector of boolean (logical) elements 436 BoolVector(seq) -> BoolVector. 437 438 The parameter 'seq' can be an instance inheriting from 439 rinterface.SexpVector, or an arbitrary Python sequence. 440 In the later case, all elements in the sequence should be either 441 booleans, or have a bool() representation. 442 """ 443 444 NAvalue = rinterface.NA_Logical 445 446 def __init__(self, obj): 447 super().__init__(obj) 448 self._add_rops() 449 450 451class ByteVector(Vector, ByteSexpVector): 452 """ Vector of byte elements 453 ByteVector(seq) -> ByteVector. 454 455 The parameter 'seq' can be an instance inheriting from 456 rinterface.SexpVector, or an arbitrary Python sequence. 457 In the later case, all elements in the sequence should be either 458 be bytes (integers >= 0 and <= 255). 459 """ 460 461 def __init__(self, obj): 462 super().__init__(obj) 463 self._add_rops() 464 465 466class ComplexVector(Vector, ComplexSexpVector): 467 """ Vector of complex elements 468 469 ComplexVector(seq) -> ComplexVector 470 471 The parameter 'seq' can be an instance inheriting from 472 rinterface.SexpVector, or an arbitrary Python sequence. 473 In the later case, all elements in the sequence should be either 474 complex, or have a complex() representation. 475 """ 476 477 NAvalue = rinterface.NA_Complex 478 479 def __init__(self, obj): 480 super().__init__(obj) 481 self._add_rops() 482 483 484class FloatVector(Vector, FloatSexpVector): 485 """ Vector of float (double) elements 486 487 FloatVector(seq) -> FloatVector. 488 489 The parameter 'seq' can be an instance inheriting from 490 rinterface.SexpVector, or an arbitrary Python sequence. 491 In the later case, all elements in the sequence should be either 492 float, or have a float() representation. 493 """ 494 495 NAvalue = rinterface.NA_Real 496 497 def __init__(self, obj): 498 super().__init__(obj) 499 self._add_rops() 500 501 502class FactorVector(IntVector): 503 """ Vector of 'factors'. 504 505 FactorVector(obj, 506 levels = rinterface.MissingArg, 507 labels = rinterface.MissingArg, 508 exclude = rinterface.MissingArg, 509 ordered = rinterface.MissingArg) -> FactorVector 510 511 obj: StrVector or StrSexpVector 512 levels: StrVector or StrSexpVector 513 labels: StrVector or StrSexpVector (of same length as levels) 514 exclude: StrVector or StrSexpVector 515 ordered: boolean 516 517 """ 518 519 _factor = baseenv_ri['factor'] 520 _levels = baseenv_ri['levels'] 521 _levels_set = baseenv_ri['levels<-'] 522 _nlevels = baseenv_ri['nlevels'] 523 _isordered = baseenv_ri['is.ordered'] 524 525 NAvalue = rinterface.NA_Integer 526 527 def __init__(self, obj, 528 levels=rinterface.MissingArg, 529 labels=rinterface.MissingArg, 530 exclude=rinterface.MissingArg, 531 ordered=rinterface.MissingArg): 532 if not isinstance(obj, Sexp): 533 obj = StrSexpVector(obj) 534 if ('factor' in obj.rclass) and \ 535 all(p is rinterface.MissingArg for p in (labels, 536 exclude, 537 ordered)): 538 res = obj 539 else: 540 res = self._factor(obj, 541 levels=levels, 542 labels=labels, 543 exclude=exclude, 544 ordered=ordered) 545 super(FactorVector, self).__init__(res) 546 547 def repr_format_elt(self, elt, max_width=8): 548 max_width = int(max_width) 549 levels = self._levels(self) 550 if elt is NA_Integer: 551 res = repr(elt) 552 else: 553 res = levels[elt-1] 554 if len(res) >= max_width: 555 res = "%s..." % (res[:(max_width - 3)]) 556 return res 557 558 def __levels_get(self): 559 res = self._levels(self) 560 return conversion.rpy2py(res) 561 562 def __levels_set(self, value): 563 res = self._levels_set(self, conversion.py2rpy(value)) 564 self.__sexp__ = res.__sexp__ 565 566 levels = property(__levels_get, __levels_set) 567 568 def __nlevels_get(self): 569 res = self._nlevels(self) 570 return res[0] 571 nlevels = property(__nlevels_get, None, None, "number of levels ") 572 573 def __isordered_get(self): 574 res = self._isordered(self) 575 return res[0] 576 isordered = property(__isordered_get, None, None, 577 "are the levels in the factor ordered ?") 578 579 def iter_labels(self): 580 """ Iterate the over the labels, that is iterate over 581 the items returning associated label for each item """ 582 levels = self.levels 583 for x in conversion.noconversion(self): 584 yield levels[x-1] 585 586 587class PairlistVector(Vector, PairlistSexpVector): 588 """R pairlist vector.""" 589 pass 590 591 592class ListVector(Vector, ListSexpVector): 593 """ R list (vector of arbitray elements) 594 595 ListVector(iterable) -> ListVector. 596 597 The parameter 'iterable' can be: 598 599 - an object with a method `items()`, such for example a dict, 600 a rpy2.rlike.container.TaggedList, 601 an rpy2.rinterface.SexpVector of type VECSXP. 602 603 - an iterable of (name, value) tuples 604 """ 605 _vector = rinterface.baseenv['vector'] 606 607 _html_template = jinja2.Template( 608 """ 609 <span>{{ classname }} with {{ nelements }} elements.</span> 610 <table> 611 <tbody> 612 {% for name, elt in names_elements %} 613 <tr> 614 <th> 615 {{ name }} 616 </th> 617 <td> 618 {{ elt }} 619 </td> 620 </tr> 621 {% endfor %} 622 </tbody> 623 </table> 624 """) 625 626 def __init__(self, tlist): 627 if isinstance(tlist, rinterface.ListSexpVector): 628 if tlist.typeof != rinterface.RTYPES.VECSXP: 629 raise ValueError("tlist should have " 630 "tlist.typeof == rinterface.RTYPES.VECSXP") 631 super().__init__(tlist) 632 elif hasattr(tlist, 'items') and callable(tlist.items): 633 kv = [(k, conversion.py2rpy(v)) for k, v in tlist.items()] 634 kv = tuple(kv) 635 df = baseenv_ri.find("list").rcall(kv, globalenv_ri) 636 super().__init__(df) 637 elif hasattr(tlist, "__iter__"): 638 if not callable(tlist.__iter__): 639 raise ValueError('tlist should have a /method/ __iter__ ' 640 '(not an attribute)') 641 kv = [(str(k), conversion.py2rpy(v)) for k, v in tlist] 642 kv = tuple(kv) 643 df = baseenv_ri.find("list").rcall(kv, globalenv_ri) 644 super().__init__(df) 645 else: 646 raise ValueError('tlist can only be either an iter-able or an ' 647 'instance of rpy2.rinterface.ListSexpVector, ' 648 'of R type VECSXP, or a Python dict.') 649 self._add_rops() 650 651 def _iter_repr(self, max_items=9): 652 if len(self) <= max_items: 653 for elt in conversion.noconversion(self): 654 yield elt 655 else: 656 half_items = max_items // 2 657 for i in range(0, half_items): 658 yield self[i] 659 yield '...' 660 for i in range(-half_items, 0): 661 yield self[i] 662 663 def __repr__(self): 664 res = [] 665 for i, elt in enumerate(self._iter_repr()): 666 if isinstance(elt, ListVector): 667 res.append(super().__repr__()) 668 elif isinstance(elt, str) and elt == '...': 669 res.append(elt) 670 else: 671 try: 672 name = self.names[i] 673 except TypeError: 674 name = '<no name>' 675 res.append(" %s: %s%s %s" 676 % (name, 677 type(elt), 678 os.linesep, 679 elt.__repr__())) 680 681 res = super().__repr__() + os.linesep + \ 682 os.linesep.join(res) 683 return res 684 685 def _repr_html_(self, max_items=7): 686 elements = list() 687 for e in self._iter_repr(max_items=max_items): 688 if hasattr(e, '_repr_html_'): 689 elements.append(e._repr_html_()) 690 else: 691 elements.append(e) 692 693 names = list() 694 rnames = self.names 695 if len(self) <= max_items: 696 names.extend( 697 rnames 698 if rnames != rinterface.NULL 699 else ['[no name]'] * len(self) 700 ) 701 else: 702 half_items = max_items // 2 703 for i in range(0, half_items): 704 try: 705 name = (rnames[i] 706 if rnames != rinterface.NULL else '[no name]') 707 except TypeError: 708 name = '[no name]' 709 names.append(name) 710 names.append('...') 711 for i in range(-half_items, 0): 712 try: 713 name = rnames[i] 714 except TypeError: 715 name = '[no name]' 716 names.append(name) 717 718 d = {'names_elements': zip(names, elements), 719 'nelements': len(self), 720 'classname': type(self).__name__} 721 html = self._html_template.render(d) 722 return html 723 724 @staticmethod 725 def from_length(length): 726 """ Create a list of given length """ 727 res = ListVector._vector(StrSexpVector(("list", )), length) 728 res = conversion.rpy2py(res) 729 return res 730 731 732class POSIXt(abc.ABC): 733 """ POSIX time vector. This is an abstract class. """ 734 735 def repr_format_elt(self, elt, max_width=12): 736 max_width = int(max_width) 737 str_elt = str(elt) 738 if len(str_elt) < max_width: 739 res = elt 740 else: 741 res = "%s..." % str_elt[:(max_width - 3)] 742 return res 743 744 def _iter_formatted(self, max_items=9): 745 ln = len(self) 746 half_items = max_items // 2 747 if ln == 0: 748 return 749 elif ln < max_items: 750 str_vec = StrVector(as_character(self)) 751 else: 752 str_vec = r_concat( 753 as_character( 754 self.rx(IntSexpVector(tuple(range(1, (half_items-1))))) 755 ), 756 StrSexpVector(['...']), 757 as_character( 758 self.rx(IntSexpVector(tuple(range((ln-half_items), ln)))) 759 )) 760 for str_elt in str_vec: 761 yield self.repr_format_elt(str_elt) 762 763 764class POSIXlt(POSIXt, ListVector): 765 """ Representation of dates with a 11-component structure 766 (similar to Python's time.struct_time). 767 768 POSIXlt(seq) -> POSIXlt. 769 770 The constructor accepts either an R vector 771 or a sequence (an object with the Python 772 sequence interface) of time.struct_time objects. 773 """ 774 775 _expected_colnames = { 776 x: i for i, x in enumerate( 777 ('sec', 'min', 'hour', 'mday', 'mon', 'year', 778 'wday', 'yday', 'isdst', 'zone', 'gmtoff')) 779 } 780 # R starts the week on Sunday while Python starts it on Monday 781 _wday_r_to_py = (6, 0, 1, 2, 3, 4, 5) 782 783 def __init__(self, seq): 784 """ 785 """ 786 if isinstance(seq, Sexp): 787 super()(seq) 788 else: 789 for elt in conversion.noconversion(seq): 790 if not isinstance(elt, struct_time): 791 raise ValueError( 792 'All elements must inherit from time.struct_time' 793 ) 794 as_posixlt = baseenv_ri['as.POSIXlt'] 795 origin = StrSexpVector([time.strftime("%Y-%m-%d", 796 time.gmtime(0)), ]) 797 rvec = FloatSexpVector([mktime(x) for x in seq]) 798 sexp = as_posixlt(rvec, origin=origin) 799 super().__init__(sexp) 800 801 def __getitem__(self, i): 802 # "[[" operator returns the components of a time object 803 # (and yes, this is confusing) 804 aslist = ListVector(self) 805 idx = self._expected_colnames 806 seq = (aslist[idx['year']][i]+1900, 807 aslist[idx['mon']][i]+1, 808 aslist[idx['mday']][i], 809 aslist[idx['hour']][i], 810 aslist[idx['min']][i], 811 aslist[idx['sec']][i], 812 self._wday_r_to_py[aslist[idx['wday']][i]], 813 aslist[idx['yday']][i]+1, 814 aslist[idx['isdst']][i]) 815 return struct_time( 816 seq, 817 {'tm_zone': aslist[idx['zone']][i], 818 'tmp_gmtoff': aslist[idx['gmtoff']][i]} 819 ) 820 821 def __repr__(self): 822 return super(Sexp, self).__repr__() 823 824 825def get_timezone(): 826 """Return the system's timezone settings.""" 827 if default_timezone: 828 timezone = default_timezone 829 else: 830 timezone = tzlocal.get_localzone() 831 return timezone 832 833 834class DateVector(FloatVector): 835 """ Representation of dates as number of days since 1/1/1970. 836 837 Date(seq) -> Date. 838 839 The constructor accepts either an R vector floats 840 or a sequence (an object with the Python 841 sequence interface) of time.struct_time objects. 842 """ 843 844 def __init__(self, seq): 845 """ Create a POSIXct from either an R vector or a sequence 846 of Python datetime.date objects. 847 """ 848 849 if isinstance(seq, Sexp): 850 init_param = seq 851 elif isinstance(seq[0], date): 852 init_param = DateVector.sexp_from_date(seq) 853 else: 854 raise TypeError( 855 'Unable to create an R Date vector from objects of type %s' % 856 type(seq)) 857 super().__init__(init_param) 858 859 @classmethod 860 def sexp_from_date(cls, seq): 861 return cls(FloatVector([x.toordinal() for x in seq])) 862 863 @staticmethod 864 def isrinstance(obj) -> bool: 865 """Return whether an R object an instance of Date.""" 866 return obj.rclass[-1] == 'Date' 867 868 869class POSIXct(POSIXt, FloatVector): 870 """ Representation of dates as seconds since Epoch. 871 This form is preferred to POSIXlt for inclusion in a DataFrame. 872 873 POSIXlt(seq) -> POSIXlt. 874 875 The constructor accepts either an R vector floats 876 or a sequence (an object with the Python 877 sequence interface) of time.struct_time objects. 878 """ 879 880 _as_posixct = baseenv_ri['as.POSIXct'] 881 _ISOdatetime = baseenv_ri['ISOdatetime'] 882 883 def __init__(self, seq): 884 """ Create a POSIXct from either an R vector or a sequence 885 of Python dates. 886 """ 887 888 if isinstance(seq, Sexp): 889 init_param = seq 890 elif isinstance(seq[0], struct_time): 891 init_param = POSIXct.sexp_from_struct_time(seq) 892 elif isinstance(seq[0], datetime): 893 init_param = POSIXct.sexp_from_datetime(seq) 894 else: 895 raise TypeError( 896 'All elements must inherit from time.struct_time or ' 897 'datetime.datetime.') 898 super().__init__(init_param) 899 900 @staticmethod 901 def _sexp_from_seq(seq, tz_info_getter, isodatetime_columns): 902 """ return a POSIXct vector from a sequence of time.struct_time 903 elements. """ 904 tz_count = 0 905 tz_info = None 906 for elt in conversion.noconversion(seq): 907 tmp = tz_info_getter(elt) 908 if tz_info is None: 909 tz_info = tmp 910 tz_count = 1 911 elif tz_info == tmp: 912 tz_count += 1 913 else: 914 # different time zones 915 # TODO: create a list of time zones with tz_count times 916 # tz_info, add the current tz_info and append further. 917 raise ValueError( 918 'Sequences of dates with different time zones not ' 919 'yet allowed.' 920 ) 921 922 if tz_info is None: 923 tz_info = tzname[0] 924 # We could use R's as.POSIXct instead of ISOdatetime 925 # since as.POSIXct is used by it anyway, but the overall 926 # interface for dates and conversion between formats 927 # is not exactly straightforward. Someone with more 928 # time should look into this. 929 930 d = isodatetime_columns(seq) 931 sexp = POSIXct._ISOdatetime(*d, tz=StrSexpVector((tz_info, ))) 932 return sexp 933 934 @staticmethod 935 def sexp_from_struct_time(seq): 936 def f(seq): 937 return [IntVector([x.tm_year for x in seq]), 938 IntVector([x.tm_mon for x in seq]), 939 IntVector([x.tm_mday for x in seq]), 940 IntVector([x.tm_hour for x in seq]), 941 IntVector([x.tm_min for x in seq]), 942 IntVector([x.tm_sec for x in seq])] 943 return POSIXct._sexp_from_seq(seq, lambda elt: time.tzname[0], f) 944 945 @staticmethod 946 def sexp_from_datetime(seq): 947 """ return a POSIXct vector from a sequence of 948 datetime.datetime elements. """ 949 def f(seq): 950 return [IntVector([x.year for x in seq]), 951 IntVector([x.month for x in seq]), 952 IntVector([x.day for x in seq]), 953 IntVector([x.hour for x in seq]), 954 IntVector([x.minute for x in seq]), 955 IntVector([x.second for x in seq])] 956 957 return POSIXct._sexp_from_seq(seq, attrgetter('tzinfo'), f) 958 959 @staticmethod 960 def isrinstance(obj) -> bool: 961 """Is an R object an instance of POSIXct.""" 962 return obj.rclass[0] == 'POSIXct' 963 964 @staticmethod 965 def _datetime_from_timestamp(ts, tz) -> datetime: 966 """Platform-dependent conversion from timestamp to datetime""" 967 if os.name != 'nt' or ts > 0: 968 return datetime.fromtimestamp(ts, tz) 969 else: 970 dt_utc = (datetime(1970, 1, 1, tzinfo=timezone.utc) + 971 timedelta(seconds=ts)) 972 dt = dt_utc.replace(tzinfo=tz) 973 return dt + dt.utcoffset() 974 975 def iter_localized_datetime(self): 976 """Iterator yielding localized Python datetime objects.""" 977 try: 978 r_tzone_name = self.do_slot('tzone')[0] 979 except LookupError: 980 warnings.warn('R object inheriting from "POSIXct" but without ' 981 'attribute "tzone".') 982 r_tzone_name = '' 983 if r_tzone_name == '': 984 # R is implicitly using the local timezone, while Python 985 # time libraries will assume UTC. 986 r_tzone = get_timezone() 987 else: 988 r_tzone = pytz.timezone(r_tzone_name) 989 990 for x in self: 991 yield ( 992 None if math.isnan(x) 993 else POSIXct._datetime_from_timestamp(x, r_tzone) 994 ) 995 996 997class Array(Vector): 998 """ An R array """ 999 _dimnames_get = baseenv_ri['dimnames'] 1000 _dimnames_set = baseenv_ri['dimnames<-'] 1001 _dim_get = baseenv_ri['dim'] 1002 _dim_set = baseenv_ri['dim<-'] 1003 _isarray = baseenv_ri['is.array'] 1004 1005 def __dim_get(self): 1006 res = self._dim_get(self) 1007 res = conversion.rpy2py(res) 1008 return res 1009 1010 def __dim_set(self, value): 1011 # TODO: R will create a copy of the object upon assignment 1012 # of a new dimension attribute. 1013 raise NotImplementedError("Not yet implemented") 1014 value = conversion.py2rpy(value) 1015 self._dim_set(self, value) 1016 1017 dim = property(__dim_get, __dim_set, None, 1018 "Get or set the dimension of the array.") 1019 1020 def __dimnames_get(self) -> sexp.Sexp: 1021 """ Return a list of name vectors 1022 (like the R function 'dimnames' does).""" 1023 1024 res = self._dimnames_get(self) 1025 res = conversion.rpy2py(res) 1026 return res 1027 1028 def __dimnames_set(self, value): 1029 """ Set list of name vectors 1030 (like the R function 'dimnames' does).""" 1031 1032 value = conversion.rpy2py(value) 1033 res = self._dimnames_set(self, value) 1034 self.__sexp__ = res.__sexp__ 1035 1036 names = property(__dimnames_get, __dimnames_set, None, 1037 "names associated with the dimension.") 1038 dimnames = names 1039 1040 1041class IntArray(Array, IntVector): 1042 pass 1043 1044 1045class ByteArray(Array, ByteVector): 1046 pass 1047 1048 1049class FloatArray(Array, FloatVector): 1050 pass 1051 1052 1053class BoolArray(Array, BoolVector): 1054 pass 1055 1056 1057class ComplexArray(Array, ComplexVector): 1058 pass 1059 1060 1061class StrArray(Array, StrVector): 1062 pass 1063 1064 1065class Matrix(Array): 1066 """ An R matrix """ 1067 _transpose = baseenv_ri['t'] 1068 _rownames = baseenv_ri['rownames'] 1069 _colnames = baseenv_ri['colnames'] 1070 _dot = baseenv_ri['%*%'] 1071 _matmul = baseenv_ri['%*%'] 1072 _crossprod = baseenv_ri['crossprod'] 1073 _tcrossprod = baseenv_ri['tcrossprod'] 1074 _svd = baseenv_ri['svd'] 1075 _eigen = baseenv_ri['eigen'] 1076 1077 def __nrow_get(self): 1078 """ Number of rows. 1079 :rtype: integer """ 1080 return self.dim[0] 1081 1082 nrow = property(__nrow_get, None, None, "Number of rows") 1083 1084 def __ncol_get(self): 1085 """ Number of columns. 1086 :rtype: integer """ 1087 return self.dim[1] 1088 1089 ncol = property(__ncol_get, None, None, "Number of columns") 1090 1091 def __rownames_get(self): 1092 """ Row names 1093 1094 :rtype: SexpVector 1095 """ 1096 res = self._rownames(self) 1097 return conversion.rpy2py(res) 1098 1099 def __rownames_set(self, rn): 1100 if isinstance(rn, StrSexpVector): 1101 if len(rn) != self.nrow: 1102 raise ValueError('Invalid length.') 1103 if self.dimnames is NULL: 1104 dn = ListVector.from_length(2) 1105 dn[0] = rn 1106 self.do_slot_assign('dimnames', dn) 1107 else: 1108 dn = self.dimnames 1109 dn[0] = rn 1110 else: 1111 raise ValueError( 1112 'The rownames attribute can only be an R string vector.' 1113 ) 1114 1115 rownames = property(__rownames_get, __rownames_set, None, "Row names") 1116 1117 def __colnames_get(self): 1118 """ Column names 1119 1120 :rtype: SexpVector 1121 """ 1122 res = self._colnames(self) 1123 return conversion.rpy2py(res) 1124 1125 def __colnames_set(self, cn): 1126 if isinstance(cn, StrSexpVector): 1127 if len(cn) != self.ncol: 1128 raise ValueError('Invalid length.') 1129 if self.dimnames is NULL: 1130 dn = ListVector.from_length(2) 1131 dn[1] = cn 1132 self.do_slot_assign('dimnames', dn) 1133 else: 1134 dn = self.dimnames 1135 dn[1] = cn 1136 else: 1137 raise ValueError( 1138 'The colnames attribute can only be an R string vector.' 1139 ) 1140 1141 colnames = property(__colnames_get, __colnames_set, None, "Column names") 1142 1143 def transpose(self): 1144 """ transpose the matrix """ 1145 res = self._transpose(self) 1146 return conversion.rpy2py(res) 1147 1148 def __matmul__(self, x): 1149 """ Matrix multiplication. """ 1150 res = self._matmul(self, conversion.py2rpy(x)) 1151 return conversion.rpy2py(res) 1152 1153 def crossprod(self, m): 1154 """ crossproduct X'.Y""" 1155 res = self._crossprod(self, conversion.rpy2py(m)) 1156 return conversion.rpy2py(res) 1157 1158 def tcrossprod(self, m): 1159 """ crossproduct X.Y'""" 1160 res = self._tcrossprod(self, m) 1161 return conversion.rpy2py(res) 1162 1163 def svd(self, nu=None, nv=None, linpack=False): 1164 """ SVD decomposition. 1165 If nu is None, it is given the default value min(tuple(self.dim)). 1166 If nv is None, it is given the default value min(tuple(self.dim)). 1167 """ 1168 if nu is None: 1169 nu = min(tuple(self.dim)) 1170 if nv is None: 1171 nv = min(tuple(self.dim)) 1172 res = self._svd(self, nu=nu, nv=nv) 1173 return conversion.rpy2py(res) 1174 1175 def dot(self, m): 1176 """ Matrix multiplication """ 1177 res = self._dot(self, m) 1178 return conversion.rpy2py(res) 1179 1180 def eigen(self): 1181 """ Eigen values """ 1182 res = self._eigen(self) 1183 return conversion.rpy2py(res) 1184 1185 1186class DataFrame(ListVector): 1187 """ R 'data.frame'. 1188 """ 1189 _dataframe_name = rinterface.StrSexpVector(('data.frame',)) 1190 _read_csv = utils_ri['read.csv'] 1191 _write_table = utils_ri['write.table'] 1192 _cbind = rinterface.baseenv['cbind.data.frame'] 1193 _rbind = rinterface.baseenv['rbind.data.frame'] 1194 _is_list = rinterface.baseenv['is.list'] 1195 1196 _html_template = jinja2.Template( 1197 """ 1198 <span>R/rpy2 DataFrame ({{ nrows }} x {{ ncolumns }})</span> 1199 <table> 1200 <thead> 1201 <tr> 1202 {% for name in column_names %} 1203 <th>{{ name }}</th> 1204 {% endfor %} 1205 </tr> 1206 </thead> 1207 <tbody> 1208 {% for row_i in rows %} 1209 <tr> 1210 {% for col_i in columns %} 1211 <td> 1212 {{ elements[col_i][row_i] }} 1213 </td> 1214 {% endfor %} 1215 </tr> 1216 {% endfor %} 1217 </tbody> 1218 </table> 1219 """) 1220 1221 def __init__(self, obj, stringsasfactor=False, checknames=False): 1222 """ Create a new data frame. 1223 1224 :param obj: object inheriting from rpy2.rinterface.SexpVector, 1225 or inheriting from TaggedList or a mapping name -> value 1226 :param stringsasfactors: Boolean indicating whether vectors 1227 of strings should be turned to vectors. Note that factors 1228 will not be turned to string vectors. 1229 :param checknames: Boolean indicating whether column names 1230 should be transformed to R syntactically valid names. 1231 """ 1232 if isinstance(obj, rinterface.ListSexpVector): 1233 if obj.typeof != rinterface.RTYPES.VECSXP: 1234 raise ValueError( 1235 "obj should of typeof RTYPES.VECSXP " 1236 " (and we get %s)" % rinterface.RTYPES(obj.typeof) 1237 ) 1238 if ( 1239 self._is_list(obj)[0] or 1240 globalenv_ri.find('inherits')( 1241 obj, self._dataframe_name 1242 )[0] 1243 ): 1244 # TODO: is it really a good idea to pass R lists 1245 # to the constructor ? 1246 super().__init__(obj) 1247 return 1248 else: 1249 raise ValueError( 1250 "When passing R objects to build a DataFrame, " 1251 "the R object must be a list or inherit from " 1252 "the R class 'data.frame'." 1253 ) 1254 elif isinstance(obj, rlc.TaggedList): 1255 kv = [(k, conversion.py2rpy(v)) for k, v in obj.items()] 1256 else: 1257 try: 1258 kv = [(str(k), conversion.py2rpy(v)) for k, v in obj.items()] 1259 except AttributeError: 1260 raise ValueError( 1261 'obj can only be' 1262 'an instance of rpy2.rinterface.ListSexpVector, ' 1263 'an instance of TaggedList, ' 1264 'or an objects with a methods items() that returns ' 1265 '(key, value) pairs ' 1266 '(such a Python dict, rpy2.rlike.container OrdDict).') 1267 1268 # Check if there is a conflicting column name 1269 if 'stringsAsFactors' in (k for k, v in kv): 1270 warnings.warn('The column name "stringsAsFactors" is ' 1271 'conflicting with named parameter ' 1272 'in underlying R function "data.frame()".') 1273 else: 1274 kv.extend((('stringsAsFactors', stringsasfactor), 1275 ('check.names', checknames))) 1276 1277 # Call R's data frame constructor 1278 kv = tuple(kv) 1279 df = baseenv_ri.find("data.frame").rcall(kv, globalenv_ri) 1280 super().__init__(df) 1281 1282 def _repr_html_(self, max_items=7): 1283 names = list() 1284 if len(self) <= max_items: 1285 names.extend(self.names) 1286 else: 1287 half_items = max_items // 2 1288 for i in range(0, half_items): 1289 try: 1290 name = self.names[i] 1291 except TypeError: 1292 name = '[no name]' 1293 names.append(name) 1294 names.append('...') 1295 for i in range(-half_items, 0): 1296 try: 1297 name = self.names[i] 1298 except TypeError: 1299 name = '[no name]' 1300 names.append(name) 1301 1302 elements = list() 1303 for e in self._iter_repr(max_items=max_items): 1304 if hasattr(e, '_repr_html_'): 1305 elements.append(tuple(e._iter_formatted())) 1306 else: 1307 elements.append(['...', ]) 1308 1309 d = {'column_names': names, 1310 'rows': range(len(elements[0]) if len(elements) else 0), 1311 'columns': tuple(range(len(names))), 1312 'nrows': self.nrow, 1313 'ncolumns': self.ncol, 1314 'elements': elements} 1315 html = self._html_template.render(d) 1316 return html 1317 1318 def _get_nrow(self): 1319 """ Number of rows. 1320 :rtype: integer """ 1321 return baseenv_ri["nrow"](self)[0] 1322 nrow = property(_get_nrow, None, None) 1323 1324 def _get_ncol(self): 1325 """ Number of columns. 1326 :rtype: integer """ 1327 return baseenv_ri["ncol"](self)[0] 1328 ncol = property(_get_ncol, None, None) 1329 1330 def _get_rownames(self): 1331 res = baseenv_ri["rownames"](self) 1332 return conversion.rpy2py(res) 1333 1334 def _set_rownames(self, rownames): 1335 res = baseenv_ri["rownames<-"](self, conversion.py2rpy(rownames)) 1336 self.__sexp__ = res.__sexp__ 1337 1338 rownames = property(_get_rownames, _set_rownames, None, 1339 'Row names') 1340 1341 def _get_colnames(self): 1342 res = baseenv_ri["colnames"](self) 1343 return conversion.rpy2py(res) 1344 1345 def _set_colnames(self, colnames): 1346 res = baseenv_ri["colnames<-"](self, conversion.py2rpy(colnames)) 1347 self.__sexp__ = res.__sexp__ 1348 1349 colnames = property(_get_colnames, _set_colnames, None) 1350 1351 def __getitem__(self, i): 1352 # Make sure this is not a List returned 1353 1354 # 3rd-party conversions could return objects 1355 # that no longer inherit from rpy2's R objects. 1356 # We need to use the low-level __getitem__ 1357 # to bypass the conversion mechanism. 1358 # R's data.frames have no representation at the C-API level 1359 # (they are lists) 1360 tmp = rinterface.ListSexpVector.__getitem__(self, i) 1361 1362 if tmp.typeof == rinterface.RTYPES.VECSXP: 1363 return DataFrame(tmp) 1364 else: 1365 return conversion.rpy2py(tmp) 1366 1367 def cbind(self, *args, **kwargs): 1368 """ bind objects as supplementary columns """ 1369 new_args = [self, ] + [conversion.rpy2py(x) for x in args] 1370 new_kwargs = dict( 1371 [(k, conversion.rpy2py(v)) for k, v in kwargs.items()] 1372 ) 1373 res = self._cbind(*new_args, **new_kwargs) 1374 return conversion.rpy2py(res) 1375 1376 def rbind(self, *args, **kwargs): 1377 """ bind objects as supplementary rows """ 1378 new_args = [conversion.rpy2py(x) for x in args] 1379 new_kwargs = dict( 1380 [(k, conversion.rpy2py(v)) for k, v in kwargs.items()] 1381 ) 1382 res = self._rbind(self, *new_args, **new_kwargs) 1383 return conversion.rpy2py(res) 1384 1385 def head(self, *args, **kwargs): 1386 """ Call the R generic 'head()'. """ 1387 res = utils_ri['head'](self, *args, **kwargs) 1388 return conversion.rpy2py(res) 1389 1390 @classmethod 1391 def from_csvfile(cls, path, header=True, sep=',', 1392 quote='"', dec='.', 1393 row_names=rinterface.MissingArg, 1394 col_names=rinterface.MissingArg, 1395 fill=True, comment_char='', 1396 na_strings=[], 1397 as_is=False): 1398 """ Create an instance from data in a .csv file. 1399 1400 :param path: string with a path 1401 :param header: boolean (heading line with column names or not) 1402 :param sep: separator character 1403 :param quote: quote character 1404 :param row_names: column name, or column index for column names 1405 (warning: indexing starts at one in R) 1406 :param fill: boolean (fill the lines when less entries than columns) 1407 :param comment_char: comment character 1408 :param na_strings: a list of strings which are interpreted to be NA 1409 values 1410 :param as_is: boolean (keep the columns of strings as such, or turn 1411 them into factors) 1412 """ 1413 path = conversion.py2rpy(path) 1414 header = conversion.py2rpy(header) 1415 sep = conversion.py2rpy(sep) 1416 quote = conversion.py2rpy(quote) 1417 dec = conversion.py2rpy(dec) 1418 if row_names is not rinterface.MissingArg: 1419 row_names = conversion.py2rpy(row_names) 1420 if col_names is not rinterface.MissingArg: 1421 col_names = conversion.py2rpy(col_names) 1422 fill = conversion.py2rpy(fill) 1423 comment_char = conversion.py2rpy(comment_char) 1424 as_is = conversion.py2rpy(as_is) 1425 na_strings = conversion.py2rpy(na_strings) 1426 res = DataFrame._read_csv(path, 1427 **{'header': header, 'sep': sep, 1428 'quote': quote, 'dec': dec, 1429 'row.names': row_names, 1430 'col.names': col_names, 1431 'fill': fill, 1432 'comment.char': comment_char, 1433 'na.strings': na_strings, 1434 'as.is': as_is}) 1435 return cls(res) 1436 1437 def to_csvfile(self, path, quote=True, sep=',', 1438 eol=os.linesep, na='NA', dec='.', 1439 row_names=True, col_names=True, 1440 qmethod='escape', append=False): 1441 """ Save the data into a .csv file. 1442 1443 :param path : string with a path 1444 :param quote : quote character 1445 :param sep : separator character 1446 :param eol : end-of-line character(s) 1447 :param na : string for missing values 1448 :param dec : string for decimal separator 1449 :param row_names : boolean (save row names, or not) 1450 :param col_names : boolean (save column names, or not) 1451 :param comment_char : method to 'escape' special characters 1452 :param append : boolean (append if the file in the path is 1453 already existing, or not) 1454 """ 1455 path = conversion.py2rpy(path) 1456 append = conversion.py2rpy(append) 1457 sep = conversion.py2rpy(sep) 1458 eol = conversion.py2rpy(eol) 1459 na = conversion.py2rpy(na) 1460 dec = conversion.py2rpy(dec) 1461 row_names = conversion.py2rpy(row_names) 1462 col_names = conversion.py2rpy(col_names) 1463 qmethod = conversion.py2rpy(qmethod) 1464 res = self._write_table( 1465 self, 1466 **{'file': path, 1467 'quote': quote, 'sep': sep, 1468 'eol': eol, 'na': na, 'dec': dec, 1469 'row.names': row_names, 1470 'col.names': col_names, 'qmethod': qmethod, 1471 'append': append}) 1472 return res 1473 1474 def iter_row(self): 1475 """ iterator across rows """ 1476 for i in range(self.nrow): 1477 yield self.rx(i+1, rinterface.MissingArg) 1478 1479 def iter_column(self): 1480 """ iterator across columns """ 1481 for i in range(self.ncol): 1482 yield self.rx(rinterface.MissingArg, i+1) 1483 1484 1485class IntMatrix(Matrix, IntVector): 1486 pass 1487 1488 1489class ByteMatrix(Matrix, ByteVector): 1490 pass 1491 1492 1493class FloatMatrix(Matrix, FloatVector): 1494 pass 1495 1496 1497class BoolMatrix(Matrix, BoolVector): 1498 pass 1499 1500 1501class ComplexMatrix(Matrix, ComplexVector): 1502 pass 1503 1504 1505class StrMatrix(Matrix, StrVector): 1506 pass 1507 1508 1509rtypeof2rotype = { 1510 rinterface.RTYPES.INTSXP: IntVector, 1511 rinterface.RTYPES.REALSXP: FloatVector, 1512 rinterface.RTYPES.STRSXP: StrVector, 1513 rinterface.RTYPES.CPLXSXP: ComplexVector, 1514 rinterface.RTYPES.LGLSXP: BoolVector 1515} 1516 1517 1518__all__ = ['Vector', 'StrVector', 'IntVector', 'BoolVector', 'ComplexVector', 1519 'FloatVector', 'FactorVector', 'Vector', 1520 'ListVector', 'POSIXlt', 'POSIXct', 1521 'Array', 'Matrix', 'DataFrame'] 1522