Skip to content

Commit b28f8b4

Browse files
committedDec 4, 2012
added boolean comparison methods to panel
1 parent 91003d6 commit b28f8b4

File tree

3 files changed

+163
-7
lines changed

3 files changed

+163
-7
lines changed
 

‎pandas/core/panel.py

Lines changed: 89 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ def f(self, other):
104104

105105
def _panel_arith_method(op, name):
106106
@Substitution(op)
107-
def f(self, other, axis='items'):
107+
def f(self, other, axis = 0):
108108
"""
109109
Wrapper method for %s
110110
@@ -123,6 +123,42 @@ def f(self, other, axis='items'):
123123
f.__name__ = name
124124
return f
125125

126+
def _comp_method(func, name):
127+
128+
def na_op(x, y):
129+
try:
130+
result = func(x, y)
131+
except TypeError:
132+
xrav = x.ravel()
133+
result = np.empty(x.size, dtype=x.dtype)
134+
if isinstance(y, np.ndarray):
135+
yrav = y.ravel()
136+
mask = notnull(xrav) & notnull(yrav)
137+
result[mask] = func(np.array(list(xrav[mask])),
138+
np.array(list(yrav[mask])))
139+
else:
140+
mask = notnull(xrav)
141+
result[mask] = func(np.array(list(xrav[mask])), y)
142+
143+
if func == operator.ne: # pragma: no cover
144+
np.putmask(result, -mask, True)
145+
else:
146+
np.putmask(result, -mask, False)
147+
result = result.reshape(x.shape)
148+
149+
return result
150+
151+
@Appender('Wrapper for comparison method %s' % name)
152+
def f(self, other):
153+
if isinstance(other, self._constructor):
154+
return self._compare_constructor(other, func)
155+
else:
156+
return self._combine_const(other, na_op)
157+
158+
159+
f.__name__ = name
160+
161+
return f
126162

127163
_agg_doc = """
128164
Return %(desc)s over requested axis
@@ -400,6 +436,49 @@ def __array_wrap__(self, result):
400436
d['copy'] = False
401437
return self._constructor(result, **d)
402438

439+
#----------------------------------------------------------------------
440+
# Comparison methods
441+
442+
def _indexed_same(self, other):
443+
return all([ getattr(self,a).equals(getattr(other,a)) for a in self._AXIS_ORDERS ])
444+
445+
def _compare_constructor(self, other, func):
446+
new_data = {}
447+
for col in getattr(self,self._info_axis):
448+
new_data[col] = func(self[col], other[col])
449+
450+
d = self._construct_axes_dict()
451+
d['copy'] = False
452+
return self._constructor(data=new_data, **d)
453+
454+
# boolean operators
455+
__and__ = _arith_method(operator.and_, '__and__')
456+
__or__ = _arith_method(operator.or_, '__or__')
457+
__xor__ = _arith_method(operator.xor, '__xor__')
458+
459+
def __neg__(self):
460+
arr = operator.neg(self.values)
461+
return self._wrap_array(arr, self.axes, copy=False)
462+
463+
def __invert__(self):
464+
arr = operator.inv(self.values)
465+
return self._wrap_array(arr, self.axes, copy=False)
466+
467+
# Comparison methods
468+
__eq__ = _comp_method(operator.eq, '__eq__')
469+
__ne__ = _comp_method(operator.ne, '__ne__')
470+
__lt__ = _comp_method(operator.lt, '__lt__')
471+
__gt__ = _comp_method(operator.gt, '__gt__')
472+
__le__ = _comp_method(operator.le, '__le__')
473+
__ge__ = _comp_method(operator.ge, '__ge__')
474+
475+
eq = _comp_method(operator.eq, 'eq')
476+
ne = _comp_method(operator.ne, 'ne')
477+
gt = _comp_method(operator.gt, 'gt')
478+
lt = _comp_method(operator.lt, 'lt')
479+
ge = _comp_method(operator.ge, 'ge')
480+
le = _comp_method(operator.le, 'le')
481+
403482
#----------------------------------------------------------------------
404483
# Magic methods
405484

