Missing Data
|
This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production. The reference material consulted while preparing these pages is listed in the section bibliography. |
Written against MATLAB R2026b. The sessions in the "Octave 11.3.0 output" blocks were run in GNU Octave 11.3.0, using only code that Octave also accepts (isnan, logical indexing, interp1). Octave 11.3.0 has no missing, ismissing, standardizeMissing, fillmissing or rmmissing (they belong to a statistics package that was not installed), so every example that uses those functions is checked line by line against the R2026b documentation and labelled "not run here".
A missing value marks a data point that is absent, unrecorded or unusable. The documentation gives two reasons to care. A missing value can be meaningful (a sensor was switched off), but more often it is an unreliable or unusable point, and a computation that does not account for it returns a misleading answer. MATLAB handles the problem in three layers: each data type has its own standard missing value; a family of functions finds, standardises, fills and removes missing values; and many statistics functions take a flag that ignores them. Tabular data is on Tables, dates on Dates and Times and categories on Categorical Arrays.
The standard missing value of each type
There is no single missing value; the representation depends on the type.
| Data type | Standard missing value | Notes |
|---|---|---|
|
|
Created by |
|
|
Displays as |
|
|
Displays as |
|
|
A missing string is not the same as an empty string |
|
|
A categorical element that belongs to no category. |
cell array of character vectors |
|
|
Integer types, |
None |
They cannot hold a missing value. |
The missing value (R2017a) is a type-independent way to write any of these. Assigning missing to an array or table element converts it to the standard missing value of that type. The documentation example builds one vector of each type this way:
xDouble = [missing 1 2 3 4] % 1x5 double, first is NaN
xDatetime = [missing datetime(2014,1:4,1)] % first is NaT
xString = [missing "a" "b" "c" "d"] % first is <missing>
xCategorical = [missing categorical({'cat1' 'cat2' 'cat3' 'cat4'})] % first is <undefined>
(Not run here.) The documentation shows the displayed values NaN, NaT, <missing> and <undefined> respectively. missing itself is not a numeric type: concatenated with a double it becomes NaN, with a string it becomes <missing>. To set elements of a table or timetable, assign missing to the element or the variable slice, as the missing reference page does for a timetable row.
How NaN behaves
NaN is defined by IEEE arithmetic, and it spreads. These are the rules that cause most trouble. The results below were produced by Octave 11.3.0; the comparison, propagation, max and sort rules are the documented MATLAB behaviour too, and for unique see its reference page (not run here).
x = [1 3 NaN 4 NaN NaN 5];
disp(isnan(x))
disp(NaN == NaN)
disp(isequal(NaN,NaN))
disp(isequaln(NaN,NaN))
disp(mean(x))
disp(sum(x))
disp(max(x))
disp(sort([3 NaN 1 2]))
disp(sort([3 NaN 1 2],'descend'))
disp(unique([NaN NaN 1]))
Octave 11.3.0 output:
0 0 1 0 1 1 0
0
0
1
NaN
NaN
5
1 2 3 NaN
NaN 3 2 1
1 NaN NaN
-
Comparison is always false.
NaN == NaNis 0,NaN > 1is 0, andNaN ~= NaNis 1. Never test withx == NaN; useisnan(x)orismissing(x). (isequalalso treats NaNs as different;isequalntreats them as equal.) -
Arithmetic propagates.
meanandsumof a vector that contains oneNaNreturnNaN. The documentation points out that aplotsilently skips theNaNpoints, so the picture can look fine while the average isNaN. -
Extrema skip it.
maxandminignoreNaNby default (the documented default flag for them is"omitmissing"), somax(x)above is 5, whilemeanandsuminclude missing values by default. The two behaviours in one program are a classic source of confusion. -
Sorting.
sortputsNaNlast for ascending order and first for descending order, as the run shows. The'MissingPlacement'option ('first','last','auto') controls it. -
uniquekeeps eachNaN. The run shows twoNaNvalues afterunique, because each is "different" from the other. See Sets and Sorting. -
Integers.
int8(NaN)is 0 in the Octave run, so converting data that containsNaNto an integer type silently turns the gaps into zeros. Convert only after filling or removing.
Finding missing values
TF = ismissing(A) returns a logical array of the size of A with 1 for each standard missing value of its type. For a table it checks each variable according to its own type, so one call finds missing values in numeric, string, categorical, datetime and cell variables together. isnan is meant for numeric data.
TF = ismissing(A,indicator) replaces the default definition: the values in indicator are the only ones treated as missing, and the standard missing values are ignored unless you list them. For a numeric array, ismissing(A,[0 -99]) treats 0 and -99 as missing and does not treat NaN as missing. For a table, give a cell array with one indicator per type (the cleaning page uses ismissing(messyTable,{string(missing),NaN,-99})). TF = ismissing(___,OutputFormat=format) selects the form of the result for tables. anymissing(A) (R2022a) returns one logical value, true if any element of A is missing, which reads better than any(ismissing(A),"all") in an if.
A = [3 NaN 5 6 7 NaN NaN 9];
TF = ismissing(A) % 0 1 0 0 0 1 1 0, the documentation result
if anymissing(A)
disp("There are gaps")
end
(Not run here.)
Standardising odd placeholders
Real files use placeholders that are not standard missing values: -99, -999, "N/A", ".", Inf. B = standardizeMissing(A,indicator) replaces every value that matches indicator with the standard missing value for the data type, and it takes the same kind of indicator as ismissing, with DataVariables= to restrict it for tables.
A = [0 1 5 -99 8 3 4 -99 16];
B = standardizeMissing(A,-99) % 0 1 5 NaN 8 3 4 NaN 16
T = standardizeMissing(T,{Inf,"N/A"}) % in every variable of a table, by type
(Not run here; the first result is the documentation’s own.) When you read the file with readtable, the TreatAsMissing name-value argument does the same job at import time, for example TreatAsMissing=[".","NA"]; any placeholder that survives import as a valid number, such as -99, still needs standardizeMissing. In base MATLAB without the function, the equivalent for numbers is a logical assignment, shown in the Octave session below.
Filling with fillmissing
F = fillmissing(A,method) replaces missing entries. By default it works along the first dimension whose size is not 1 (columns of a matrix); a dim argument selects another. [F,TF] = fillmissing(___) also returns which entries were filled. These are the choices.
| Method | Fills with |
|---|---|
|
The constant |
|
The previous, next or nearest nonmissing value. |
|
Linear interpolation of the neighbouring nonmissing values. |
|
Cubic interpolation: piecewise cubic spline, shape-preserving piecewise cubic, and modified Akima. |
|
The mean, median or mode of the nonmissing values along the operating dimension. |
|
A moving mean or median over a window length; an integer |
|
Values from the nearest rows (Euclidean distance), or the mean of the |
|
A function handle |
Name-value arguments refine the fill:
-
SamplePointsgives the x-locations when the data are not equally spaced (a vector, or a table variable name); it must be sorted and unique. This is how timestamps drive the interpolation. -
EndValuescontrols leading and trailing gaps; the default"extrap"applies the chosen method, and"previous","next","nearest","none", or a scalar or vector can replace it. -
MaxGapleaves gaps wider than the limit unfilled. A numeric value, or a duration for datetime sample points, is accepted. -
DataVariablesrestricts a table to some variables;ReplaceValues=falseappends the filled variables instead of replacing the originals. -
MissingLocations(R2024a) is a logical array or table that says which entries count as missing, instead of the standard definition.
fillmissing has existed since R2016b (the reference page’s version history); the mean, median and mode methods were added in R2026a.
A = [1 3 NaN 4 NaN NaN 5];
fillmissing(A,"previous") % 1 3 3 4 4 4 5 (documentation result)
fillmissing(A,"constant",0) % 1 3 0 4 0 0 5
(Not run here.)
Removing with rmmissing
R = rmmissing(A) removes missing entries from a vector, and any row that contains one from a matrix or table. rmmissing(A,dim) selects rows or columns, MinNumMissing=n removes only the rows with at least n missing values, DataVariables limits the check to named table variables, and [R,TF] = rmmissing(___) returns the logical vector of removed rows or columns.
A = [1 3 NaN 6 NaN];
R = rmmissing(A) % 1 3 6, the documentation result
(Not run here.) Removing rows of a matrix is what the logical-indexing form does in plain code: A(~any(isnan(A),2),:) keeps the complete rows. The choice between filling and removing is a decision about the data, not a coding one. Removing rows from a time series breaks equal spacing; filling invents values. Record which readings were filled (the TF output).
Source: MATLAB documentation: rmmissing
Ignoring missing values in statistics
Many summary functions can skip missing values without a separate cleaning step. A flag is passed after the data (and the dimension):
xDouble = [NaN 1 2 3 4];
sum(xDouble) % NaN
sum(xDouble,"omitnan") % 10, the documentation result
mean(xDouble,"omitmissing") % 2.5
-
"omitmissing"ignores the missing values of any supported type;"includemissing"keeps them. These generic flags are the current form. The type-specific forms"omitnan","omitnat"and"omitundefined"(and theincludecounterparts) are still accepted;"omitnan"is what older code and books use. The version history ofmeanlists the generic flags under R2023a; check the page of each function you use. -
For
mean,sum,stdand similar functions the default is to include missing values, so the answer isNaN. Formaxandminthe default is to omit them. State the flag explicitly so the choice is visible.
In base MATLAB the same result is obtained by selecting the valid data: mean(x(~isnan(x))) (this form is also in the Octave run below). Statistics on tables are in Tables and the descriptive-statistics functions in Descriptive Statistics and Preprocessing.
Cleaning the sensor log
The heatflow scenario (The heatflow scenario) records thermocouple readings in sensors.csv. The logger writes -999 when a reading is lost and NA when a field is blank. The first listing, in plain base-language code, was run in Octave 11.3.0. It uses a single channel, readings every 5 minutes, and -999 as the placeholder:
t = 0:5:35;
raw = [20.1 20.8 -999 22.4 -999 -999 25.0 25.6];
temp = raw;
temp(temp == -999) = NaN; % standardise the placeholder
disp(temp)
missingNow = isnan(temp)
fprintf('%d of %d readings missing\n', nnz(missingNow), numel(temp));
good = ~missingNow;
filled = temp;
filled(missingNow) = interp1(t(good), temp(good), t(missingNow)); % linear fill
disp(filled)
fprintf('mean with gaps: %g\n', mean(temp));
fprintf('mean of valid readings: %.4f\n', mean(temp(good)));
disp(class(int8(NaN)))
disp(int8(NaN))
Octave 11.3.0 output:
20.1000 20.8000 NaN 22.4000 NaN NaN 25.0000 25.6000
missingNow =
0 0 1 0 1 1 0 0
3 of 8 readings missing
20.100 20.800 21.600 22.400 23.267 24.133 25.000 25.600
mean with gaps: NaN
mean of valid readings: 22.7800
int8
0
MATLAB shows the numbers in its own layout (the disp lines differ in spacing). The run illustrates the whole pipeline: standardise, count, fill by interpolation over the time axis, and compare the mean that includes gaps (NaN) with the mean of the valid data. Note that interp1 returns the interpolated values only inside the range of good points; a missing reading at the end of the log is not filled by interp1 in this form, whereas fillmissing with the default EndValues extrapolates.
The same job on the real log, as a table, uses the missing-data functions (MATLAB R2026b; not run here; the column names Time, SensorID and Temperature are those assumed for sensors.csv):
T = readtable("data/sensors.csv",TextType="string",TreatAsMissing=["NA" "."]);
T = standardizeMissing(T,-999,DataVariables="Temperature");
summary(T) % NumMissing per variable
fprintf("%d missing readings\n",nnz(ismissing(T.Temperature)));
% Fill each sensor separately, using the timestamps as the x-axis,
% and leave gaps wider than 15 minutes as they are.
for id = unique(T.SensorID)'
rows = T.SensorID == id;
T.Temperature(rows) = fillmissing(T.Temperature(rows),"linear", ...
SamplePoints=T.Time(rows),MaxGap=minutes(15));
end
T = rmmissing(T,DataVariables="Temperature"); % drop what could not be filled
avg = mean(T.Temperature,"omitmissing")
The loop fills each sensor on its own, because interpolating across rows from different sensors would mix them. The last line is shown with the flag for completeness: after rmmissing nothing is missing, so the flag has no effect. When you keep the unfilled rows, the flag is what keeps the mean from becoming NaN. Reading CSV files is on Reading and Writing Tabular Data.
Pitfalls
-
x == NaNis never true. Useisnanorismissing. -
isnanon a string or datetime.isnanis for numeric data; forstring,categoricalanddatetimedata useismissing,isundefined(categorical) orisnat(datetime). -
The empty string is not missing.
""is a valid text value andismissing("")is false;<missing>is the missing string. A file that uses blank fields needsTreatAsMissingorstandardizeMissing(T,""). -
mean,sum,stdreturnNaN;max,minskip it. Pass the flag explicitly. -
Filling across the wrong axis. A matrix is filled column by column by default. For samples stored in rows, pass
dimas 2. For timestamps, passSamplePoints; without it the points are assumed equally spaced. -
Converting to integers.
int8(NaN)is 0 (shown above). Fill or remove first. -
Silent plots.
plotskipsNaN, so a gap looks like a break in the line rather than a warning. Count the missing values before plotting. -
Filling the test set. Filling with the mean of the whole data set leaks information when you later split the data for a model; compute the fill value from the training part only.
Where older books differ
Books written for MATLAB 5 to 7 treat missing data as NaN only, test it with isnan, and remove or replace it with logical indexing (x(isnan(x)) = 0), as in the base-language code above. The missing value (R2017a), ismissing for all types, standardizeMissing, fillmissing (R2016b), rmmissing, anymissing (R2022a), NaT for dates, <missing> for strings and the "omitmissing" flag are all newer than those books. The older 'omitnan' form still works. The functions of the Statistics and Machine Learning Toolbox named nanmean, nansum and similar, which older code uses, are superseded by the "omitmissing" flag in base MATLAB.