Programming & Coding

Reading NetCDF Files in MATLAB and Octave

NetCDF files are the quiet workhorses of scientific computing. Weather models, ocean reanalyses, satellite swaths, climate projections — a huge chunk of it lands on your disk as a .nc file with dimensions, variables, and a pile of metadata attached. The good news: both MATLAB and Octave can read them without any drama. The bad news: the two environments don’t always behave identically, and a few quirks will bite you if you don’t know where to look.

This guide covers the whole workflow: figuring out what’s inside a file before you load a single byte, pulling variables and attributes, slicing out only the data you need, handling scale factors and fill values, and fixing the errors that trip people up most often. The following sections walk through quick file inspection, high-level reading functions, the low-level API for the edge cases, subsetting strategies, and the performance habits that keep large files from eating your RAM.

  • Inspecting structure, dimensions, and attributes before loading
  • High-level reading in MATLAB versus Octave
  • Dropping into the low-level API when you need control
  • Reading slices and strides instead of whole variables
  • Scaling, fill values, and time coordinates
  • NetCDF-4 groups and modern file variants
  • Error messages and what they actually mean

Know What’s Inside Before You Load Anything

The fastest way to waste five minutes is to load a 4 GB variable and then discover it wasn’t the one you wanted. Inspect first.

In MATLAB, the built-in inspector prints dimensions, variables, and attributes in one shot:

ncdisp('air_temperature.nc')

info = ncinfo('air_temperature.nc');
{info.Variables.Name}'
{info.Dimensions.Name}'

Octave exposes the same idea through its netcdf package. Load the package once per session, then inspect:

pkg load netcdf

ncdisp('air_temperature.nc')
info = ncinfo('air_temperature.nc');

Two things to note in that output. First, dimension order: the variable’s size vector tells you exactly how the axes are arranged. Second, the variable’s class and fill value. If a temperature field is stored as 16-bit integers with a scale_factor, the raw numbers in the file are not the numbers you want. More on that below.

Reading Variables in MATLAB

MATLAB ships NetCDF support in the base product, so there’s nothing to install. The workhorse is ncread:

temp = ncread('air_temperature.nc', 'temp');
lon  = ncread('air_temperature.nc', 'lon');
lat  = ncread('air_temperature.nc', 'lat');

size(temp)

Attributes come along via ncreadatt:

units = ncreadatt('air_temperature.nc', 'temp', 'units');
longName = ncreadatt('air_temperature.nc', 'temp', 'long_name');

For global attributes, pass the special group path instead of a variable name:

title = ncreadatt('air_temperature.nc', '/', 'title');

Reading Variables in Octave

Octave’s netcdf package mirrors the MATLAB high-level functions closely enough that most scripts port over with a single added line at the top:

pkg load netcdf

temp = ncread('air_temperature.nc', 'temp');
units = ncreadatt('air_temperature.nc', 'temp', 'units');

The catch is version drift. High-level readers came later to Octave, and some inspectors and attribute helpers arrived even later. If a function is missing, do not fight it — drop to the low-level API, which has been stable for years and gives you the same data:

ncid  = netcdf_open('air_temperature.nc', 'NC_NOWRITE');
varid = netcdf_inq_varid(ncid, 'temp');
data  = netcdf_get_var(ncid, varid);
netcdf_close(ncid);

MATLAB has a parallel low-level API with the same shape — open the file, ask for a variable ID, read, close. It’s more code, but it’s the escape hatch when a high-level helper doesn’t exist or doesn’t support a feature you need.

Read Only the Slice You Actually Need

Whole-variable reads are fine for small files and a disaster for large ones. Both environments let you specify a start index, a count, and a stride, which means you can grab one timestep from a 500-timestep file or every tenth row of a dense grid.

% First timestep of a 3D variable: start, count, stride
t0 = ncread('air_temperature.nc', 'temp', [1 1 1], [Inf Inf 1], [1 1 1]);

% Every fourth grid point in both horizontal directions
coarse = ncread('air_temperature.nc', 'temp', [1 1 1], [Inf Inf 1], [4 4 1]);

Using Inf for count means “everything remaining along that dimension.” That single trick turns an out-of-memory read into a routine one. Indexing is one-based in both environments, matching the rest of their array conventions.

Scaling, Fill Values, and Time Coordinates

NetCDF files rarely store floating-point data at full width. A common pattern is packed integers plus a scale_factor and add_offset attribute. The high-level readers in both MATLAB and Octave apply these automatically, and they convert _FillValue and missing_value to NaN. The low-level API does not. If your low-level read returns suspiciously round integers and the ocean shows up as temperature 32767, that’s why.

Time is the other landmine. Time coordinates are usually stored as numbers with a units attribute like “days since 1950-01-01”. No reader converts that for you. Parse it explicitly:

t = ncread('air_temperature.nc', 'time');
units = ncreadatt('air_temperature.nc', 'time', 'units');
% units tells you the origin and the step; build your datetime vector from it

Read the string, extract the origin date and the unit word, then convert. It’s three lines of code that will save you from plotting a decade of data at the wrong dates.

Groups and Modern File Variants

Newer NetCDF files can contain nested groups, similar to folders. You address variables by path rather than name alone:

data = ncread('model_output.nc', '/forecast/temperature');

Older readers may not understand groups at all. If a file opens but returns an error about an unknown variable, check whether it uses groups before assuming the variable name is wrong. Compression and chunking are invisible to you as a reader, but they have a real effect on performance — a chunked variable read as a slice is fast, the same variable read as a scattered selection can be slow enough to notice.

Errors You’ll Hit, and What They Mean

  • “Undefined function” for a reader: in Octave, the netcdf package isn’t loaded, or your version predates the high-level functions. Load the package or use the low-level API.
  • Size mismatch in your analysis: the variable came back in the file’s native order. Many other tools transpose automatically. Run size and compare against the dimension list from your inspection step, then permute as needed.
  • Everything is NaN: the fill value may be stored as a value that doesn’t map cleanly, or the packed integers weren’t scaled. Check the attributes on the variable.
  • Warnings about type conversion: unsigned integer types are common in NetCDF-4 and some operations dislike them. Cast explicitly to double or single after reading.
  • Painfully slow reads: you’re probably reading the whole variable when you wanted a slice, or reading over a network mount.

Habits That Keep Large Files Manageable

Three rules cover most of it. Inspect before loading, so you never read a variable you don’t need. Subset at read time rather than loading and then cropping, because the read is what costs you. And always know the shape of what came back before you feed it into anything else — a quick size and class call has saved more debugging sessions than any fancy tool.

When you’re working across both environments, write your reading logic in a small wrapper function so the only version-specific line is the package load. That one structural choice makes the rest of your analysis portable, and it means a file that reads in one environment reads in the other.

NetCDF reading isn’t glamorous, but it’s the front door to almost everything interesting in scientific data work. Get the inspection, subsetting, and scaling steps right once, and every dataset after that becomes a variation on a theme you already understand.

There’s plenty more where this came from — keep exploring on TechBlazing for practical, no-hype walkthroughs on tools, data formats, and the tech that actually helps you get work done.