This simple example provides an explanation on some functions I found extremely useful since I have started using MATLAB: cellfun
, arrayfun
. The idea is to take an array or cell class variable, loop through all its elements and apply a dedicated function on each element. An applied function can either be anonymous, which is usually a case, or any regular function define in a *.m file.
Let's start with a simple problem and say we need to find a list of *.mat files given the folder. For this example, first let's create some *.mat files in a current folder:
for n=1:10; save(sprintf('mymatfile%d.mat',n)); end
After executing the code, there should be 10 new files with extension *.mat. If we run a command to list all *.mat files, such as:
mydir = dir('*.mat');
we should get an array of elements of a dir structure; MATLAB should give a similar output to this one:
10x1 struct array with fields:
name
date
bytes
isdir
datenum
As you can see each element of this array is a structure with couple of fields. All information are indeed important regarding each file but in 99% I am rather interested in file names and nothing else. To extract information from a structure array, I used to create a local function that would involve creating temporal variables of a correct size, for loops, extracting a name from each element, and save it to created variable. Much easier way to achieve exactly the same result is to use one of the aforementioned functions:
mydirlist = arrayfun(@(x) x.name, dir('*.mat'), 'UniformOutput', false)
mydirlist =
'mymatfile1.mat'
'mymatfile10.mat'
'mymatfile2.mat'
'mymatfile3.mat'
'mymatfile4.mat'
'mymatfile5.mat'
'mymatfile6.mat'
'mymatfile7.mat'
'mymatfile8.mat'
'mymatfile9.mat'
How this function works? It usually takes two parameters: a function handle as the first parameter and an array. A function will then operate on each element of a given array. The third and fourth parameters are optional but important. If we know that an output will not be regular, it must be saved in cell. This must be point out setting false
to UniformOutput
. By default this function attempts to return a regular output such as a vector of numbers. For instance, let's extract information about how much of disc space is taken by each file in bytes:
mydirbytes = arrayfun(@(x) x.bytes, dir('*.mat'))
mydirbytes =
34560
34560
34560
34560
34560
34560
34560
34560
34560
34560
or kilobytes:
mydirbytes = arrayfun(@(x) x.bytes/1024, dir('*.mat'))
mydirbytes =
33.7500
33.7500
33.7500
33.7500
33.7500
33.7500
33.7500
33.7500
33.7500
33.7500
This time the output is a regular vector of double. UniformOutput
was set to true
by default.
cellfun
is a similar function. The difference between this function and arrayfun
is that cellfun
operates on cell class variables. If we wish to extract only names given a list of file names in a cell 'mydirlist', we would just need to run this function as follows:
mydirnames = cellfun(@(x) x(1:end-4), mydirlist, 'UniformOutput', false)
mydirnames =
'mymatfile1'
'mymatfile10'
'mymatfile2'
'mymatfile3'
'mymatfile4'
'mymatfile5'
'mymatfile6'
'mymatfile7'
'mymatfile8'
'mymatfile9'
Again, as an output is not a regular vector of numbers, an output must be saved in a cell variable.
In the example below, I combine two functions in one and return only a list of file names without an extension:
cellfun(@(x) x(1:end-4), arrayfun(@(x) x.name, dir('*.mat'), 'UniformOutput', false), 'UniformOutput', false)
ans =
'mymatfile1'
'mymatfile10'
'mymatfile2'
'mymatfile3'
'mymatfile4'
'mymatfile5'
'mymatfile6'
'mymatfile7'
'mymatfile8'
'mymatfile9'
It is crazy but very possible because arrayfun
returns a cell which is expected input of cellfun
; a side note to this is that we can force any of those functions to return results in a cell variable by setting UniformOutput
to false, explicitly. We can always get results in a cell. We may not be able to get results in a regular vector.
There is one more similar function that operates on fields a structure: structfun
. I have not particularly found it as useful as the other two but it would shine in some situations. If for instance one would like to know which fields are numeric or non-numeric, the following code can give the answer:
structfun(@(x) ischar(x), mydir(1))
The first and the second field of a dir structure is of a char type. Therefore, the output is:
1
1
0
0
0
Also, the output is a logical vector of true
/ false
. Consequently, it is regular and can be saved in a vector; no need to use a cell class.