Extracting a matrix from a cell vector in MATLAB - matlab

A sparse matrix is a large matrix with almost all elements of the same
value (typically zero). The normal representation of a sparse matrix
takes up lots of memory when the useful information can be captured
with much less. A possible way to represent a sparse matrix is with a
cell vector whose first element is a 2-element vector representing the
size of the sparse matrix. The second element is a scalar specifying
the default value of the sparse matrix. Each successive element of the
cell vector is a 3-element vector representing one element of the
sparse matrix that has a value other than the default. The three
elements are the row index, the column index and the actual value.
Write a function called "sparse2matrix" that takes a single input of a
cell vector as defined above and returns the output argument called
"matrix", the matrix in its traditional form. Consider the following
run:
cellvec = {[2 3], 0, [1 2 3], [2 2 -3]};
matrix = sparse2matrix(cellvec)
matrix =
0 3 0
0 -3 0
Good morning/afternoon/night, everyone
I was wondering if you could help me with this.
I am trying to complete this, but I am not sure how to deal with this. I understand that I am interested in the first part of the cell vector, but I am not sure about how to tell Matlab I need that. This is my code:
function matrix = sparse2matrix(x)
A = [2 3];
B = 0;
C = [1, 2, 3];
x = {A, 0, C};
matrix = cell2mat(x);
end
The result of this code is different from the result I showed above.
I am not getting the right answer and honestly I do not know what to do, so I would appreciate if you guide me a little bit.

Not the most elegant way of doing it but it gets the job done. Uses the cell2mat() function and indexing to grab the necessary values for each step. A for-loop is then used to obtain the 3-element vectors that is used to change the array values from the default values in the respective indices.
cellvec = {[2 3], 0, [1 2 3], [2 2 -3]};
[matrix] = sparse2matrix(cellvec)
function [matrix] = sparse2matrix(x)
Matrix_Size = cell2mat(x(1,1));
Matrix_Height = Matrix_Size(1);
Matrix_Width = Matrix_Size(2);
Default_Value = cell2mat(x(1,2));
matrix = Default_Value*ones(Matrix_Height,Matrix_Width);
Triplets = x(3:end);
Number_Of_Triplets = length(Triplets);
for Triplet_Index = 1: Number_Of_Triplets
Current_Triplet = cell2mat(Triplets(1,Triplet_Index));
Row = Current_Triplet(1,1);
Column = Current_Triplet(1,2);
Value = Current_Triplet(1,3);
matrix(Row,Column) = Value;
end
end
Explanation:
The first line in the local function sparse2matrix() indicates to retrieve row 1, column 1 of the cell array x and then convert it to a matrix. The conversion is done using the cell2mat() function.
Matrix_Size = cell2mat(x(1,1));
Results in:
Matrix_Size = [2 3]
The following line indicates to grab index 1 of the previous array Matrix_Size. This will be used to determine the height of the output array matrix. This is done in a similar fashion to evaluate the Matrix_Width using index 2.
Matrix_Height = Matrix_Size(1);
Results in:
Matrix_Height = 2
Ran using MATLAB R2019b

Related

Append vector to 3d matrix in Matlab

