Skip to content

Commit 79decd7

Browse files
committed
ENH: can pass dict of values per column to DataFrame.fillna, close #661
1 parent 041d2a3 commit 79decd7

File tree

4 files changed

+54
-9
lines changed

4 files changed

+54
-9
lines changed

pandas/core/frame.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2402,8 +2402,10 @@ def fillna(self, value=None, method='pad', inplace=False):
24022402
Method to use for filling holes in reindexed Series
24032403
pad / ffill: propagate last valid observation forward to next valid
24042404
backfill / bfill: use NEXT valid observation to fill gap
2405-
value : any kind (should be same type as array)
2406-
Value to use to fill holes (e.g. 0)
2405+
value : scalar or dict
2406+
Value to use to fill holes (e.g. 0), alternately a dict of values
2407+
specifying which value to use for each column (columns not in the
2408+
dict will not be filled)
24072409
inplace : boolean, default False
24082410
If True, fill the DataFrame in place. Note: this will modify any
24092411
other views on this DataFrame, like if you took a no-copy slice of
@@ -2438,7 +2440,17 @@ def fillna(self, value=None, method='pad', inplace=False):
24382440
# Float type values
24392441
if len(self.columns) == 0:
24402442
return self
2441-
new_data = self._data.fillna(value)
2443+
if np.isscalar(value):
2444+
new_data = self._data.fillna(value, inplace=inplace)
2445+
elif isinstance(value, dict):
2446+
result = self if inplace else self.copy()
2447+
for k, v in value.iteritems():
2448+
if k not in result:
2449+
continue
2450+
result[k].fillna(v, inplace=True)
2451+
return result
2452+
else: # pragma: no cover
2453+
raise TypeError('Invalid fill value type: %s' % type(value))
24422454

24432455
if inplace:
24442456
self._data = new_data

pandas/core/internals.py

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -201,15 +201,20 @@ def split_block_at(self, item):
201201
left_block = make_block(self.values[:loc],
202202
self.items[:loc].copy(), self.ref_items)
203203
right_block = make_block(self.values[loc + 1:],
204-
self.items[loc + 1:].copy(), self.ref_items)
204+
self.items[loc + 1:].copy(),
205+
self.ref_items)
205206

206207
return left_block, right_block
207208

208-
def fillna(self, value):
209-
new_values = self.values.copy()
209+
def fillna(self, value, inplace=False):
210+
new_values = self.values if inplace else self.values.copy()
210211
mask = com.isnull(new_values.ravel())
211212
new_values.flat[mask] = value
212-
return make_block(new_values, self.items, self.ref_items)
213+
214+
if inplace:
215+
return self
216+
else:
217+
return make_block(new_values, self.items, self.ref_items)
213218

214219
def interpolate(self, method='pad', inplace=False):
215220
values = self.values if inplace else self.values.copy()
@@ -931,11 +936,14 @@ def add_suffix(self, suffix):
931936
f = ('%s' + ('%s' % suffix)).__mod__
932937
return self.rename_items(f)
933938

934-
def fillna(self, value):
939+
def fillna(self, value, inplace=False):
935940
"""
936941
937942
"""
938-
new_blocks = [b.fillna(value) for b in self.blocks]
943+
new_blocks = [b.fillna(value, inplace=inplace)
944+
for b in self.blocks]
945+
if inplace:
946+
return self
939947
return BlockManager(new_blocks, self.axes)
940948

941949
@property

pandas/tests/test_frame.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2968,6 +2968,21 @@ def test_fillna_inplace(self):
29682968
self.assert_(df2 is df)
29692969
assert_frame_equal(df2, expected)
29702970

2971+
def test_fillna_dict(self):
2972+
df = DataFrame({'a': [nan, 1, 2, nan, nan],
2973+
'b': [1, 2, 3, nan, nan],
2974+
'c': [nan, 1, 2, 3, 4]})
2975+
2976+
result = df.fillna({'a': 0, 'b': 5})
2977+
2978+
expected = df.copy()
2979+
expected['a'] = expected['a'].fillna(0)
2980+
expected['b'] = expected['b'].fillna(5)
2981+
assert_frame_equal(result, expected)
2982+
2983+
# it works
2984+
result = df.fillna({'a': 0, 'b': 5, 'd' : 7})
2985+
29712986
def test_truncate(self):
29722987
offset = datetools.bday
29732988

vb_suite/frame_methods.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,13 @@
2323
frame_fancy_lookup_all = Benchmark('df.lookup(row_labels_all, col_labels_all)',
2424
setup,
2525
start_date=datetime(2012, 1, 12))
26+
27+
#----------------------------------------------------------------------
28+
# fillna in place
29+
30+
setup = common_setup + """
31+
df = DataFrame(randn(10000, 100))
32+
df.values[::2] = np.nan
33+
"""
34+
35+
frame_fillna_inplace = Benchmark('df.fillna(0, inplace=True)', setup)

0 commit comments

Comments
 (0)