Skip to content

Commit dcf9d6e

Browse files
changhiskhanwesm
authored andcommitted
API: fillna defaults to no method. Also add ffill and bfill as convenience functions #2027
1 parent 1db53cf commit dcf9d6e

File tree

8 files changed

+82
-13
lines changed

8 files changed

+82
-13
lines changed

pandas/core/frame.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3170,7 +3170,7 @@ def reorder_levels(self, order, axis=0):
31703170
#----------------------------------------------------------------------
31713171
# Filling NA's
31723172

3173-
def fillna(self, value=None, method='pad', axis=0, inplace=False,
3173+
def fillna(self, value=None, method=None, axis=0, inplace=False,
31743174
limit=None):
31753175
"""
31763176
Fill NA/NaN values using the specified method
@@ -3207,7 +3207,11 @@ def fillna(self, value=None, method='pad', axis=0, inplace=False,
32073207
self._consolidate_inplace()
32083208

32093209
if value is None:
3210+
if method is None:
3211+
raise ValueError('must specify a fill method or value')
32103212
if self._is_mixed_type and axis == 1:
3213+
if inplace:
3214+
raise NotImplementedError()
32113215
return self.T.fillna(method=method, limit=limit).T
32123216

32133217
new_blocks = []
@@ -3222,6 +3226,8 @@ def fillna(self, value=None, method='pad', axis=0, inplace=False,
32223226

32233227
new_data = BlockManager(new_blocks, self._data.axes)
32243228
else:
3229+
if method is not None:
3230+
raise ValueError('cannot specify both a fill method and value')
32253231
# Float type values
32263232
if len(self.columns) == 0:
32273233
return self
@@ -3246,6 +3252,14 @@ def fillna(self, value=None, method='pad', axis=0, inplace=False,
32463252
else:
32473253
return self._constructor(new_data)
32483254

3255+
def ffill(self, axis=0, inplace=False, limit=None):
3256+
return self.fillna(method='ffill', axis=axis, inplace=inplace,
3257+
limit=limit)
3258+
3259+
def bfill(self, axis=0, inplace=False, limit=None):
3260+
return self.fillna(method='bfill', axis=axis, inplace=inplace,
3261+
limit=limit)
3262+
32493263
def replace(self, to_replace, value=None, method='pad', axis=0,
32503264
inplace=False, limit=None):
32513265
"""

pandas/core/panel.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -901,7 +901,7 @@ def _combine_panel(self, other, func):
901901

902902
return self._constructor(result_values, items, major, minor)
903903

904-
def fillna(self, value=None, method='pad'):
904+
def fillna(self, value=None, method=None):
905905
"""
906906
Fill NaN values using the specified method.
907907
@@ -927,15 +927,27 @@ def fillna(self, value=None, method='pad'):
927927
DataFrame.reindex, DataFrame.asfreq
928928
"""
929929
if value is None:
930+
if method is None:
931+
raise ValueError('must specify a fill method or value')
930932
result = {}
931933
for col, s in self.iterkv():
932934
result[col] = s.fillna(method=method, value=value)
933935

934936
return self._constructor.from_dict(result)
935937
else:
938+
if method is not None:
939+
raise ValueError('cannot specify both a fill method and value')
936940
new_data = self._data.fillna(value)
937941
return self._constructor(new_data)
938942

943+
944+
def ffill(self):
945+
return self.fillna(method='ffill')
946+
947+
def bfill(self):
948+
return self.fillna(method='bfill')
949+
950+
939951
add = _panel_arith_method(operator.add, 'add')
940952
subtract = sub = _panel_arith_method(operator.sub, 'subtract')
941953
multiply = mul = _panel_arith_method(operator.mul, 'multiply')

pandas/core/series.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2420,7 +2420,7 @@ def fillna(self, value=None, method=None, inplace=False,
24202420
np.putmask(result, mask, value)
24212421
else:
24222422
if method is None: # pragma: no cover
2423-
raise ValueError('must specify a fill method')
2423+
raise ValueError('must specify a fill method or value')
24242424

24252425
fill_f = _get_fill_func(method)
24262426

@@ -2438,6 +2438,12 @@ def fillna(self, value=None, method=None, inplace=False,
24382438

24392439
return result
24402440

2441+
def ffill(self, inplace=False, limit=None):
2442+
return self.fillna(method='ffill', inplace=inplace, limit=limit)
2443+
2444+
def bfill(self, inplace=False, limit=None):
2445+
return self.fillna(method='bfill', inplace=inplace, limit=limit)
2446+
24412447
def replace(self, to_replace, value=None, method='pad', inplace=False,
24422448
limit=None):
24432449
"""

pandas/sparse/frame.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -816,7 +816,7 @@ def applymap(self, func):
816816
return self.apply(lambda x: map(func, x))
817817

818818
@Appender(DataFrame.fillna.__doc__)
819-
def fillna(self, value=None, method='pad', inplace=False, limit=None):
819+
def fillna(self, value=None, method=None, inplace=False, limit=None):
820820
new_series = {}
821821
for k, v in self.iterkv():
822822
new_series[k] = v.fillna(value=value, method=method, limit=limit)

pandas/sparse/series.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -451,7 +451,7 @@ def sparse_reindex(self, new_index):
451451
fill_value=self.fill_value)
452452

453453
@Appender(Series.fillna.__doc__)
454-
def fillna(self, value=None, method='pad', inplace=False, limit=None):
454+
def fillna(self, value=None, method=None, inplace=False, limit=None):
455455
dense = self.to_dense()
456456
filled = dense.fillna(value=value, method=method, limit=limit)
457457
result = filled.to_sparse(kind=self.kind,

pandas/tests/test_frame.py

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4901,6 +4901,23 @@ def test_fillna(self):
49014901

49024902
result = self.mixed_frame.fillna(value=0)
49034903

4904+
self.assertRaises(ValueError, self.tsframe.fillna)
4905+
self.assertRaises(ValueError, self.tsframe.fillna, 5, method='ffill')
4906+
4907+
def test_ffill(self):
4908+
self.tsframe['A'][:5] = nan
4909+
self.tsframe['A'][-5:] = nan
4910+
4911+
assert_frame_equal(self.tsframe.ffill(),
4912+
self.tsframe.fillna(method='ffill'))
4913+
4914+
def test_bfill(self):
4915+
self.tsframe['A'][:5] = nan
4916+
self.tsframe['A'][-5:] = nan
4917+
4918+
assert_frame_equal(self.tsframe.bfill(),
4919+
self.tsframe.fillna(method='bfill'))
4920+
49044921
def test_fillna_skip_certain_blocks(self):
49054922
# don't try to fill boolean, int blocks
49064923

@@ -4923,10 +4940,10 @@ def test_fillna_inplace(self):
49234940

49244941
df[1][:4] = np.nan
49254942
df[3][-4:] = np.nan
4926-
expected = df.fillna()
4943+
expected = df.fillna(method='ffill')
49274944
self.assert_(expected is not df)
49284945

4929-
df2 = df.fillna(inplace=True)
4946+
df2 = df.fillna(method='ffill', inplace=True)
49304947
self.assert_(df2 is df)
49314948
assert_frame_equal(df2, expected)
49324949

@@ -4957,13 +4974,13 @@ def test_fillna_columns(self):
49574974
df = DataFrame(np.random.randn(10, 10))
49584975
df.values[:, ::2] = np.nan
49594976

4960-
result = df.fillna(axis=1)
4977+
result = df.fillna(method='ffill', axis=1)
49614978
expected = df.T.fillna(method='pad').T
49624979
assert_frame_equal(result, expected)
49634980

49644981
df.insert(6, 'foo', 5)
4965-
result = df.fillna(axis=1)
4966-
expected = df.astype(float).fillna(axis=1)
4982+
result = df.fillna(method='ffill', axis=1)
4983+
expected = df.astype(float).fillna(method='ffill', axis=1)
49674984
assert_frame_equal(result, expected)
49684985

49694986
def test_fillna_invalid_method(self):
@@ -7654,7 +7671,8 @@ def test_fillna_col_reordering(self):
76547671
cols = ["COL." + str(i) for i in range(5, 0, -1)]
76557672
data = np.random.rand(20, 5)
76567673
df = DataFrame(index=range(20), columns=cols, data=data)
7657-
self.assert_(df.columns.tolist() == df.fillna().columns.tolist())
7674+
filled = df.fillna(method='ffill')
7675+
self.assert_(df.columns.tolist() == filled.columns.tolist())
76587676

76597677
def test_take(self):
76607678
# homogeneous

pandas/tests/test_panel.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -950,6 +950,15 @@ def test_fillna(self):
950950
filled = empty.fillna(0)
951951
assert_panel_equal(filled, empty)
952952

953+
self.assertRaises(ValueError, self.panel.fillna)
954+
self.assertRaises(ValueError, self.panel.fillna, 5, method='ffill')
955+
956+
def test_ffill_bfill(self):
957+
assert_panel_equal(self.panel.ffill(),
958+
self.panel.fillna(method='ffill'))
959+
assert_panel_equal(self.panel.bfill(),
960+
self.panel.fillna(method='bfill'))
961+
953962
def test_truncate_fillna_bug(self):
954963
# #1823
955964
result = self.panel.truncate(before=None, after=None, axis='items')

pandas/tests/test_series.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2948,6 +2948,9 @@ def test_fillna(self):
29482948

29492949
self.assert_(np.array_equal(ts.fillna(value=5), [0., 1., 5., 3., 4.]))
29502950

2951+
self.assertRaises(ValueError, ts.fillna)
2952+
self.assertRaises(ValueError, self.ts.fillna, value=0, method='ffill')
2953+
29512954
def test_fillna_bug(self):
29522955
x = Series([nan, 1., nan, 3., nan],['z','a','b','c','d'])
29532956
filled = x.fillna(method='ffill')
@@ -2974,8 +2977,15 @@ def test_fillna_invalid_method(self):
29742977
except ValueError, inst:
29752978
self.assert_('ffil' in str(inst))
29762979

2977-
def test_fillna_toomany_params(self):
2978-
self.assertRaises(ValueError, self.ts.fillna, value=0, method='ffill')
2980+
def test_ffill(self):
2981+
ts = Series([0., 1., 2., 3., 4.], index=tm.makeDateIndex(5))
2982+
ts[2] = np.NaN
2983+
assert_series_equal(ts.ffill(), ts.fillna(method='ffill'))
2984+
2985+
def test_bfill(self):
2986+
ts = Series([0., 1., 2., 3., 4.], index=tm.makeDateIndex(5))
2987+
ts[2] = np.NaN
2988+
assert_series_equal(ts.bfill(), ts.fillna(method='bfill'))
29792989

29802990
def test_replace(self):
29812991
N = 100

0 commit comments

Comments
 (0)