How to load Matlab .mat files in Python | by Ashique Mahmood | Towards Data Science

Matlab is a really popular platform for scientific computing in the academia. I’ve used it my throughout my engineering degree and chances are, you will come across .mat files for datasets released by the universities.
This is a brief post which explains how to load these files using python, the most popular language for machine learning today.
I wanted to build a classifier for detecting cars of different models and makes and so the Stanford Cars Dataset appeared to be a great starting point. Coming from the academia, the annotations for the dataset was in the .mat format. You can get the file used in this post here.
Scipy is a really popular python library used for scientific computing and quite naturally, they have a method which lets you read in .mat files. Reading them in is definitely the easy part. You can get it done in one line of code:
from scipy.io import loadmatannots = loadmat('cars_train_annos.mat')
Well, it’s really that simple. But let’s go on and actually try to get the data we need out of this dictionary.
The loadmat method returns a more familiar data structure, a python dictionary. If we peek into the keys, we’ll see how at home we feel now compared to dealing with a .mat file:
annots.keys()> dict_keys(['__header__', '__version__', '__globals__', 'annotations'])
Looking at the documentation for this dataset, we’ll get to learn what this is really made of. The README.txt gives us the following information:
This file gives documentation for the cars 196 dataset.(http://ai.stanford.edu/~jkrause/cars/car_dataset.html) — — — — — — — — — — — — — — — — — — — — Metadata/Annotations — — — — — — — — — — — — — — — — — — — — Descriptions of the files are as follows:-cars_meta.mat: Contains a cell array of class names, one for each class.-cars_train_annos.mat: Contains the variable ‘annotations’, which is a struct array of length num_images and where each element has the fields: bbox_x1: Min x-value of the bounding box, in pixels bbox_x2: Max x-value of the bounding box, in pixels bbox_y1: Min y-value of the bounding box, in pixels bbox_y2: Max y-value of the bounding box, in pixels class: Integral id of the class the image belongs to. fname: Filename of the image within the folder of images.-cars_test_annos.mat: Same format as ‘cars_train_annos.mat’, except the class is not provided. — — — — — — — — — — — — — — — — — — — — Submission file format — — — — — — — — — — — — — — — — — — — — Files for submission should be .txt files with the class prediction forimage M on line M. Note that image M corresponds to the Mth annotation inthe provided annotation file. An example of a file in this format istrain_perfect_preds.txtIncluded in the devkit are a script for evaluating training accuracy,eval_train.m. Usage is:(in MATLAB)>> [accuracy, confusion_matrix] = eval_train(‘train_perfect_preds.txt’)If your training predictions work with this function then your testingpredictions should be good to go for the evaluation server, assumingthat they’re in the same format as your training predictions.
Our interest is in the 'annotations' variable, as it contains our class labels and bounding boxes. It’s a struct, a data type very familiar to folks coming from a strongly typed language like a flavour of C or java.
A little digging into the object gives us some interesting things to work with:
type(annots[‘annotations’]),annots[‘annotations’].shape>(numpy.ndarray, (1, 8144))type(annots['annotations'][0][0]),annots['annotations'][0][0].shape>(numpy.void, ())
The annotations are stored in a numpy.ndarray format, however the data type for the items inside this array is numpy.void and numpy doesn’t really seem to know the shape of them.
The documentation page for the loadmat method tells us how it loads matlab structs into numpy structured arrays.You can access the members of the structs using the keys:
annots[‘annotations’][0][0][‘bbox_x1’], annots[‘annotations’][0][0][‘fname’]> (array([[39]], dtype=uint8), array(['00001.jpg'], dtype='<U9'))
So now that we know how to access the members of the struct, we can iterate through all of them and store them in a list:
[item.flat[0] for item in annots[‘annotations’][0][0]]> [39, 116, 569, 375, 14, '00001.jpg']
Here, we can use the flat method to squeeze the value out of the array.
Now that we know how to deal with matlab files in python, let’s convert it into a pandas data frame. We can do so easily using a list of lists:
data = [[row.flat[0] for row in line] for line in annots[‘annotations’][0]]columns = [‘bbox_x1’, ‘bbox_y1’, ‘bbox_x2’, ‘bbox_y2’, ‘class’, ‘fname’]df_train = pd.DataFrame(data, columns=columns)
Finally, familiar territory!
The code for this post can be found here.