How to convert CSV to a MATLAB .mat file (4 ways)
By MegaRows · Published · Updated · 12 min read
You have a CSV, a logger export or a test run, and you want it in MATLAB as a .mat file: one load and every column is there under its own name, as numbers rather than text. This guide shows four ways to make one, what each of them writes, and the snags on the way: column names MATLAB won’t take, text and empty cells, dates and time zones, and files too big for one variable. We ran the Python and GNU Octave code on a test file and checked every value it wrote.
The four ways at a glance
| Way | You need | Good for | What it writes |
|---|---|---|---|
| In your browser (MegaRows) | A browser. No install, no account, no MATLAB | A one-off file or a big one, without code. Nothing is uploaded | MAT-file v7: a struct, one variable per column or a matrix, with the names and units |
| MATLAB | MATLAB, R2019a or later for all the code here | Data you will go on working with in MATLAB | v7 or v7.3, and tables and datetimes if only MATLAB will read the file |
| Python | Python with pandas and SciPy | Scripts, many files, a step in a pipeline | MATLAB’s Level 5 format, compressed like v7. Not v7.3 |
| GNU Octave | Octave, free for Windows, macOS and Linux | MATLAB-style code without a MATLAB licence | v6 or v7. Not v7.3 |
The examples use the same small file throughout, weather.csv: hourly UK weather with a time column, two number columns, a text column, a column called “Wind Speed” (with a space) and two empty cells.
Way 1: In your browser, with MegaRows
The MegaRows CSV to MAT converter reads your CSV on your own device and writes the .mat file there: nothing is uploaded, there is no sign-up, and you don’t need MATLAB.
- Open your file. Drop the CSV onto the converter or click “Choose a file”. TSV, TXT, Excel, JSON and Parquet files work too. The separator, the header row, a units row and the column types are found for you.
- Choose what goes in. Untick the columns you don’t need, then pick the layout and how dates are written.
- Download. Every row goes into a .mat file named after yours.
What you get:
- Three layouts: a struct
datawith a field per column (the default,data.London_Temperature), one variable per column, or a matrix of the number columns withnamesandunitsin the same order. - The names and units:
nameskeeps your column names as they were, andunitsthe units, when the file has a units row or the names carry them. Field names are made valid for MATLAB: “London Temperature” becomesLondon_Temperature. - Dates as MATLAB datenum (the default), POSIX seconds or text.
- Exact numbers: the doubles themselves, not rounded text, so every digit of your CSV is kept. Text becomes cell arrays, true and false become logical, and a missing number is NaN.
- MAT-file version 7, compressed: the format MathWorks lists as readable by MATLAB 7.0 (R14) and later, and one GNU Octave and SciPy read too. We check every layout with SciPy and with Octave, value by value, and MATLAB R2026b read the sample’s file exactly (9 Oct 2026).
- Up to 2 GB per variable, the limit of version 7 files. For a bigger file choose one variable per column, and each column gets its own 2 GB. Bigger variables need v7.3, which the converter doesn’t write yet.
Convert your CSV now, without uploading it
Free, no account. A sample file of a million rows is one click away if you want to try it first.
Check it yourself: open your browser’s developer tools, select the Network tab and convert a file. No request carries your data.
To look inside a .mat file first, open it in the MAT file viewer; to go the other way, use MAT to CSV.
Way 2: In MATLAB
MATLAB reads the CSV with readtable and writes the .mat file with save. The code below uses documented MATLAB functions only (see Sources). We ran it in MATLAB R2026b on the same test file as the Python and Octave code.
A struct with a field per column
T = readtable('weather.csv'); % a table, one variable per column
T.time = convertTo(T.time, 'datenum'); % dates as datenum (R2018b and later)
data = table2struct(T, 'ToScalar', true); % a struct with a field per column
save('weather.mat', 'data', '-v7')- readtable reads every row into a table, one variable per column. An empty number becomes NaN and an empty text an empty character vector, and text columns are cell arrays of text by default.
- Column names: by default readtable turns names that aren’t valid MATLAB names into valid ones, so check
T.Properties.VariableNamesbefore you rely on a name. - Dates: readtable detects dates and gives you
datetime.convertToturns it into a datenum, a plain number that MATLAB, Octave and SciPy all read. Leave that line out to keep the datetime: MATLAB loads it back as one, but SciPy and Octave can’t read a datetime from a .mat file. - table2struct with
'ToScalar', truemakes one struct whose fields are the columns, each a column vector. - save … '-v7' writes a MAT-file version 7: compressed, readable by MATLAB 7.0 (R14) and later, at most 2 GB per variable. Name the version: without it, save uses the default set in MATLAB’s settings (General › MAT Files).
Or keep the table
T = readtable('weather.csv');
save('weather_table.mat', 'T', '-v7')A .mat file can hold the table itself, with its datetime column. It needs MATLAB R2013b or later to load, when tables arrived, and SciPy and GNU Octave can’t read it, so save the struct for anyone without MATLAB.
Version 7 or 7.3
save('weather_v73.mat', 'data', '-v7.3')-v7.3 (MATLAB R2006b and later) is for variables of 2 GB or more. It is an HDF5-based file, and SciPy’s loadmat can’t read it, while Octave reads only parts of it (see which version is my .mat file?). Stay with -v7 unless a variable is too big for it.
A file of numbers only: readmatrix
M = readmatrix('weather-numbers.csv'); % the header row is skipped; empty cells are NaN
save('weather_matrix.mat', 'M', '-v7')readmatrix (R2019a) gives one numeric matrix. It detects the header lines and skips them, and fills empty cells with NaN. The column names are not kept, so save them yourself if you need them.
Dates or headers readtable gets wrong: detectImportOptions
opts = detectImportOptions('weather.csv');
opts = setvartype(opts, 'time', 'datetime');
opts = setvaropts(opts, 'time', 'InputFormat', 'yyyy-MM-dd HH:mm:ss');
T = readtable('weather.csv', opts);detectImportOptions (R2016b) shows what readtable would do and lets you change it before reading: here the time column is read as datetime with its format spelled out. Spell it out whenever days and months could swap: a UK date such as 03/01/2026 09:00 needs 'dd/MM/yyyy HH:mm'. For a units row under the header, set opts.VariableUnitsLine = 2 and opts.DataLines = [3 Inf] on the same options; the units go into the table’s VariableUnits property.
Way 3: In Python, with pandas and SciPy
pandas reads the CSV and SciPy’s savemat writes the .mat file. A Python dict becomes a MATLAB struct, so this gives the same layout as the MATLAB code: a struct data with a field per column, plus names with the original column names. Install the packages with pip install pandas scipy.
import re
import unicodedata
import numpy as np
import pandas as pd
from scipy.io import savemat
KEYWORDS = {"break", "case", "catch", "classdef", "continue", "else", "elseif",
"end", "for", "function", "global", "if", "otherwise", "parfor",
"persistent", "return", "spmd", "switch", "try", "while"}
def matlab_name(name, taken):
"""A valid MATLAB name: a letter, then letters, digits or _, 63 at most."""
name = unicodedata.normalize("NFKD", name).encode("ascii", "ignore").decode()
name = re.sub(r"[^A-Za-z0-9_]+", "_", name).strip("_")
if not name[:1].isalpha() or name in KEYWORDS:
name = "x" + name
name = unique = name[:63]
n = 2
while unique in taken: # repeated names get _2, _3, ...
unique = f"{name[:59]}_{n}"
n += 1
taken.add(unique)
return unique
df = pd.read_csv("weather.csv", parse_dates=["time"])
data, taken = {}, set()
for column, values in df.items():
if pd.api.types.is_datetime64_any_dtype(values):
# MATLAB datenum: days since year 0; 719529 is 1 January 1970
values = (values - pd.Timestamp("1970-01-01")) / pd.Timedelta(days=1) + 719529
if pd.api.types.is_numeric_dtype(values):
values = values.to_numpy(dtype=np.float64) # double; empty cells are NaN
else:
values = values.fillna("").astype(str).to_numpy(dtype=object) # a cell array
data[matlab_name(column, taken)] = values
names = np.array(df.columns, dtype=object) # the original column names
savemat("weather.mat", {"data": data, "names": names},
do_compression=True, long_field_names=True, oned_as="column")- Valid names:
matlab_namemakes every column name one MATLAB accepts. SciPy doesn’t check: in our test it wrote “Wind Speed” as it is, which MATLAB can’t use as a field name, and left out a field whose name starts with a digit or an underscore with only a warning. - Long names:
long_field_names=Trueallows field names of up to 63 characters. Without it, savemat stops at 31 with “Field names are restricted to 31 characters”. - Numbers as double: pandas reads a column of whole numbers as int64, and savemat would save it as int64. Empty cells are NaN.
- Text as a cell array: a NumPy object array becomes a cell array. A plain list of strings would become a char matrix padded with spaces.
- Dates as datenum: days since year 0, where 719529 is 1 January 1970. For POSIX seconds instead, divide by
pd.Timedelta(seconds=1)and leave out+ 719529. In MATLAB,datetime(x, 'ConvertFrom', 'datenum')or'posixtime'turns them back into dates. - Columns, not rows:
oned_as="column"saves each column as a column vector, as readtable gives them; the default is a row. - The format: savemat writes MATLAB’s Level 5 format (SciPy’s documentation: “MATLAB 5 and up (to 7.2)”), and
do_compression=Truecompresses it as MATLAB’s-v7does. It doesn’t write v7.3, so the 2 GB per variable limit applies. For bigger variables, the hdf5storage package is one option that writes v7.3 files; we haven’t tested it.
Tested with Python 3.12, pandas 3.0, NumPy 2.5 and SciPy 1.18 on our weather CSV: every number came back bit for bit, the empty cells as NaN and an empty text, and the times to within 0.1 milliseconds.
Way 4: In GNU Octave
GNU Octave is free and runs most MATLAB-style code. The version we tested, 8.4, has no readtable or readmatrix, so read the file with textscan, which takes a format for each column (%s for text, %f for numbers):
fid = fopen("weather.csv");
names = strsplit(fgetl(fid), ","); % the header row
C = textscan(fid, "%s %f %f %s %f", "Delimiter", ",", "EmptyValue", NaN);
fclose(fid);
data.time = datenum(C{1}, "yyyy-mm-dd HH:MM:SS"); % dates as datenum
data.London_Temperature = C{2};
data.Leeds_Humidity = C{3};
data.Conditions = C{4}; % text: a cell array
data.Wind_Speed = C{5};
save("-v7", "weather.mat", "data", "names");For a file of numbers only, dlmread is shorter. The row and column offsets 1, 0 skip the header row:
M = dlmread("weather-numbers.csv", ",", 1, 0, "emptyvalue", NaN); % skip the header
save("-v7", "weather_matrix.mat", "M");- Empty cells:
dlmreadandcsvreadfill an empty cell with 0, not NaN, unless you pass"emptyvalue", NaNas above.textscangives NaN. - Always name the format: without one, Octave’s
savewrites its own text format, not a MAT-file."-v7"writes a compressed MAT-file version 7,"-v6"an uncompressed one. - No v7.3: Octave’s manual says it doesn’t yet save MATLAB’s v7.3 format.
- Dates:
datenumwith a format turns the date text into the same datenum numbers MATLAB uses.
Tested with GNU Octave 8.4.0: every value of both files matched the CSV when read back with SciPy, and Octave read the file the Python code wrote as well as its own.
Seven pitfalls and how to avoid them
| Pitfall | What goes wrong | What to do |
|---|---|---|
| Column names MATLAB won’t take | A MATLAB name starts with a letter and holds only letters, digits and underscores, so “Wind Speed”, “2nd sensor”, “Temp (°C)”, a repeated name or a keyword such as end or for can’t be a field name (Octave reserves a few more words, such as do and until). Before R2025a a name stops at 63 characters (R2025a allows 2,048). SciPy writes a name with a space as it is and leaves out one that starts with a digit or an underscore. | Make every name valid before saving: readtable and table2struct do it in MATLAB, matlab_name above in Python. Keep the original names in a variable of their own, such as names. |
| Text columns | MATLAB has two kinds of text: cell arrays of character vectors and string arrays. A string array, like a datetime or a table, is saved as a MATLAB object that SciPy and Octave can’t read. In Python, a plain list of strings becomes a char matrix padded with spaces, not a cell array. | Keep text as cell arrays: readtable gives them by default, and in Python a NumPy object array (dtype=object) becomes one. |
| Empty cells | readtable, readmatrix, textscan and pandas make an empty number NaN, but Octave’s dlmread and csvread make it 0, which looks like a real reading. | Pass "emptyvalue", NaN to dlmread or csvread. After loading, count the NaN values and compare them with the empty cells you expect. |
| Dates and time zones | A datetime in a .mat file is a MATLAB object that SciPy and Octave can’t read. A datenum or POSIX number has no time zone: it is whatever clock you converted. Times with an offset (Z, +01:00) or a clock change in the file need a decision, and a UK date such as 03/01/2026 can be read as 1 March if the format is guessed. | Save dates as datenum or POSIX seconds and say which clock in a note. Convert offsets to UTC first: in pandas, pd.to_datetime(df["time"], utc=True).dt.tz_localize(None). Give day-first formats explicitly: date_format="%d/%m/%Y %H:%M" in pandas, 'InputFormat', 'dd/MM/yyyy HH:mm' in MATLAB. |
| Precision | A .mat file stores each double exactly, but a CSV is text: if the program that wrote it rounded the numbers, the .mat file keeps that rounding. Saving as single keeps about 7 significant digits. | Export the CSV with full precision when every digit matters, and keep double unless half the file size matters more. |
| Very large files | A MAT-file v7 holds at most 2 GB per variable before compression, about 268 million doubles. A struct is one variable, so all its fields share the 2 GB. | Save one variable per column, or use -v7.3 in MATLAB. SciPy, Octave and the MegaRows converter don’t write v7.3 (in the converter it is coming). |
| A CSV that went through Excel | Excel opens at most 1,048,576 rows. A CSV opened in Excel and saved again has lost every row after that. | Convert the original CSV, not a copy saved by Excel, and count its rows first. |
To count a CSV’s rows without opening it, use the CSV row counter; for why Excel stops, see CSV too large for Excel.
Check the result
Whichever way you made the file, load it back and look before you pass it on.
In MATLAB
whos -file weather.mat
S = load('weather.mat');
S.data.London_Temperature
t = datetime(S.data.time, 'ConvertFrom', 'datenum')whos -file lists the variables, their sizes and classes without loading them, and datetime with 'ConvertFrom', 'datenum' turns the datenums back into dates.
In Python
import pandas as pd
from scipy.io import loadmat, whosmat
print(whosmat("weather.mat")) # name, size and class of each variable
mat = loadmat("weather.mat", simplify_cells=True)
data = mat["data"]
print(list(data))
print(data["London_Temperature"])
print(pd.to_datetime(data["time"] - 719529, unit="D").round("s")[:2])What it printed for the file the Python code above wrote:
[('data', (1, 1), 'struct'), ('names', (5, 1), 'cell')]
['time', 'London_Temperature', 'Leeds_Humidity', 'Conditions', 'Wind_Speed']
[4.2 3.9 nan 3.1 2.8 0.1]
DatetimeIndex(['2026-01-15 00:00:00', '2026-01-15 01:00:00'], dtype='datetime64[ns]', freq=None)whosmat lists the variables without loading them, and simplify_cells=True gives plain dicts and arrays instead of NumPy record arrays.
In GNU Octave
whos -file weather.mat
s = load("weather.mat");
fieldnames(s.data)'
s.data.London_Temperature'The same check reads the file the Python code wrote, too.
Rather not write code?
Open the CSV in the converter and download the .mat file. It’s free, needs no account or MATLAB, and your file never leaves your device.
Convert CSV to MATFrequently asked questions
Can I convert a CSV to a .mat file without MATLAB?
Yes. The MegaRows CSV to MAT converter does it in your browser, free and without uploading the file. Python (pandas and SciPy) and GNU Octave do it in a few lines of code. All three write MAT-file version 7 or MATLAB’s compatible Level 5 format, which MathWorks lists as readable by MATLAB 7.0 (R14) and later.
What is the difference between a .mat file and a .m file?
A .mat file holds data: binary arrays that load reads in one go. A .m file is MATLAB code, a script or a function that MATLAB runs. Some “CSV to MATLAB” tools write .m code with your numbers typed out, which is fine for a small table but slow and unwieldy for a big one.
Which MAT-file version should I use?
Version 7 for nearly everything: compressed, readable by MATLAB 7.0 (R14) and later, GNU Octave and SciPy. Version 7.3 only for a variable of 2 GB or more: it needs MATLAB R2006b or later, SciPy’s loadmat can’t read it and Octave reads only parts of it.
Why did my column names change?
MATLAB field and variable names must start with a letter and hold only letters, digits and underscores, so a name like “Wind Speed” can’t stay as it is. readtable and table2struct change such names for you, the MegaRows converter turns “London Temperature” into London_Temperature, and the Python code in this guide does the same. Keep the original names in a separate variable, as the converter and the Python code do with names.
Do I lose precision converting a CSV to MAT?
No. A double in a .mat file is stored exactly as the number read from the text, so nothing is lost on the way. Digits are lost only if the CSV was rounded when it was written, or if you choose to save as single.
Sources
The documentation behind what this page says about MATLAB, SciPy, pandas and GNU Octave, besides our own tests (run on 8 October 2026).
MATLAB (MathWorks documentation)
Python
Related tools and guides
- CSV plotterOpen a CSV, Excel, TXT, JSON, MATLAB, Parquet, HDF5 or LabVIEW TDMS file with millions of rows and plot it in several linked charts, with zoom and statistics. Works today.
- Large CSV viewerOpen a CSV with millions of rows in your browser: scroll every row, see each column’s type and statistics, chart any column. Works today.
- MATLAB .mat file viewerOpen v4 to v7.3 .mat files without MATLAB and plot any signal. Works today.
- Parquet viewerOpen large .parquet files, see the schema at once and plot millions of rows, without Python. Works today.
- TDMS viewerOpen .tdms files from LabVIEW, DIAdem and DAQmx loggers: every group, channel and property, each channel plotted against its time. Works today.
- HDF5 viewerOpen .h5, .hdf5 and NetCDF-4 .nc files: every group, dataset and attribute, each group’s datasets plotted against their time. Works today.
- MAT to CSVConvert the signals of a .mat file, v4 to v7.3, to a CSV for Excel or Python, without MATLAB. Works today.
- Parquet to CSVConvert a .parquet file to CSV in your browser, with the columns you pick. Works today.
- TDMS to CSVConvert the channels of a .tdms file to CSV, a group at a time, with its time column, without LabVIEW or Excel add-ins. Works today.
- HDF5 to CSVConvert the datasets of an .h5 or NetCDF-4 file to CSV, a table at a time, with its time column, without Python. Works today.
- Excel to CSVConvert any sheet of an .xlsx or .xlsm workbook to CSV, with the saved results of formulas, real dates and 15 significant digits. Works today.
- JSON to CSVConvert JSON and JSON Lines files to CSV: nested fields become columns named by their path, arrays stay as JSON text. Works today.
- CSV to MATConvert a CSV, Excel, JSON or Parquet file to a MATLAB .mat file: a field per column, with names and units, every row. Works today.
- Merge CSV filesAppend many CSV files into one, even with different columns or separators. Works today.
- Split CSV filesSplit a CSV too big for Excel into parts of 1,000,000 rows, parts of a size you choose, or one file per value of a column. Works today.
- CSV row counterCount the lines in a CSV, TSV or TXT file of any size. Works today.
- CSV statisticsCount, missing values, min, max, mean, standard deviation, RMS and peak to peak of each number column, for the whole file or the rows you zoom into. Works today.
- CSV histogramA histogram of any number column, with automatic bins or 10 to 200 of them, and each bin’s count as a table. Works today.
- CSV too large for Excel?Open every row of a CSV beyond Excel’s 1,048,576-row limit, or work around the limit in six other ways.
- FFT and PSD from a CSVTurn a signal column into an FFT spectrum or a PSD with Grms, with results checked against SciPy. Works today.
- PSD calculatorTurn a vibration column into a power spectral density (Welch) in g²/Hz, with the Grms of any band and a dB view. Works today.
- Spectrogram from CSVSee how the frequencies of a measured signal change over time, as a heat map in dB, with the window and range you choose. Works today.
- FRF and coherencePick an input and an output column and get the frequency response (H1, H2, magnitude and phase) and their coherence. Works today.
- The MegaRows toolboxWhat works today and what is coming next, and why nothing is ever uploaded.
Coming soon to MegaRows: CSV to Excel and PDF reports · Compare CSV versions.