Skip to content

Commit 32b3732

Browse files
committed
added boolean comparison methods to panel
1 parent 87e00ac commit 32b3732

File tree

3 files changed

+147
-7
lines changed

3 files changed

+147
-7
lines changed

pandas/core/panel.py

+89-7
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

+38
Original file line numberDiff line numberDiff line change
@@ -635,6 +635,44 @@ def _check_view(self, indexer, comp):
635635
self.assert_((obj.values == 0).all())
636636
comp(cp.ix[indexer].reindex_like(obj), obj)
637637

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

pandas/tests/test_panel4d.py

+20
Original file line numberDiff line numberDiff line change
@@ -396,6 +396,26 @@ def test_setitem(self):
396396
self.panel4d['lP'] = self.panel4d['l1'] > 0
397397
self.assert_(self.panel4d['lP'].values.dtype == np.bool_)
398398

399+
def test_comparisons(self):
400+
p1 = tm.makePanel4D()
401+
p2 = tm.makePanel4D()
402+
403+
def test_comp(func):
404+
result = func(p1, p2)
405+
self.assert_(np.array_equal(result.values,
406+
func(p1.values, p2.values)))
407+
408+
result3 = func(self.panel4d, 0)
409+
self.assert_(np.array_equal(result3.values,
410+
func(self.panel4d.values, 0)))
411+
412+
test_comp(operator.eq)
413+
test_comp(operator.ne)
414+
test_comp(operator.lt)
415+
test_comp(operator.gt)
416+
test_comp(operator.ge)
417+
test_comp(operator.le)
418+
399419
def test_setitem_ndarray(self):
400420
raise nose.SkipTest
401421
# from pandas import DateRange, datetools

0 commit comments

Comments
 (0)