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