I wish to append a row-vector (and later, also a column vector) to an existing x by y by z matrix. So basically "Add a new row (at the "bottom") for each z in the original 3d matrix. Consider the following short Matlab program
appendVector = [1 2 3 4 5]; % Small matrix for brevity. Actual matrices used are much larger.
origMatrix = ones(5,5,3);
appendMatrix = [origMatrix( ... ); appendVector];
My question is: How do I adress (using Matlab-style matrix adressing, not a "manual" C-like loop) origMatrix( ... ) in order to append the vector above? Feel free to also include a suggestion on how to do the same operation for a column-vector (I am thinking that the correct way to do the latter is to simply use the '-operator in Matlab).
A "row" in a 3D matrix is actually a multi-dimensional array.
size(origMatrix(1,:,:))
% 5 3
So to append a row, you would need to append a 5 x 3 array.
toAppend = rand(5, 3);
appendMatrix = cat(1, origMatrix, toAppend);
You could append just a 5 element vector and specify an index for the third dimension. In this case, the value for the "row" for all other indices in the third dimension would be filled with zeros.
appendVector = [1 2 3 4 5];
origMatrix = ones(5,5,3);
appendMatrix = origMatrix;
appendMatrix(end+1, :, 1) = appendVector;
If instead, you want to append the same vector along the third dimension, you could use repmat to turn your vector into a 1 x 5 x 3 array and then append that.
appendVector = repmat([1 2 3 4 5], 1, 1, size(origMatrix, 3));
appendMatrix = cat(1, origMatrix, appendVector);

Print the value of a multidimensional array with the output as compatible matlab code

For matrices with dimensions equal or less then 2 the command is:
For instance:
>> mat2str(ones(2,2))
ans =
[1 1;1 1]
However, as the help states, this does not work for higher dimensions:
>> mat2str(rand(2,2,2))
Error using mat2str (line 49)
Input matrix must be 2-D.
How to output matrices with higher dimensions than 2 with that is code compatible, without resorting to custom made for loops?
This isn't directly possible because there is no built-in character to represent concatenation in the third dimension (an analog to the comma and semicolon in 2D). One potential workaround for this would be to perform mat2str on all "slices" in the third dimension and wrap them in a call to cat which, when executed, would concatenate all of the 2D matrices in the third dimension to recreate your input matrix.
M = reshape(1:8, [2 2 2]);
arrays = arrayfun(#(k)mat2str(M(:,:,k)), 1:size(M, 3), 'uni', 0);
result = ['cat(3', sprintf(', %s', arrays{:}), ')'];
result =
'cat(3, [1 3;2 4], [5 7;6 8])'
isequal(eval(result), M)
1
UPDATE
After thinking about this some more, a more elegant solution is to flatten the input matrix, run mat2str on that, and then in the string used to recreate the data, we utilize reshape combined with the original dimensions to provide a command which will recreate the data. This will work for any dimension of data.
result = sprintf('reshape(%s, %s);', mat2str(M(:)), mat2str(size(M)));
So for the following 4D input
M = randi([0 9], 1, 2, 3, 4);
result = sprintf('reshape(%s, %s);', mat2str(M(:)), mat2str(size(M)));
'reshape([6;9;4;6;5;2;6;1;7;2;1;7;2;1;6;2;2;8;3;1;1;3;8;5], [1 2 3 4]);'
Now if we reconstruct the data using this generated string, we can ensure that we get the correct data back.
Mnew = eval(result);
size(Mnew)
1 2 3 4
isequal(Mnew, M)
1
By specifying both the class and precision inputs to mat2str, we can even better approximate the input data including floating point numbers.
M = rand(1,2,3,4,5);
result = sprintf('reshape(%s, %s);', mat2str(M(:),64,'class'), mat2str(size(M)));
isequal(eval(result), M)
1

Treat each row of a matrix as a vector in a MATLAB function

Say I have a nxm matrix and want to treat each row as vectors in a function. So, if I have a function that adds vectors, finds the Cartesian product of vectors or for some reason takes the input of several vectors, I want that function to treat each row in a matrix as a vector.
This sounds like a very operation in Matlab. You can access the ith row of a matrix A using A(i, :). For example, to add rows i and j, you would do A(i, :) + A(j, :).
Given an nxm matrix A:
If you want to edit a single column/row you could use the following syntax: A(:, i) for the ith-column and A(i, :) for ith-row.
If you want to edit from a column/row i to a column/row j, you could use that syntax: A(:, i:j) or A(i:j, :)
If you want to edit (i.e.) from the penultimate column/row to the last one, you could you: A(:, end-1:end) or A(end-1:end, :)
EDIT:
I can't add a comment above because I don't have 50 points, but you should post the function setprod. I think you should be able to do what you want to do, by iterating the matrix you're passing as an argument, with a for-next statement.
I think you're going to have to loop:
Input
M = [1 2;
3 4;
5 6];
Step 1: Generate a list of all possible row pairs (row index numbers)
n = size(M,1);
row_ind = nchoosek(1:n,2)
Step 2: Loop through these indices and generate the product set:
S{n,n} = []; //% Preallocation of cell matrix
for pair = 1:size(row_ind,1)
p1 = row_ind(pair,1);
p2 = row_ind(pair,2);
S{p1,p2} = setprod(M(p1,:), M(p2,:))
end
Transform the matrix into a list of row vectors using these two steps:
Convert the matrix into a cell array of the matrix rows, using mat2cell.
Generate a comma-separated list from the cell array, using linear indexing of the cell contents.
Example: let
v1 = [1 2];
v2 = [10 20];
v3 = [11 12];
M = [v1; v2; v3];
and let fun be a function that accepts an arbitrary number of vectors as its input. Then
C = mat2cell(M, ones(1,size(M,1)));
result = fun(C{:});
is the same as result = fun(v1, v2, v3).

MATLAB: Adding vectors to a matrix and counting the number of non zeros

I have the following problem:
A = [] % matrix
Q = [] % number on non zero in each row
for i=1:10
% I generate a path with name path
% add the path to the matrix A
end
% count number on non zero numbers in each row
How can I do that?
Here is an illustrative example:
If the path in first iteration was: path = [ 1 2 3 4 ]
In the second iteration: path = [ 4 5 6 ]
In third iteration: path = [ 7 2 ]
Then A should be like this:
A =
[1 2 3 4 ,
4 5 6 0 ,
7 2 0 0 ]
After, I would like a vector to show me the number of numbers different than zero in each row like so:
Q = [4 3 2]
Given your comments, this is what I would suggest that you do:
Because the actual paths are not known a priori, I would suggest that you store all of the paths in a cell array. Given your for loop condition, I'm assuming that there will be 10 paths. The actual paths we don't know, but we know there will be 10.
Write some code that will figure out the maximum length of path as this will dictate the size of our matrix A.
Create the matrix A then store of all the paths in, with zeroes appended at the end of each path should they not be the size of the maximum length of the path.
Using the matrix A, figure out how many non-zeroes there are.
Without any further ado, here is the code you are seeking. With each step, I will explain what I am doing to get the desired step.
Step #1
pathCellArray = cell(10, 1);
for i = 1 : 10
%// Insert path generation code here...
%// Assume that the variable is called
%// 'path' that stores this path
%//..
pathCellArray{i} = path;
end
This should be self-explanatory. For each path that you generate, store it in its corresponding cell. The reason why we are placing each path in a cell is because the length of each path is variable. We are also doing this so we can figure out how many columns we need to allocate for A. Also take note that I am creating a cell array of columns. The reason why is because when we do a reconstruction and solve for A, it will make things easier.
Step #2
%// Use cellfun that takes in a cell array and we will
%// generate an array as output that contains the
%// size of each path. We will find the maximum of
%// all of these that will dictate our size of A
lengthArray = cellfun(#length, pathCellArray);
numCols = max(lengthArray);
Now this is something cool. You probably have never seen cellfun before. How cellfun works is that we specify a custom function, and cellfun will apply this function to every element to this cell. For the output of cellfun the i'th position of the output is the output of the custom function for each i'th position of the input cell array. As such, what we are doing here is that we are generating a length array (stored as lengthArray) that goes through each cell and figures out how big the path is. lengthArray will thus store how long each path is. Once we do that, we figure out what the longest path is so we know how big A will be.
Step #3
pathCellArrayAppend = cellfun(#(x) [x zeros(1,numCols-length(x))], ...
pathCellArray, 'UniformOutput', false);
A = cell2mat(pathCellArrayAppend);
This code is probably the most complex out of all that you have dealt with. What I am basically doing here is taking that cell array of paths that I created, and generating a new cell array where each element in the cell (which is our path) gets appended with zeroes up until the length of the longest path. If you look, the custom function we have generated basically creates the array that first stores the path that we want, then zero-pads the rest of the array with zeroes. We have to specify UniformOutput to false as because the output of the the custom function is no longer a single value. It is an array as the output, which is why this flag is set to false. The output of this call to cellfun will generate 10 cell arrays where each element is a path and is zero-padded with zeroes. We then call cell2mat to convert this cell array back to matrix form, and A is your result.
Step #4
Q = cellfun(#(x) sum(x ~= 0), pathCellArrayAppend);
We thus call cellfun again. This time we take a look at our cell array that has all of the paths that are zero-padded. We go through each cell, and count how many non-zero entries there are. Q will store this result. Note that we don't need to set UniformOutput to false as the output of the custom function is a single number.
Using your example that you have provided, here is how the code will look with their corresponding outputs:
%// Step #1 - Simulated
p1 = [1 2 3 4];
p2 = [4 5 6];
p3 = [7 2];
pathCellArray = {p1; p2; p3};
%// Step #2
lengthArray = cellfun(#length, pathCellArray)
>> lengthArray =
4
3
2
numCols = max(lengthArray)
>> numCols =
4
%// Step #3
pathCellArrayAppend = cellfun(#(x) [x zeros(1,numCols-length(x))], ...
pathCellArray, 'UniformOutput', false);
A = cell2mat(pathCellArrayAppend)
>> A =
1 2 3 4
4 5 6 0
7 2 0 0
%// Step #4
Q = cellfun(#(x) sum(x ~= 0), pathCellArrayAppend)
>> Q =
4
3
2
NB: You could simply use Step #2 as your Q, provided that your paths are non-zero. If there are, then you need to go right to Step #4.

Vectorizing the Notion of Colon (:) - values between two vectors in MATLAB

I have two vectors, idx1 and idx2, and I want to obtain the values between them. If idx1 and idx2 were numbers and not vectors, I could do that the following way:
idx1=1;
idx2=5;
values=idx1:idx2
% Result
% values =
%
% 1 2 3 4 5
But in my case, idx1 and idx2 are vectors of variable length. For example, for length=2:
idx1=[5,9];
idx2=[9 11];
Can I use the colon operator to directly obtain the values in between? This is, something similar to the following:
values = [5 6 7 8 9 9 10 11]
I know I can do idx1(1):idx2(1) and idx1(2):idx2(2), this is, extract the values for each column separately, so if there is no other solution, I can do this with a for-loop, but maybe Matlab can do this more easily.
Your sample output is not legal. A matrix cannot have rows of different length. What you can do is create a cell array using arrayfun:
values = arrayfun(#colon, idx1, idx2, 'Uniform', false)
To convert the resulting cell array into a vector, you can use cell2mat:
values = cell2mat(values);
Alternatively, if all vectors in the resulting cell array have the same length, you can construct an output matrix as follows:
values = vertcat(values{:});
Try taking the union of the sets. Given the values of idx1 and idx2 you supplied, run
values = union(idx1(1):idx1(2), idx2(1):idx2(2));
Which will yield a vector with the values [5 6 7 8 9 10 11], as desired.
I couldn't get #Eitan's solution to work, apparently you need to specify parameters to colon. The small modification that follows got it working on my R2010b version:
step = 1;
idx1 = [5, 9];
idx2 = [9, 11];
values = arrayfun(#(x,y)colon(x, step, y), idx1, idx2, 'UniformOutput', false);
values=vertcat(cell2mat(values));
Note that step = 1 is actually the default value in colon, and Uniform can be used in place of UniformOutput, but I've included these for the sake of completeness.
There is a great blog post by Loren called Vectorizing the Notion of Colon (:). It includes an answer that is about 5 times faster (for large arrays) than using arrayfun or a for-loop and is similar to run-length-decoding:
The idea is to expand the colon sequences out. I know the lengths of
each sequence so I know the starting points in the output array. Fill
the values after the start values with 1s. Then I figure out how much
to jump from the end of one sequence to the beginning of the next one.
If there are repeated start values, the jumps might be negative. Once
this array is filled, the output is simply the cumulative sum or
cumsum of the sequence.
function x = coloncatrld(start, stop)
% COLONCAT Concatenate colon expressions
% X = COLONCAT(START,STOP) returns a vector containing the values
% [START(1):STOP(1) START(2):STOP(2) START(END):STOP(END)].
% Based on Peter Acklam's code for run length decoding.
len = stop - start + 1;
% keep only sequences whose length is positive
pos = len > 0;
start = start(pos);
stop = stop(pos);
len = len(pos);
if isempty(len)
x = [];
return;
end
% expand out the colon expressions
endlocs = cumsum(len);
incr = ones(1, endlocs(end));
jumps = start(2:end) - stop(1:end-1);
incr(endlocs(1:end-1)+1) = jumps;
incr(1) = start(1);
x = cumsum(incr);