Skip to content

Commit d8626fb

Browse files
committed
BUG: test coverage and misc bug fixes, cruft deletion in period.py #1245
1 parent 54c0a1a commit d8626fb

File tree

3 files changed

+160
-90
lines changed

3 files changed

+160
-90
lines changed

pandas/tseries/period.py

+42-85
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
from pandas.lib import Timestamp
1515
import pandas.lib as lib
1616
import pandas._period as plib
17+
import pandas._algos as _algos
1718

1819

1920
#---------------
@@ -157,7 +158,8 @@ def __hash__(self):
157158
def __add__(self, other):
158159
if com.is_integer(other):
159160
return Period(ordinal=self.ordinal + other, freq=self.freq)
160-
raise ValueError("Cannot add with non-integer value")
161+
else: # pragma: no cover
162+
raise TypeError(other)
161163

162164
def __sub__(self, other):
163165
if com.is_integer(other):
@@ -167,7 +169,8 @@ def __sub__(self, other):
167169
raise ValueError("Cannot do arithmetic with "
168170
"non-conforming periods")
169171
return self.ordinal - other.ordinal
170-
raise ValueError("Cannot sub with non-integer value")
172+
else: # pragma: no cover
173+
raise TypeError(other)
171174

172175
def asfreq(self, freq=None, how='E'):
173176
"""
@@ -188,9 +191,6 @@ def asfreq(self, freq=None, how='E'):
188191
if mult2 != 1:
189192
raise ValueError('Only mult == 1 supported')
190193

191-
if how not in ('S', 'E'):
192-
raise ValueError('relation argument must be one of S or E')
193-
194194
end = how == 'E'
195195
new_ordinal = plib.period_asfreq(self.ordinal, base1, base2, end)
196196

@@ -441,18 +441,10 @@ def _period_unbox(key, check=None):
441441
return np.int64(key.ordinal)
442442

443443
def _period_unbox_array(arr, check=None):
444-
if arr is None:
445-
return arr
446444
unboxer = np.frompyfunc(lambda x: _period_unbox(x, check=check), 1, 1)
447445
return unboxer(arr)
448446

449447
def _period_box_array(arr, freq):
450-
if arr is None:
451-
return arr
452-
453-
if not isinstance(arr, np.ndarray):
454-
return arr
455-
456448
boxfunc = lambda x: Period(ordinal=x, freq=freq)
457449
boxer = np.frompyfunc(boxfunc, 1, 1)
458450
return boxer(arr)
@@ -461,14 +453,7 @@ def dt64arr_to_periodarr(data, freq):
461453
if data.dtype != np.dtype('M8[ns]'):
462454
raise ValueError('Wrong dtype: %s' % data.dtype)
463455

464-
if data is None:
465-
return data
466-
467-
if isinstance(freq, basestring):
468-
base, mult = _gfc(freq)
469-
else:
470-
base, mult = freq
471-
456+
base, mult = _gfc(freq)
472457
return plib.dt64arr_to_periodarr(data.view('i8'), base)
473458

474459
# --- Period index sketch
@@ -490,12 +475,11 @@ def wrapper(self, other):
490475
other = Period(other, freq=self.freq)
491476
func = getattr(self.values, opname)
492477
result = func(other.ordinal)
493-
try:
494-
return result.view(np.ndarray)
495-
except:
496-
return result
478+
479+
return result
497480
return wrapper
498481

482+
_INT64_DTYPE = np.dtype(np.int64)
499483

500484
class PeriodIndex(Int64Index):
501485
"""
@@ -560,10 +544,7 @@ def __new__(cls, data=None, ordinal=None,
560544
year=None, month=None, quarter=None, day=None,
561545
hour=None, minute=None, second=None):
562546

563-
if isinstance(freq, Period):
564-
freq = freq.freq
565-
else:
566-
freq = _freq_mod.get_standard_freq(freq)
547+
freq = _freq_mod.get_standard_freq(freq)
567548

