1
1
from datetime import datetime, date, timedelta
2
2
import operator
3
3
4
+ from cpython cimport (
5
+ PyObject_RichCompareBool,
6
+ Py_EQ, Py_NE,
7
+ )
8
+
4
9
from numpy cimport (int8_t, int32_t, int64_t, import_array, ndarray,
5
10
NPY_INT64, NPY_DATETIME, NPY_TIMEDELTA)
6
11
import numpy as np
@@ -27,6 +32,7 @@ from tslib cimport (
27
32
_is_utc,
28
33
_is_tzlocal,
29
34
_get_dst_info,
35
+ _nat_scalar_rules,
30
36
)
31
37
32
38
from sys import version_info
@@ -606,16 +612,7 @@ cdef ndarray[int64_t] localize_dt64arr_to_period(ndarray[int64_t] stamps,
606
612
return result
607
613
608
614
609
- def _period_field_accessor (name , alias ):
610
- def f (self ):
611
- from pandas.tseries.frequencies import get_freq_code as _gfc
612
- base, mult = _gfc(self .freq)
613
- return get_period_field(alias, self .ordinal, base)
614
- f.__name__ = name
615
- return property(f)
616
-
617
-
618
- class Period (object ):
615
+ cdef class Period(object ):
619
616
"""
620
617
Represents an period of time
621
618
@@ -634,14 +631,17 @@ class Period(object):
634
631
minute : int, default 0
635
632
second : int, default 0
636
633
"""
637
- __slots__ = [' freq' , ' ordinal' ]
634
+ cdef public:
635
+ int64_t ordinal
636
+ object freq
637
+
638
638
_comparables = [' name' ,' freqstr' ]
639
639
_typ = ' period'
640
640
641
641
@classmethod
642
642
def _from_ordinal (cls , ordinal , freq ):
643
643
""" fast creation from an ordinal and freq that are already validated! """
644
- self = object .__new__ (cls )
644
+ self = Period .__new__ (cls )
645
645
self .ordinal = ordinal
646
646
self .freq = freq
647
647
return self
@@ -659,7 +659,6 @@ class Period(object):
659
659
self .freq = None
660
660
661
661
# ordinal is the period offset from the gregorian proleptic epoch
662
- self .ordinal = None
663
662
664
663
if ordinal is not None and value is not None :
665
664
raise ValueError ((" Only value or ordinal but not both should be "
@@ -669,26 +668,25 @@ class Period(object):
669
668
raise ValueError (" Ordinal must be an integer" )
670
669
if freq is None :
671
670
raise ValueError (' Must supply freq for ordinal value' )
672
- self .ordinal = ordinal
673
671
674
672
elif value is None :
675
673
if freq is None :
676
674
raise ValueError (" If value is None, freq cannot be None" )
677
675
678
- self . ordinal = _ordinal_from_fields(year, month, quarter, day,
676
+ ordinal = _ordinal_from_fields(year, month, quarter, day,
679
677
hour, minute, second, freq)
680
678
681
679
elif isinstance (value, Period):
682
680
other = value
683
681
if freq is None or _gfc(freq) == _gfc(other.freq):
684
- self . ordinal = other.ordinal
682
+ ordinal = other.ordinal
685
683
freq = other.freq
686
684
else :
687
685
converted = other.asfreq(freq)
688
- self . ordinal = converted.ordinal
686
+ ordinal = converted.ordinal
689
687
690
688
elif lib.is_null_datetimelike(value) or value in tslib._nat_strings:
691
- self . ordinal = tslib.iNaT
689
+ ordinal = tslib.iNaT
692
690
if freq is None :
693
691
raise ValueError (" If value is NaT, freq cannot be None "
694
692
" because it cannot be inferred" )
@@ -722,26 +720,30 @@ class Period(object):
722
720
# TODO: Better error message - this is slightly confusing
723
721
raise ValueError (' Only mult == 1 supported' )
724
722
725
- if self . ordinal is None :
726
- self .ordinal = period_ordinal (dt.year, dt.month, dt.day,
723
+ if ordinal is None :
724
+ self .ordinal = get_period_ordinal (dt.year, dt.month, dt.day,
727
725
dt.hour, dt.minute, dt.second, dt.microsecond, 0 ,
728
726
base)
727
+ else :
728
+ self .ordinal = ordinal
729
729
730
730
self .freq = frequencies._get_freq_str(base)
731
731
732
- def __eq__ (self , other ):
732
+ def __richcmp__ (self , other , op ):
733
733
if isinstance (other, Period):
734
734
from pandas.tseries.frequencies import get_freq_code as _gfc
735
735
if other.freq != self .freq:
736
736
raise ValueError (" Cannot compare non-conforming periods" )
737
737
if self .ordinal == tslib.iNaT or other.ordinal == tslib.iNaT:
738
- return False
739
- return (self .ordinal == other.ordinal
740
- and _gfc(self .freq) == _gfc(other.freq))
741
- return NotImplemented
742
-
743
- def __ne__ (self , other ):
744
- return not self == other
738
+ return _nat_scalar_rules[op]
739
+ return PyObject_RichCompareBool(self .ordinal, other.ordinal, op)
740
+ else :
741
+ if op == Py_EQ:
742
+ return NotImplemented
743
+ elif op == Py_NE:
744
+ return NotImplemented
745
+ raise TypeError (' Cannot compare type %r with type %r ' %
746
+ (type (self ).__name__, type (other).__name__))
745
747
746
748
def __hash__ (self ):
747
749
return hash ((self .ordinal, self .freq))
@@ -807,25 +809,6 @@ class Period(object):
807
809
else : # pragma: no cover
808
810
return NotImplemented
809
811
810
- def _comp_method (func , name ):
811
- def f (self , other ):
812
- if isinstance (other, Period):
813
- if other.freq != self .freq:
814
- raise ValueError (" Cannot compare non-conforming periods" )
815
- if self .ordinal == tslib.iNaT or other.ordinal == tslib.iNaT:
816
- return False
817
- return func(self .ordinal, other.ordinal)
818
- else :
819
- raise TypeError (other)
820
-
821
- f.__name__ = name
822
- return f
823
-
824
- __lt__ = _comp_method(operator.lt, ' __lt__' )
825
- __le__ = _comp_method(operator.le, ' __le__' )
826
- __gt__ = _comp_method(operator.gt, ' __gt__' )
827
- __ge__ = _comp_method(operator.ge, ' __ge__' )
828
-
829
812
def asfreq (self , freq , how = ' E' ):
830
813
"""
831
814
Convert Period to desired frequency, either at the start or end of the
@@ -898,19 +881,50 @@ class Period(object):
898
881
dt64 = period_ordinal_to_dt64(val.ordinal, base)
899
882
return Timestamp(dt64, tz = tz)
900
883
901
- year = _period_field_accessor(' year' , 0 )
902
- month = _period_field_accessor(' month' , 3 )
903
- day = _period_field_accessor(' day' , 4 )
904
- hour = _period_field_accessor(' hour' , 5 )
905
- minute = _period_field_accessor(' minute' , 6 )
906
- second = _period_field_accessor(' second' , 7 )
907
- weekofyear = _period_field_accessor(' week' , 8 )
908
- week = weekofyear
909
- dayofweek = _period_field_accessor(' dayofweek' , 10 )
910
- weekday = dayofweek
911
- dayofyear = _period_field_accessor(' dayofyear' , 9 )
912
- quarter = _period_field_accessor(' quarter' , 2 )
913
- qyear = _period_field_accessor(' qyear' , 1 )
884
+ cdef _field(self , alias):
885
+ from pandas.tseries.frequencies import get_freq_code as _gfc
886
+ base, mult = _gfc(self .freq)
887
+ return get_period_field(alias, self .ordinal, base)
888
+
889
+ property year :
890
+ def __get__ (self ):
891
+ return self ._field(0 )
892
+ property month :
893
+ def __get__ (self ):
894
+ return self ._field(3 )
895
+ property day :
896
+ def __get__ (self ):
897
+ return self ._field(4 )
898
+ property hour :
899
+ def __get__ (self ):
900
+ return self ._field(5 )
901
+ property minute :
902
+ def __get__ (self ):
903
+ return self ._field(6 )
904
+ property second :
905
+ def __get__ (self ):
906
+ return self ._field(7 )
907
+ property weekofyear :
908
+ def __get__ (self ):
909
+ return self ._field(8 )
910
+ property week :
911
+ def __get__ (self ):
912
+ return self .weekofyear
913
+ property dayofweek :
914
+ def __get__ (self ):
915
+ return self ._field(10 )
916
+ property weekday :
917
+ def __get__ (self ):
918
+ return self .dayofweek
919
+ property dayofyear :
920
+ def __get__ (self ):
921
+ return self ._field(9 )
922
+ property quarter :
923
+ def __get__ (self ):
924
+ return self ._field(2 )
925
+ property qyear :
926
+ def __get__ (self ):
927
+ return self ._field(1 )
914
928
915
929
@classmethod
916
930
def now (cls , freq = None ):
@@ -1094,7 +1108,7 @@ def _ordinal_from_fields(year, month, quarter, day, hour, minute,
1094
1108
if quarter is not None :
1095
1109
year, month = _quarter_to_myear(year, quarter, freq)
1096
1110
1097
- return period_ordinal (year, month, day, hour, minute, second, 0 , 0 , base)
1111
+ return get_period_ordinal (year, month, day, hour, minute, second, 0 , 0 , base)
1098
1112
1099
1113
1100
1114
def _quarter_to_myear (year , quarter , freq ):
0 commit comments