Functional matrix vectorization in MATLAB - matlab

To vectorize a matrix in MATLAB, you can execute this simple command:
A = reshape(1:9, 3, 3)
% A =
% [1 4 7]
% [2 5 8]
% [3 6 9]
b = A(:)
% b = [1 2 3 4 5 6 7 8 9]'
But how about if you have a matrix that you want to first slice, then vectorize? How do you go about doing this without assigning to a temporary variable?
Let's say A is now:
A = reshape(1:27, 3, 3, 3)
% A(:,:,1) =
% [1 4 7]
% [2 5 8]
% [3 6 9]
% A(:,:,2) =
% [10 13 16]
% [11 14 17]
% [12 15 18]
% A(:,:,3) =
% [19 22 25]
% [20 23 26]
% [21 24 27]
If you run
b = A(:,:,1)(:)
% Error: ()-indexing must appear last in an index expression.
Is there some function, vectorize(A) that gives this functionality?
b = vectorize(A(:,:,1))
% b = [1 2 3 4 5 6 7 8 9]'
Or if not a function, is there an alternative method than
tmp = A(:,:,1)
b = tmp(:)
Thanks in advance for the guidance!

If only elegance could be measured, but here's one to get through the night -
A(1:numel(A(:,:,1))).'

This is a function that I've seen many seasoned Matlab users add to their code hoard by hand:
function A = vectorize(A)
A = A(:);
% save this code as vectorize.m
Once you've got vectorize.m on your path, then you can do what you want in one line.
You can define the function inline if you prefer:
vectorize = inline( 'A(:)' );
but then of course you have to ensure that that's in memory for every session.
If for some reason it's unacceptable to write and save your own functions to disk (if so, I wonder how your sanity ever survives using Matlab, but it takes all sorts...) then the following code snippet is a one-liner that uses only builtins, works for arbitrarily high-dimensional A, and is still not too unreadable:
reshape( A, numel(A), 1 )
Note that this, in common with (:) but contrary to what you assume in your question, produces a column vector. Its disadvantage is that A must already be assigned in the workspace, and that assignment may require one extra line. By contrast, the function version can work even on unnamed outputs of other operations—e.g.:
A = vectorize( randn(5) + magic(5) )

One-liner for arbitrary indices.
i=3;
A((i-1)*numel(A(:,:,i))+(1:numel(A(:,:,i)))).'

Related

Element-wise mutiplication .* of vectors gives matrix in Matlab

