Skip to content

Commit b368802

Browse files
committed
ENH add ff filter method to DataFrame
1 parent 8975170 commit b368802

File tree

2 files changed

+86
-0
lines changed

2 files changed

+86
-0
lines changed

pandas/core/frame.py

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3786,6 +3786,77 @@ def tail(self, n=5):
37863786
"""
37873787
return self[-n:]
37883788

3789+
def ff(self, filter=None, how='all', complement=False):
3790+
"""
3791+
Restrict frame's rows to only those which satisfy
3792+
for col_name, col_value in filter.items():
3793+
row[col_name] == col_value or col_value(row[col_name])
3794+
3795+
Parameters
3796+
----------
3797+
filter : dictionary
3798+
Keys are column names and values are criteria,
3799+
where critria is either a value (check equals) or a function
3800+
how : string ('all' or 'any')
3801+
If 'all' then returns those rows which satisfy every criteria
3802+
otherwise returns those rows which satisfy at least one criteria
3803+
complement : boolean
3804+
If True returns the rows which don't satisfy the criteria
3805+
3806+
Notes
3807+
-----
3808+
df.ff({'a': 1, 'b': lambda x: 1 < x < 2})
3809+
is equivalent to
3810+
df[(df.a == 1) & (1 < df.b) & (df.b < 2)]
3811+
3812+
Always returns a copy
3813+
3814+
Examples
3815+
--------
3816+
>>> df = DataFrame([['A', 1], ['A', 2], ['A', 3],
3817+
['B', 1], ['B', 2]], columns=['x', 'y'])
3818+
>>> df
3819+
x y
3820+
0 A 1
3821+
1 A 2
3822+
2 A 3
3823+
3 B 1
3824+
4 B 2
3825+
>>> df.ff({'x': 'A'})
3826+
x y
3827+
0 A 1
3828+
1 A 2
3829+
2 A 3
3830+
>>> df.ff({'y': lambda x: x == 2 or x == 3})
3831+
x y
3832+
1 A 2
3833+
2 A 3
3834+
4 B 2
3835+
>>> df.ff({'y': lambda x: x == 2 or x == 3}, complement=True)
3836+
x y
3837+
0 A 1
3838+
3 B 1
3839+
3840+
Returns
3841+
-------
3842+
DataFrame with filtered rows
3843+
"""
3844+
if not filter:
3845+
return self
3846+
3847+
mask = True if how == 'all' else False
3848+
op = np.bitwise_and if how == 'all' else np.bitwise_or
3849+
for col_name, col_value in filter.iteritems():
3850+
if hasattr(col_value, '__call__'): # test it's a function
3851+
mask = op(mask, self[col_name].apply(col_value))
3852+
else:
3853+
mask = op(mask, self[col_name] == col_value)
3854+
3855+
if complement:
3856+
return self[~mask]
3857+
else:
3858+
return self[mask]
3859+
37893860
#----------------------------------------------------------------------
37903861
# Data reshaping
37913862

pandas/tests/test_frame.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3838,6 +3838,21 @@ def test_head_tail(self):
38383838
assert_frame_equal(self.frame.head(), self.frame[:5])
38393839
assert_frame_equal(self.frame.tail(), self.frame[-5:])
38403840

3841+
def test_ff(self):
3842+
df = DataFrame([[1,2],[3,4]], columns=['a', 'b'])
3843+
df0 = DataFrame([[1,2]], columns=['a', 'b'])
3844+
df1 = DataFrame([[3,4]], columns=['a', 'b'], index=[1])
3845+
dfe = DataFrame(columns=['a', 'b'])
3846+
assert_frame_equal(df.ff({'a': 1}), df0)
3847+
assert_frame_equal(df.ff({'a': 1}, how='all'), df0)
3848+
assert_frame_equal(df.ff({'a': 1, 'b': 2}), df0)
3849+
assert_frame_equal(df.ff({'a': 1, 'b': 4}, how='any'), df)
3850+
assert_frame_equal(df.ff({'a': 1, 'b': 4}, how='all'), dfe, check_dtype=False)
3851+
3852+
assert_frame_equal(df.ff({'a': lambda x: x == 1}), df0)
3853+
assert_frame_equal(df.ff({'a': lambda x: 0 < x < 2}), df0)
3854+
assert_frame_equal(df.ff({'a': lambda x: x == 1, 'b': 2}), df0)
3855+
38413856
def test_insert(self):
38423857
df = DataFrame(np.random.randn(5, 3), index=np.arange(5),
38433858
columns=['c', 'b', 'a'])

0 commit comments

Comments
 (0)