568549
if periods is not None:
569550
if com.is_float(periods):
@@ -598,9 +579,9 @@ def _generate_range(cls, start, end, periods, freq, fields):
598579
'or endpoints, but not both')
599580
subarr, freq = _get_ordinal_range(start, end, periods, freq)
600581
elif field_count > 0:
601-
y, m, q, d, h, m, s = fields
602-
subarr, freq = _range_from_fields(year=y, month=m, quarter=q,
603-
day=d, hour=h, minute=m,
582+
y, mth, q, d, h, minute, s = fields
583+
subarr, freq = _range_from_fields(year=y, month=mth, quarter=q,
584+
day=d, hour=h, minute=minute,
604585
second=s, freq=freq)
605586
else:
606587
raise ValueError('Not enough parameters to construct '
@@ -611,15 +592,11 @@ def _generate_range(cls, start, end, periods, freq, fields):
611592
@classmethod
612593
def _from_arraylike(cls, data, freq):
613594
if not isinstance(data, np.ndarray):
614-
if np.isscalar(data):
595+
if np.isscalar(data) or isinstance(data, Period):
615596
raise ValueError('PeriodIndex() must be called with a '
616597
'collection of some kind, %s was passed'
617598
% repr(data))
618599

619-
elif isinstance(data, Period):
620-
raise ValueError('Data must be array of dates, strings, '
621-
'or Period objects')
622-
623600
# other iterable of some kind
624601
if not isinstance(data, (list, tuple)):
625602
data = list(data)
@@ -648,7 +625,7 @@ def _from_arraylike(cls, data, freq):
648625
data = plib.period_asfreq_arr(data.values, base1, base2, 1)
649626
else:
650627
if freq is None and len(data) > 0:
651-
freq = getattr(data[0], 'freq')
628+
freq = getattr(data[0], 'freq', None)
652629

653630
if freq is None:
654631
raise ValueError(('freq not specified and cannot be '
@@ -684,7 +661,10 @@ def astype(self, dtype):
684661
result = np.empty(len(self), dtype=dtype)
685662
result[:] = [x for x in self]
686663
return result
687-
return np.ndarray.astype(self.values, dtype)
664+
elif dtype == _INT64_DTYPE:
665+
return self.values.copy()
666+
else: # pragma: no cover
667+
raise ValueError('Cannot cast PeriodIndex to dtype %s' % dtype)
688668

689669
def __iter__(self):
690670
for val in self.values:
@@ -717,18 +697,11 @@ def asfreq(self, freq=None, how='E'):
717697
freq = _freq_mod.get_standard_freq(freq)
718698

719699
base1, mult1 = _gfc(self.freq)
720-
721-
if isinstance(freq, basestring):
722-
base2, mult2 = _gfc(freq)
723-
else:
724-
base2, mult2 = freq
700+
base2, mult2 = _gfc(freq)
725701

726702
if mult2 != 1:
727703
raise ValueError('Only mult == 1 supported')
728704

729-
if how not in ('S', 'E'):
730-
raise ValueError('relation argument must be one of S or E')
731-
732705
end = how == 'E'
733706
new_data = plib.period_asfreq_arr(self.values, base1, base2, end)
734707

@@ -753,11 +726,12 @@ def asfreq(self, freq=None, how='E'):
753726

754727
# Try to run function on index first, and then on elements of index
755728
# Especially important for group-by functionality
756-
def map(self, func_to_map):
729+
def map(self, f):
757730
try:
758-
return func_to_map(self)
731+
return f(self)
759732
except:
760-
return super(PeriodIndex, self).map(func_to_map)
733+
values = np.asarray(list(self), dtype=object)
734+
return _algos.arrmap_object(values, f)
761735

762736
def _mpl_repr(self):
763737
# how to represent ourselves to matplotlib
@@ -810,19 +784,10 @@ def shift(self, n):
810784
return PeriodIndex(data=self.values + n, freq=self.freq)
811785

812786
def __add__(self, other):
813-
if com.is_integer(other):
814-
return PeriodIndex(ordinal=self.values + other, freq=self.freq)
815-
return super(PeriodIndex, self).__add__(other)
787+
return PeriodIndex(ordinal=self.values + other, freq=self.freq)
816788

817789
def __sub__(self, other):
818-
if com.is_integer(other):
819-
return PeriodIndex(ordinal=self.values - other, freq=self.freq)
820-
if isinstance(other, Period):
821-
if other.freq != self.freq:
822-
raise ValueError("Cannot do arithmetic with "
823-
"non-conforming periods")
824-
return PeriodIndex(self.values - other.ordinal)
825-
return super(PeriodIndex, self).__sub__(other)
790+
return PeriodIndex(ordinal=self.values - other, freq=self.freq)
826791

827792
@property
828793
def inferred_type(self):
@@ -843,11 +808,17 @@ def get_value(self, series, key):
843808
grp = _freq_mod._infer_period_group(reso)
844809
freqn = _freq_mod._period_group(self.freq)
845810

811+
vals = self.values
812+
846813
# if our data is higher resolution than requested key, slice
847814
if grp < freqn:
848815
iv = Period(asdt, freq=(grp,1))
849816
ord1 = iv.asfreq(self.freq, how='S').ordinal
850817
ord2 = iv.asfreq(self.freq, how='E').ordinal
818+
819+
if ord2 < vals[0] or ord1 > vals[-1]:
820+
raise KeyError(key)
821+
851822
pos = np.searchsorted(self.values, [ord1, ord2])
852823
key = slice(pos[0], pos[1]+1)
853824
return series[key]
@@ -858,9 +829,6 @@ def get_value(self, series, key):
858829
pass
859830
except KeyError:
860831
pass
861-
except IndexError:
862-
ival = Period(key, freq=self.freq)
863-
raise IndexError("%s is out of bounds" % ival)
864832

865833
key = to_period(key, self.freq)
866834
return self._engine.get_value(series, key.ordinal)
@@ -881,8 +849,6 @@ def get_loc(self, key):
881849
key = asdt
882850
except TypeError:
883851
pass
884-
except KeyError:
885-
pass
886852

887853
key = to_period(key, self.freq).ordinal
888854
return self._engine.get_loc(key)
@@ -904,7 +870,7 @@ def join(self, other, how='left', level=None, return_indexers=False):
904870

905871
def _assert_can_do_setop(self, other):
906872
if not isinstance(other, PeriodIndex):
907-
raise TypeError('can only call with other PeriodIndex-ed objects')
873+
raise ValueError('can only call with other PeriodIndex-ed objects')
908874

909875
if self.freq != other.freq:
910876
raise ValueError('Only like-indexed PeriodIndexes compatible '
@@ -933,7 +899,10 @@ def __getitem__(self, key):
933899

934900
result = arr_idx[key]
935901
if result.ndim > 1:
936-
return PeriodIndex(result, name=self.name, freq=self.freq)
902+
values = PeriodIndex(result.squeeze(), name=self.name,
903+
freq=self.freq)
904+
values = np.asarray(list(values), dtype=object)
905+
return values.reshape(result.shape)
937906

938907
return PeriodIndex(result, name=self.name, freq=self.freq)
939908

@@ -948,12 +917,6 @@ def format(self, name=False):
948917

949918
return header + ['%s' % Period(x, freq=self.freq) for x in self]
950919

951-
def _view_like(self, ndarray):
952-
result = ndarray.view(type(self))
953-
result.freq = self.freq
954-
result.name = self.name
955-
return result
956-
957920
def __array_finalize__(self, obj):
958921
if self.ndim == 0: # pragma: no cover
959922
return self.item()
@@ -988,11 +951,6 @@ def _get_ordinal_range(start, end, periods, freq):
988951

989952
is_start_per = isinstance(start, Period)
990953
is_end_per = isinstance(end, Period)
991-
if (start is not None and not is_start_per):
992-
raise ValueError('Failed to convert %s to period' % start)
993-
994-
if (end is not None and not is_end_per):
995-
raise ValueError('Failed to convert %s to period' % end)
996954

997955
if is_start_per and is_end_per and (start.freq != end.freq):
998956
raise ValueError('Start and end must have same freq')
@@ -1002,7 +960,7 @@ def _get_ordinal_range(start, end, periods, freq):
1002960
freq = start.freq
1003961
elif is_end_per:
1004962
freq = end.freq
1005-
else:
963+
else: # pragma: no cover
1006964
raise ValueError('Could not infer freq from start/end')
1007965

1008966
if periods is not None:
@@ -1014,9 +972,6 @@ def _get_ordinal_range(start, end, periods, freq):
1014972
data = np.arange(start.ordinal, start.ordinal + periods,
1015973
dtype=np.int64)
1016974
else:
1017-
if start is None or end is None:
1018-
msg = 'Must specify both start and end if periods is None'
1019-
raise ValueError(msg)
1020975
data = np.arange(start.ordinal, end.ordinal+1, dtype=np.int64)
1021976

1022977
return data, freq
@@ -1055,8 +1010,8 @@ def _range_from_fields(year=None, month=None, quarter=None, day=None,
10551010
raise ValueError('Only mult == 1 supported')
10561011

10571012
arrays = _make_field_arrays(year, month, day, hour, minute, second)
1058-
for y, m, d, h, m, s in zip(*arrays):
1059-
ordinals.append(plib.period_ordinal(y, m, d, h, m, s, base))
1013+
for y, mth, d, h, mn, s in zip(*arrays):
1014+
ordinals.append(plib.period_ordinal(y, mth, d, h, mn, s, base))
10601015

10611016
return np.array(ordinals, dtype=np.int64), freq
10621017

@@ -1066,6 +1021,8 @@ def _make_field_arrays(*fields):
10661021
if isinstance(x, (list, np.ndarray)):
10671022
if length is not None and len(x) != length:
10681023
raise ValueError('Mismatched Period array lengths')
1024+
elif length is None:
1025+
length = len(x)
10691026

10701027
arrays = [np.asarray(x) if isinstance(x, (np.ndarray, list))
10711028
else np.repeat(x, length) for x in fields]

0 commit comments

Comments
 (0)