Replace NaNs with previous values

164 views (last 30 days)
Johannes
Johannes on 9 Oct 2012
Edited: Namrata Goswami on 11 Dec 2020
Hello,
I have the following problem. I like to replace NaNs with the previous values.
A =
4 5 6 7 8
32 NaN NaN 21 NaN
12 NaN 12 NaN NaN
34 NaN NaN NaN NaN
B =
4 5 6 7 8
32 5 6 21 8
12 5 12 21 8
34 5 12 21 8
I sloved it like this:
for i = 2:5
[r,c] = find(isnan(A(:,i)));
while sum(isnan(A(:,i)))>0
A(r,i) = A(r-1,i);
end
end
I'm sure there is a way avoiding the for and the while statement. I search for an "elegant" solution.
Someone's able to help me?
  2 Comments
Johannes
Johannes on 9 Oct 2012
If the first value is NaN, everything should be NaN untill a different value appears in the column.
Thanks, Johannes

Sign in to comment.

Answers (5)

Moshe Flam
Moshe Flam on 3 Dec 2017
Edited: Moshe Flam on 4 Dec 2017
Use `fillmissing` According to this matlab documentation (click here) on their website.
ROWBYROW = 2;
B = fillmissing(A,'previous',ROWBYROW);
  2 Comments
Namrata Goswami
Namrata Goswami on 11 Dec 2020
This worked for me partially, since I need to replace missing values withing group. How to use fillmising within a group, like with splitapply ?

Sign in to comment.


Matt Fig
Matt Fig on 9 Oct 2012
Edited: Matt Fig on 9 Oct 2012
Johannes, notice that your solution will fail if the first value in a column is nan. Rather than looking for a vectorized solution that may end up being rather convoluted (and being slower!), I would simply write a good FOR loop function that can handle all cases. For example, the following solution does not use the FIND function, and only uses simple loops and thus should be very fast:
function A = fill_nans(A)
% Replaces the nans in each column with
% previous non-nan values.
for ii = 1:size(A,2)
I = A(1,ii);
for jj = 2:size(A,1)
if isnan(A(jj,ii))
A(jj,ii) = I;
else
I = A(jj,ii);
end
end
end
  7 Comments
Faez Alkadi
Faez Alkadi on 1 May 2017
Good question Timothy Jackson. I hope someone can answer this

Sign in to comment.


Wayne King
Wayne King on 9 Oct 2012
Edited: Wayne King on 9 Oct 2012
How about:
A = [ 4 5 6 7 8
32 NaN NaN 21 NaN
12 NaN 12 NaN NaN
34 NaN NaN NaN NaN];
indices = isnan(A);
A(indices) = 0;
B = repmat([4 5 6 7 8],size(A,1),1);
A = A+B.*indices;
  1 Comment
Matt Fig
Matt Fig on 9 Oct 2012
Johannes comments:
"Solution there:
A =
4 5 6 7 8
32 5 6 21 8
12 5 12 7 8
34 5 6 7 8
Not good, would need the following: 4 5 6 7 8 32 5 6 21 8 12 5 12 21 8 34 5 12 21 8
Still thanks for you help!"

Sign in to comment.


owr
owr on 9 Oct 2012
I do this all the time, my code uses for loops, but I dont see anything wrong with for loops. Im sure there are more elegent solutions but this does the trick for me and is more than fast enough:
function datai = backfillnans(data)
% Dimensions
[numRow,numCol] = size(data);
% First, datai is copy of data
datai = data;
% For each column
for c = 1:numCol
% Find first non-NaN row
indxFirst = find(~isnan(data(:,c)),1,'first');
% Find all NaN rows
indxNaN = find(isnan(data(:,c)));
% Find NaN rows beyond first non-NaN
indx = indxNaN(indxNaN > indxFirst);
% For each of these, copy previous value
for r = (indx(:))'
datai(r,c) = datai(r-1,c);
end
end
  2 Comments
owr
owr on 9 Oct 2012
Ah, good catch Matt, thanks for that. Ive been using this for almost 2 years multiple times a day and thats never come up - I guess I never have a full column of nans. It can be fixed I guess by putting an:
if( ~isempty(indxFirst) )
after the line that calculates "indxFirst". Part of me would actually like the whole process to fail so I can figure out why I passed a full column of nans in the first place - that would be symptomatic of a much bigger issue...
Anyways, thanks for taking the time to run and test the code.

Sign in to comment.


Carlos Vladimir Rodriguez Caballero
I found your procedure much more elegant and efficient. It was very helpful man.

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!

Translated by