Given two vectors
a = 1:3;
b = 2:4;
it's well known that the element-wise mutiplication a.*b produces
[ 2 6 12 ]
Calling that result c, we have c(i) = a(i)*b(i)
But I don't understand how a.*b', b'.*a and b'*a all produce
[ 2 4 6
3 6 9
4 8 12 ]
For the matrix multiplication b'*a, we know c(i,j) = b(i)*a(j).
But why do the other two also produce the same result?
Due to implicit expansion (introduced in 2016b) it's essentially the same as using bsxfun.
But what does that mean?
Setup:
a = 1:3;
b = 2:4;
All MATLAB versions:
c = a.*b;
% c = [2 6 12], element-wise multiplication c(j) = a(j)*b(j)
c = b'*a;
% c = [2 4 5; 3 6 9; 4 8 12]
% standard matrix multiplication of vectors
% c(i,j) = a(i) + b(j)
c = bsxfun(#times, b', a)
% c = [2 4 5; 3 6 9; 4 8 12]
% bsxfun applies the function (in this case #times) to b' and a
By definition, bsxfun "applies the element-by-element binary operation specified by the function handle fun to arrays A and B, with singleton expansion enabled". This means that singleton dimensions (dimensions whose size is 1) are expanded row-wise/column-wise to match the size of the other argument supplied to bsxfun.
So, bsxfun(#times, b', a) is equivalent to
% b' in singleton in the 2nd dimension, a is singleton in the 1st dimension
% Use repmat to perform the expansion to the correct size
repmat(b', 1, size(a,2)) .* repmat(a, size(b',1), 1)
% Equivalent to...
repmat(b', 1, 3) .* repmat(a, 3, 1)
% Equivalent to...
[2 2 2; 3 3 3; 4 4 4] .* [1 2 3; 1 2 3; 1 2 3]
% = [2 4 5; 3 6 9; 4 8 12] the same as b'*a
Before R2016b
c = a.*b'; % Error: Matrix dimensions must agree.
c = b'.*a; % Error: Matrix dimensions must agree.
Since R2016b
Newer MATLAB versions use implicit expansion, which basically means that a bsxfun equivalent is called 'under the hood' if necessary for a valid operation.
c = a.*b'; % [2 4 5; 3 6 9; 4 8 12] the same as bsxfun(#times, a, b')
c = b'.*a; % [2 4 5; 3 6 9; 4 8 12] the same as bsxfun(#times, b', a)
% These two are equivalent also because order of operations is irrelevant
% We can see this by thinking about the expansion discussed above
As you've noticed, this can be confusing if you don't keep track of your vector orientations! If you ever want to get a 1D output (without expansion), then you can ensure your inputs are 1D column vectors by using the colon operator like so
c = a(:).*b(:); % c = [2; 6; 12] always a column vector
The examples you listed are all element-wise multiplication.
a.*b' will give error in earlier matlab, while it performs
bsxfun(#times, a, b')
in Matlab since R2016b. This should explain the identical result for a.*b', b'.*a and b'*a.
a * b' will be matrix multiplication (inner dimension match).

Evaluating MATLAB Quadratic rBform

I have created a planar piecewise biarc curve in MATLAB using the rscvn function. I have been able to plot it as follows:
p = [0 1 2 3; 2 6 3 9];
B = rscvn(p)
fnplt(B)
hold on
scatter([0 1 2 3],[2 6 3 9]);
hold off
Unfortunately I can't for the life of me figure out how to evaluate the function B for an arbitrary position, say 2.6.
How should I attempt this in MATLAB?
You can evaluate a function from the curve fitting toolbox using the fnval function.
See https://www.mathworks.com/help/curvefit/fnval.html
Example code
p = [0 1 2 3; 2 6 3 9];
B = rscvn(p);
fnval(B,2.6)
Output
ans =
1.8526
5.1884
Edit From your comment and the format of your data I assume you are actually looking to estimate a continuous function from your data. In that case you can use.
p = [0 1 2 3; 2 6 3 9];
C = csapi(p(1,:), p(2,:));
fnplt(C)
hold on
scatter([0 1 2 3],[2 6 3 9]);
hold off
fnval(C,2.6)
Output
ans =
4.4960

Defining a multivariable function for vector inputs

I am a bit new in using Matlab, and I have a question about defining a multivariable function for vector input.
If the function is a single function, say f(t), I know how to make it for vector input. The general way is to use arrayfun after defining a f(t). How about for multivariable function, say f(x,y)? What I want to do is to get two inputs, say [1 2 3] for x and [4 5 6 7] for y (dimension may be different, but both of them are either column vector or row vector) so that I can calculate to give
[f(1,4),f(1,5),f(1,6),f(1,7);
f(2,4),f(2,5),f(2,6),f(2,7);
f(3,4),f(3,5),f(3,6),f(3,7)]
The difficulty is that the vector input for x and y may not be in the same dimension.
I understand it may be difficult to illustrate if I do not have an example of f(x,y). For my use of f(x,y), it may be very complicated to display f(x,y). For simplicity, treat f(x,y) to be x^2+y, and once defined, you cannot change it to x.^2+y for vector inputs.
Here is a set of suggestions using ndgrid:
testfun = #(x,y) x^2+y; % non-vectorized form
x = 1:3;
y = 4:7;
[X,Y] = ndgrid(x,y);
% if the function can be vectorized (fastest!):
testfun_vec = #(x,y) x.^2+y; % vectorized form
A = testfun_vec(X,Y);
% or without ndgrid (also super fast):
B = bsxfun(testfun_vec,x.',y); % use the transpose to take all combinations
% if not, or if it's not bivariate operation (slowest):
C = arrayfun(testfun,X(:),Y(:));
C = reshape(C,length(x),length(y));
% and if you want a loop:
D = zeros(length(x),length(y));
for k = 1:length(X(:))
D(k) = testfun(X(k),Y(k));
end
Which will output for all cases (A,B,C and D):
5 6 7 8
8 9 10 11
13 14 15 16
As mentioned already, if you can vectorize your function - this is the best solution, and if it has only two inputs bsxfun is also a good solution. Otherwise if you have small data and want to keep your code compact use arrayfun, if you are dealing with large arrays use an un-nested for loop.
Here is the code using for loops and inline functions:
x = [1 2 3];
y = [4 5 6 7];
f = #(x,y) x^2 +y;
A = zeros(length(x), length(y));
for m = 1:length(x)
for n = 1:length(y)
A(m, n) = f(x(m), y(n));
end
end
disp(A);
Result:
A =
5 6 7 8
8 9 10 11
13 14 15 16
>> x = [1 2 3];
>> y = [4 5 6 7];
>> outValue = foo(x, y);
>> outValue
outValue =
5 6 7 8
8 9 10 11
13 14 15 16
Make this function:
function out = foo(x, y)
for i = 1 : length(x)
for j = 1 : length(y)
out(i, j) = x(i)^2 + y(j);
end
end

The im2col algorithm for ND input

I am trying to write my own im2col algorithm for input dimensions > 2D.
Currently I am looking at the Matlab im2col implementation. However, I cannot find any documentation regarding what is going on for any input of more than 2 dimensions.
I do get an output if I feed in a 3D tensor into the function. However I don't really understand how you get from 2D to ND. The fact that this isn't mentioned in the documentation suggests that its something straightforward, still, I don't get it.
Heck, I dont even understand why the size of the output matrix is the size it is.
Let me just start by saying that im2col is only intended for 2D matrices. The fact that it sometimes worked (and by that I mean returned a result without throwing an error) is just a happy coincidence.
Now I took a peek at edit im2col.m, and without studying the code too much, the first line of each of the distinct and sliding methods should give you an intuition of what's happening:
...
if strcmp(kind, 'distinct')
[m,n] = size(a);
...
elseif strcmp(kind,'sliding')
[ma,na] = size(a);
...
end
...
First recall that [s1,s2] = size(arr) where arr is a 3d array will collapse the size of 2nd and 3rd dimension into one size. Here's the relevant doc size:
[d1,d2,d3,...,dn] = size(X) returns the sizes of the dimensions of the array X, provided the number of output arguments n equals ndims(X). If n < ndims(X), di equals the size of the ith dimension of X for 0<i<n, but dn equals the product of the sizes of the remaining dimensions of X, that is, dimensions n through ndims(X).
So basically for an array of size M-by-N-by-P, the function instead thinks it's a matrix of size M-by-(N*P). Now MATLAB has some quirky indexing rules that lets you do things like:
>> x = reshape(1:4*3*2,4,3,2)
x(:,:,1) =
1 5 9
2 6 10
3 7 11
4 8 12
x(:,:,2) =
13 17 21
14 18 22
15 19 23
16 20 24
>> x(:,:)
ans =
1 5 9 13 17 21
2 6 10 14 18 22
3 7 11 15 19 23
4 8 12 16 20 24
which is what I think ended up happening here. Here is an example to confirm the behavior of im2col on an RGB image:
% normal case (grayscale image)
>> M = magic(5);
>> B1 = im2col(M, [3 3], 'sliding');
% (RGB image)
>> MM = cat(3, M, M+50, M+100);
>> B2 = im2col(MM, [3 3], 'sliding');
>> B3 = im2col(reshape(MM, [5 5*3]), [3 3], 'sliding');
>> assert(isequal(B2,B3))
Note that B2 and B3 are equal, so basically think of the result of im2col on an array arr = cat(3,R,G,B) to be the same as that of arr = cat(2,R,G,B) (concatenated horizontally).
Interestingly, you won't get so lucky with "distinct" blocks method:
>> B1 = im2col(M, [3 3], 'distinct') % works
% ..snip..
>> B2 = im2col(MM, [3 3], 'distinct') % errors
Subscripted assignment dimension mismatch.
Error in im2col (line 59)
aa(1:m,1:n) = a;
Now that we understand what was happening, let's think how to do this properly for 3D arrays.
In my opinion to implement im2col for color images, I would just run it on each color channel separately (each being a 2d matrix), and concatenate the result along the third dimension. So something like this wrapper function:
function B = im2col_rgb(img, sz, varargin)
B = cell(1,size(img,3));
for i=1:size(img,3)
B{i} = im2col(img(:,:,i), sz, varargin{:});
end
B = cat(3, B{:});
end

simple sliding window filter in Matlab

I don't have the package for nlfilter and I didn't quite follow this example.
I have a really simple function fun and I want to apply it to a moving window of an array. The array is Nx1, and I want to look at length k intervals, say. So for N=10 and k=3 and fun = #(x) min(x); I would get
A = [13 14 2 14 10 3 5 9 15 8];
filter(A,k,fun) = [2 2 2 3 3 3 5 8];
Here I only want to look at indices 1,2,3 then 2,3,4 then ... then 8,9,10, so the final sequence is length 7. I can do this easy with a for loop, but I have no idea how to vectorize it for Matlab. Help, please. Thanks.
Here is one very simple and fast way to do it:
>> min([A(1:(end-2)); A(2:(end-1)); A(3:end)], [], 1)
ans =
2 2 2 3 3 3 5 8
EDIT: Since you want a full function...
function running_min = running_min(x, k)
xrep = repmat(x, 1, k);
xrep = reshape([xrep zeros(1, k)], length(x)+1, k);
running_min = min(xrep, [], 2)';
running_min = running_min(1:end-k);
The post you mentioned gave a general solution for building sliding windows (you could control: overlapping vs. distinct, slide step, overlap amount, windows size)
In your case, it is much simpler and can be easily performed with the HANKEL function:
x = [13 14 2 14 10 3 5 9 15 8];
idx = hankel(1:3, 3:length(x))
min( x(idx) )
If you want to build a reusable solution:
function y = myFilter(x,k,fcn)
idx = hankel(1:k, k:length(x));
y = cellfun(fcn, num2cell(x(idx),1));
end
which we use as:
x = [13 14 2 14 10 3 5 9 15 8];
y = myFilter(x, 3, #(x)min(x))
Note I am using CELLFUN in case fcn cannot operate across dimensions in a vectorized manner...