diff --git a/pandas/core/frame.py b/pandas/core/frame.py index bb0e4512ea267..6ed54ab67026d 100644 --- a/pandas/core/frame.py +++ b/pandas/core/frame.py @@ -3041,7 +3041,7 @@ def reorder_levels(self, order, axis=0): #---------------------------------------------------------------------- # Filling NA's - def fillna(self, value=None, method='pad', axis=0, inplace=False, + def fillna(self, value=None, method=None, axis=0, inplace=False, limit=None): """ Fill NA/NaN values using the specified method @@ -3078,7 +3078,11 @@ def fillna(self, value=None, method='pad', axis=0, inplace=False, self._consolidate_inplace() if value is None: + if method is None: + raise ValueError('must specify a fill method or value') if self._is_mixed_type and axis == 1: + if inplace: + raise NotImplementedError() return self.T.fillna(method=method, limit=limit).T new_blocks = [] @@ -3093,6 +3097,8 @@ def fillna(self, value=None, method='pad', axis=0, inplace=False, new_data = BlockManager(new_blocks, self._data.axes) else: + if method is not None: + raise ValueError('cannot specify both a fill method and value') # Float type values if len(self.columns) == 0: return self @@ -3117,6 +3123,14 @@ def fillna(self, value=None, method='pad', axis=0, inplace=False, else: return self._constructor(new_data) + def ffill(self, axis=0, inplace=False, limit=None): + return self.fillna(method='ffill', axis=axis, inplace=inplace, + limit=limit) + + def bfill(self, axis=0, inplace=False, limit=None): + return self.fillna(method='bfill', axis=axis, inplace=inplace, + limit=limit) + def replace(self, to_replace, value=None, method='pad', axis=0, inplace=False, limit=None): """ diff --git a/pandas/core/panel.py b/pandas/core/panel.py index 42adf0420db0d..fb47b7aa2f102 100644 --- a/pandas/core/panel.py +++ b/pandas/core/panel.py @@ -863,7 +863,7 @@ def _combine_panel(self, other, func): return self._constructor(result_values, items, major, minor) - def fillna(self, value=None, method='pad'): + def fillna(self, value=None, method=None): """ Fill NaN values using the specified method. @@ -889,15 +889,27 @@ def fillna(self, value=None, method='pad'): DataFrame.reindex, DataFrame.asfreq """ if value is None: + if method is None: + raise ValueError('must specify a fill method or value') result = {} for col, s in self.iterkv(): result[col] = s.fillna(method=method, value=value) return self._constructor.from_dict(result) else: + if method is not None: + raise ValueError('cannot specify both a fill method and value') new_data = self._data.fillna(value) return self._constructor(new_data) + + def ffill(self): + return self.fillna(method='ffill') + + def bfill(self): + return self.fillna(method='bfill') + + add = _panel_arith_method(operator.add, 'add') subtract = sub = _panel_arith_method(operator.sub, 'subtract') multiply = mul = _panel_arith_method(operator.mul, 'multiply') diff --git a/pandas/core/series.py b/pandas/core/series.py index 140a2f1f22a05..f89bd0ed3275e 100644 --- a/pandas/core/series.py +++ b/pandas/core/series.py @@ -2217,7 +2217,7 @@ def take(self, indices, axis=0): truncate = generic.truncate - def fillna(self, value=None, method='pad', inplace=False, + def fillna(self, value=None, method=None, inplace=False, limit=None): """ Fill NA/NaN values using the specified method @@ -2249,12 +2249,14 @@ def fillna(self, value=None, method='pad', inplace=False, return self.copy() if not inplace else self if value is not None: + if method is not None: + raise ValueError('Cannot specify both a fill value and method') result = self.copy() if not inplace else self mask = isnull(self.values) np.putmask(result, mask, value) else: if method is None: # pragma: no cover - raise ValueError('must specify a fill method') + raise ValueError('must specify a fill method or value') fill_f = _get_fill_func(method) @@ -2272,6 +2274,12 @@ def fillna(self, value=None, method='pad', inplace=False, return result + def ffill(self, inplace=False, limit=None): + return self.fillna(method='ffill', inplace=inplace, limit=limit) + + def bfill(self, inplace=False, limit=None): + return self.fillna(method='bfill', inplace=inplace, limit=limit) + def replace(self, to_replace, value=None, method='pad', inplace=False, limit=None): """ diff --git a/pandas/sparse/frame.py b/pandas/sparse/frame.py index c38936b55696f..40b9ad704dc47 100644 --- a/pandas/sparse/frame.py +++ b/pandas/sparse/frame.py @@ -808,7 +808,7 @@ def applymap(self, func): return self.apply(lambda x: map(func, x)) @Appender(DataFrame.fillna.__doc__) - def fillna(self, value=None, method='pad', inplace=False, limit=None): + def fillna(self, value=None, method=None, inplace=False, limit=None): new_series = {} for k, v in self.iterkv(): new_series[k] = v.fillna(value=value, method=method, limit=limit) diff --git a/pandas/sparse/series.py b/pandas/sparse/series.py index 8be9e2b5c7d75..910e5de86334d 100644 --- a/pandas/sparse/series.py +++ b/pandas/sparse/series.py @@ -438,7 +438,7 @@ def sparse_reindex(self, new_index): fill_value=self.fill_value) @Appender(Series.fillna.__doc__) - def fillna(self, value=None, method='pad', inplace=False, limit=None): + def fillna(self, value=None, method=None, inplace=False, limit=None): dense = self.to_dense() filled = dense.fillna(value=value, method=method, limit=limit) result = filled.to_sparse(kind=self.kind, diff --git a/pandas/tests/test_frame.py b/pandas/tests/test_frame.py index 1be1203480ed1..b3323edd8d684 100644 --- a/pandas/tests/test_frame.py +++ b/pandas/tests/test_frame.py @@ -4567,6 +4567,23 @@ def test_fillna(self): result = self.mixed_frame.fillna(value=0) + self.assertRaises(ValueError, self.tsframe.fillna) + self.assertRaises(ValueError, self.tsframe.fillna, 5, method='ffill') + + def test_ffill(self): + self.tsframe['A'][:5] = nan + self.tsframe['A'][-5:] = nan + + assert_frame_equal(self.tsframe.ffill(), + self.tsframe.fillna(method='ffill')) + + def test_bfill(self): + self.tsframe['A'][:5] = nan + self.tsframe['A'][-5:] = nan + + assert_frame_equal(self.tsframe.bfill(), + self.tsframe.fillna(method='bfill')) + def test_fillna_skip_certain_blocks(self): # don't try to fill boolean, int blocks @@ -4589,10 +4606,10 @@ def test_fillna_inplace(self): df[1][:4] = np.nan df[3][-4:] = np.nan - expected = df.fillna() + expected = df.fillna(method='ffill') self.assert_(expected is not df) - df2 = df.fillna(inplace=True) + df2 = df.fillna(method='ffill', inplace=True) self.assert_(df2 is df) assert_frame_equal(df2, expected) @@ -4623,13 +4640,13 @@ def test_fillna_columns(self): df = DataFrame(np.random.randn(10, 10)) df.values[:, ::2] = np.nan - result = df.fillna(axis=1) + result = df.fillna(method='ffill', axis=1) expected = df.T.fillna(method='pad').T assert_frame_equal(result, expected) df.insert(6, 'foo', 5) - result = df.fillna(axis=1) - expected = df.astype(float).fillna(axis=1) + result = df.fillna(method='ffill', axis=1) + expected = df.astype(float).fillna(method='ffill', axis=1) assert_frame_equal(result, expected) def test_fillna_invalid_method(self): @@ -7317,7 +7334,8 @@ def test_fillna_col_reordering(self): cols = ["COL." + str(i) for i in range(5, 0, -1)] data = np.random.rand(20, 5) df = DataFrame(index=range(20), columns=cols, data=data) - self.assert_(df.columns.tolist() == df.fillna().columns.tolist()) + filled = df.fillna(method='ffill') + self.assert_(df.columns.tolist() == filled.columns.tolist()) def test_take(self): # homogeneous diff --git a/pandas/tests/test_panel.py b/pandas/tests/test_panel.py index 82c6ea65d133a..5cc3d4db5bd04 100644 --- a/pandas/tests/test_panel.py +++ b/pandas/tests/test_panel.py @@ -950,6 +950,15 @@ def test_fillna(self): filled = empty.fillna(0) assert_panel_equal(filled, empty) + self.assertRaises(ValueError, self.panel.fillna) + self.assertRaises(ValueError, self.panel.fillna, 5, method='ffill') + + def test_ffill_bfill(self): + assert_panel_equal(self.panel.ffill(), + self.panel.fillna(method='ffill')) + assert_panel_equal(self.panel.bfill(), + self.panel.fillna(method='bfill')) + def test_truncate_fillna_bug(self): # #1823 result = self.panel.truncate(before=None, after=None, axis='items') diff --git a/pandas/tests/test_series.py b/pandas/tests/test_series.py index a906489e67b57..0676c9b724393 100644 --- a/pandas/tests/test_series.py +++ b/pandas/tests/test_series.py @@ -2818,8 +2818,8 @@ def test_isin(self): def test_fillna_int(self): s = Series(np.random.randint(-100, 100, 50)) - self.assert_(s.fillna(inplace=True) is s) - assert_series_equal(s.fillna(inplace=False), s) + self.assert_(s.fillna(method='ffill', inplace=True) is s) + assert_series_equal(s.fillna(method='ffill', inplace=False), s) #------------------------------------------------------------------------------- # TimeSeries-specific @@ -2827,16 +2827,19 @@ def test_fillna_int(self): def test_fillna(self): ts = Series([0., 1., 2., 3., 4.], index=tm.makeDateIndex(5)) - self.assert_(np.array_equal(ts, ts.fillna())) + self.assert_(np.array_equal(ts, ts.fillna(method='ffill'))) ts[2] = np.NaN - self.assert_(np.array_equal(ts.fillna(), [0., 1., 1., 3., 4.])) + self.assert_(np.array_equal(ts.fillna(method='ffill'), [0., 1., 1., 3., 4.])) self.assert_(np.array_equal(ts.fillna(method='backfill'), [0., 1., 3., 3., 4.])) self.assert_(np.array_equal(ts.fillna(value=5), [0., 1., 5., 3., 4.])) + self.assertRaises(ValueError, ts.fillna) + self.assertRaises(ValueError, self.ts.fillna, value=0, method='ffill') + def test_fillna_bug(self): x = Series([nan, 1., nan, 3., nan],['z','a','b','c','d']) filled = x.fillna(method='ffill') @@ -2863,6 +2866,16 @@ def test_fillna_invalid_method(self): except ValueError, inst: self.assert_('ffil' in str(inst)) + def test_ffill(self): + ts = Series([0., 1., 2., 3., 4.], index=tm.makeDateIndex(5)) + ts[2] = np.NaN + assert_series_equal(ts.ffill(), ts.fillna(method='ffill')) + + def test_bfill(self): + ts = Series([0., 1., 2., 3., 4.], index=tm.makeDateIndex(5)) + ts[2] = np.NaN + assert_series_equal(ts.bfill(), ts.fillna(method='bfill')) + def test_replace(self): N = 100 ser = Series(np.random.randn(N))