@@ -741,7 +820,10 @@ def reindex(self, major=None, minor=None, method=None,
741820
if (method is None and not self._is_mixed_type and al <= 3):
742821
items = kwargs.get('items')
743822
if com._count_not_none(items, major, minor) == 3:
744-
return self._reindex_multi(items, major, minor)
823+
try:
824+
return self._reindex_multi(items, major, minor)
825+
except:
826+
pass
745827

746828
if major is not None:
747829
result = result._reindex_axis(major, method, al-2, copy)
@@ -873,12 +955,12 @@ def _combine(self, other, func, axis=0):
873955
elif isinstance(other, DataFrame):
874956
return self._combine_frame(other, func, axis=axis)
875957
elif np.isscalar(other):
876-
new_values = func(self.values, other)
877-
d = self._construct_axes_dict()
878-
return self._constructor(new_values, **d)
958+
return self._combine_const(other, func)
879959

880-
def __neg__(self):
881-
return -1 * self
960+
def _combine_const(self, other, func):
961+
new_values = func(self.values, other)
962+
d = self._construct_axes_dict()
963+
return self._constructor(new_values, **d)
882964

883965
def _combine_frame(self, other, func, axis=0):
884966
index, columns = self._get_plane_axes(axis)

‎pandas/tests/test_panel.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -642,6 +642,44 @@ def _check_view(self, indexer, comp):
642642
self.assert_((obj.values == 0).all())
643643
comp(cp.ix[indexer].reindex_like(obj), obj)
644644

645+
def test_logical_with_nas(self):
646+
d = Panel({ 'ItemA' : {'a': [np.nan, False] }, 'ItemB' : { 'a': [True, True] } })
647+
648+
result = d['ItemA'] | d['ItemB']
649+
expected = DataFrame({ 'a' : [np.nan, True] })
650+
assert_frame_equal(result, expected)
651+
652+
result = d['ItemA'].fillna(False) | d['ItemB']
653+
expected = DataFrame({ 'a' : [True, True] }, dtype=object)
654+
assert_frame_equal(result, expected)
655+
656+
def test_neg(self):
657+
# what to do?
658+
assert_panel_equal(-self.panel, -1 * self.panel)
659+
660+
def test_invert(self):
661+
assert_panel_equal(-(self.panel < 0), ~(self.panel <0))
662+
663+
def test_comparisons(self):
664+
p1 = tm.makePanel()
665+
p2 = tm.makePanel()
666+
667+
def test_comp(func):
668+
result = func(p1, p2)
669+
self.assert_(np.array_equal(result.values,
670+
func(p1.values, p2.values)))
671+
672+
result3 = func(self.panel, 0)
673+
self.assert_(np.array_equal(result3.values,
674+
func(self.panel.values, 0)))
675+
676+
test_comp(operator.eq)
677+
test_comp(operator.ne)
678+
test_comp(operator.lt)
679+
test_comp(operator.gt)
680+
test_comp(operator.ge)
681+
test_comp(operator.le)
682+
645683
def test_get_value(self):
646684
for item in self.panel.items:
647685
for mjr in self.panel.major_axis[::2]:

‎pandas/tests/test_panel4d.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,42 @@ def test_setitem(self):
368368
self.panel4d['lP'] = self.panel4d['l1'] > 0
369369
self.assert_(self.panel4d['lP'].values.dtype == np.bool_)
370370

371+
def test_comparisons(self):
372+
p1 = tm.makePanel4D()
373+
p2 = tm.makePanel4D()
374+
375+
def test_comp(func):
376+
result = func(p1, p2)
377+
self.assert_(np.array_equal(result.values,
378+
func(p1.values, p2.values)))
379+
380+
result3 = func(self.panel4d, 0)
381+
self.assert_(np.array_equal(result3.values,
382+
func(self.panel4d.values, 0)))
383+
384+
test_comp(operator.eq)
385+
test_comp(operator.ne)
386+
test_comp(operator.lt)
387+
test_comp(operator.gt)
388+
test_comp(operator.ge)
389+
test_comp(operator.le)
390+
391+
def test_setitem_ndarray(self):
392+
raise nose.SkipTest
393+
# from pandas import DateRange, datetools
394+
395+
# timeidx = DateRange(start=datetime(2009,1,1),
396+
# end=datetime(2009,12,31),
397+
# offset=datetools.MonthEnd())
398+
# lons_coarse = np.linspace(-177.5, 177.5, 72)
399+
# lats_coarse = np.linspace(-87.5, 87.5, 36)
400+
# P = Panel(items=timeidx, major_axis=lons_coarse, minor_axis=lats_coarse)
401+
# data = np.random.randn(72*36).reshape((72,36))
402+
# key = datetime(2009,2,28)
403+
# P[key] = data#
404+
405+
# assert_almost_equal(P[key].values, data)
406+
371407
def test_major_xs(self):
372408
ref = self.panel4d['l1']['ItemA']
373409

0 commit comments

Comments
 (0)
Please sign in to comment.