How to insert a column of characters to a matrix? - matlab

I'd like to insert a column of character to a matrix in MATLAB.
For example, we want to reach from first matrix to the second matrix:
first_matrix = [2 3; 4 5; 1 7]
second_matrix = [c 2 3; c 4 5; c 1 7]
In fact the reason is that, I have a output.txt file from a software. In that file, I should select a matrix in it, and change matrix column order. After doing this, i.e. reach to first_matrix, the output in form of second_matrix should be used in another software. So, finally I should save it in a text file format for second software.

You cannot do with this numeric arrays. The possible ways to do this are:
Using a categorical array i.e.
>> second_matrix = [num2cell(repmat('c',3,1)) categorical(first_matrix)]
ans =
3×3 categorical array
c 2 3
c 4 5
c 1 7
Using a character array i.e.
>> second_matrix = [repmat('c ',3,1) num2str(first_matrix)]
second_matrix =
3×7 char array
'c 2 3'
'c 4 5'
'c 1 7'
Using a string array (requires ≥ R2016b) i.e.
>> second_matrix = [repmat("c",3,1) first_matrix] %in ≥ R2017a
% second_matrix = [repmat(string('c'),3,1) first_matrix] %in ≥ R2016b
second_matrix =
3×3 string array
"c" "2" "3"
"c" "4" "5"
"c" "1" "7"
Using a cell array i.e.
>> second_matrix = [num2cell(repmat('c',3,1)) num2cell(first_matrix)]]
second_matrix =
3×3 cell array
{'c'} {[2]} {[3]}
{'c'} {[4]} {[5]}
{'c'} {[1]} {[7]}
Using a symbolic array (requires Symbolic Math Toolbox) i.e.
>> second_matrix = [repmat(sym('c'),3,1) first_matrix]
second_matrix =
[ c, 2, 3]
[ c, 4, 5]
[ c, 1, 7]

Related

How to vectorize operations on cells?

Given B = {"7" "8" "9"}, I'd like to use its values as respective replacements for the ultimate elements of each cell in A, i.e. transform
A = {{1 2} {3 4} {5 6}}
into
A = {{1 "7"} {3 "8"} {5 "9"}}
In unvectorized form, this can be written as:
nn = cellfun(#numel, A)
for i = 1:numel(a)
A{i}{nn(i)} = B{i};
end
All sub-cells may have the same length, but a solution for sub-cells of arbitrary length would be ideal.
The only solution I found involves making a reshaped copy of A:
nn = cellfun(#numel, A) #=> 2 2 2
A2 = horzcat(A{:}) #=> {1 2 3 4 5 6}
jj = cumsum(nn) #=> 2 4 6
[A2{jj}] = B{:} #=> {1 "7" 3 "8" 5 "9"}
ii = [1 jj(1:end-1)+1] #=> 1 3 5
A = cellslices(A2,ii,jj,2) #=> {{1 "7"} {3 "8"} {5 "9"}
I'd have hoped something like the below would work - but it doesn't:
octave:1> [[A{:}]{ii}] = B{:}
error: invalid lvalue function called in expression
Is there a way to vectorize this operation?
As learned from the comments that you actually just want to avoid an explicit for/while loop and are okay with cellfun (the use of which is not vectorisation), the process can be replicated with cellfun as follows:
A = cellfun(#(k) {A{k}{1:end-1} B{k}}, num2cell(1:numel(A)), 'un', 0);
or with arrayfun as:
A = arrayfun(#(k) {A{k}{1:end-1} B{k}}, 1:numel(A), 'un', 0);

How to merge two 4D matrices?

I plan to merge two matrix using matlab.
A is M*N*3*P and B is M*N*3*Q.
how to get a matrix, which is M*N*3* (P+Q)?
Is there a function available?
The function you are looking for is called cat - "Concatenate arrays along specified dimension".
C = cat(dim, A, B) concatenates the arrays A and B along array
the dimension specified by dim. The dim argument must be a real,
positive, integer value.
In your case C = cat(4, A, B) does the trick. The dim=4 as A and B are both 4-dimensional and you want to concatenate in the 4th dimension.
Example:
A = ones(4,5,3,7);
B = zeros(4,5,3,17);
C = cat(4, A, B);
>> size(A)
ans =
4 5 3 7
>> size(B)
ans =
4 5 3 17
>> size(C)
ans =
4 5 3 24

Find row-wise combinations of a 2 dimensional matrix

I have a matrix:
X = [2,6,1; 3,8,1; 4,7,1; 6,2,1; 6,4,1; 7,3,1; 8,5,1; 7,6,1];
I want to find all row-wise combinations of X. i.e.
A(1) = [2, 6, 1; 3, 8, 1; 4, 7, 1]
A(2) = [2, 6, 1; 3, 8, 1; 6, 2, 1]
:
:
:
Here's what I've tried:
X = [2,6,1; 3,8,1; 4,7,1; 6,2,1; 6,4,1; 7,3,1; 8,5,1; 7,6,1];
p = 3
[m, n] = size(X);
comb = combnk(1:m, p);
[s, t] = size(comb);
c = [X(comb(:,1), :, :) X(comb(:,2), :, :) X(comb(:,3), :, :)];
This gives me a matrix like:
c = 2 6 1 3 8 1 4 7 1
2 6 1 3 8 1 6 2 1
2 6 1 3 8 1 6 4 1
I want to apply the concatenate matrix option to obtain c to make it dynamic depending on value of p but I'm not sure how to use it. I don't want to use For loops. Please help me out.
This is fully vectorized, so it should be fast:
n = 3; %// number of rows to pick each time
ind = reshape(nchoosek(1:size(X,1), n).', [], 1); %'// indices of combinations
A = permute(reshape(X(ind,:).', size(X,2), n, []), [2 1 3]);
The result is
A(:,:,1)
ans =
2 6 1
3 8 1
4 7 1
A(:,:,2)
ans =
2 6 1
3 8 1
6 2 1
etc.
Should you need the result in the form of a cell array, you can convert A from 3D-array to cell array this way:
A = mat2cell(A, size(A,1), size(A,2), ones(1,size(A,3)));
Your thinking is pretty close. This code does the job. I put comments in code, which should be easy to read.
X = [2,6,1; 3,8,1; 4,7,1; 6,2,1; 6,4,1; 7,3,1; 8,5,1; 7,6,1];
p = 3;
%// List all combinations choosing 3 out of 1:8.
v = nchoosek(1:size(X,1), p);
%// Use each row of v to create the matrices, and put the results in an cell array.
%// This is the A matrix in your question.
A = arrayfun(#(k)X(v(k,:), :), 1:size(v,1), 'UniformOutput', false);
%// And you can concatenate A vertically to get c.
flatA = cellfun(#(x)reshape(x, 1, []), A, 'UniformOutput', false);
c = vertcat(flatA{:});
PS: From my understanding I thought the result you wanted was A, which is an easy to use cell array. But I added an extra step to get c exactly as in your question just in case.
Disclaimer: arrayfun and cellfun are pretty much equivalent to for loop in terms of performance.
You can do it using reshape and a bunch of transposes since Matlab is column-major ordered:
c = reshape(X(comb',:)',9,[])'
or if you want a 3D matrix:
A = permute(reshape(X(comb',:)',3,3,[])', [2,1,3])

How to loop two vectors in MATLAB?

In python one can use zip to loop multiple vectors or enumerate to get the current index of the looped vector like so
one = ['A', 'B', 'C']
two = [1, 2, 3]
for i, j in zip(one, two):
print i, j
for j, i in enumerate(one):
print i, two[j]
Gives
>>>
A 1
B 2
C 3
A 1
B 2
C 3
In MATLAB it's possible to do
one = {'A' 'B' 'C'};
two = [1 2 3];
for i = 1:1:length(one)
printf('%s %i\n', one{i}, two(i));
endfor
j = 1;
for i = one
printf('%s %i\n', i{1}, two(j));
j = j + 1;
endfor
giving
A 1
B 2
C 3
A 1
B 2
C 3
So is one of those two options the common way how one would do it in MATLAB, i. e. to loop through several vectors "in parallel" or is there another, maybe better way?
Bonus:
two = [1 2 3];
two = [1, 2, 3];
Both of these lines give the same output in the upper MATLAB program. Whats the difference?
Using printf, or fprintf in Matlab, is pretty good. The Matlab code for your first approach is
one = {'A' 'B' 'C'};
two = [1 2 3];
for ii = 1:length(one)
fprintf('%s %i\n', one{ii}, two(ii));
end
It's also possible to put the strings into a cell array, without any for loop.
s = cellfun(#(a,b) [a,' ',b], one', ...
arrayfun(#num2str, two', 'UniformOutput', false),....
'UniformOutput', false)
Bonus:
>> A = [1;2;3]
A =
1
2
3
>> A = [1 2 3]
A =
1 2 3
>> A = [1,2,3]
A =
1 2 3
>> A = [1,2,3;4 5 6;7,8 9]
A =
1 2 3
4 5 6
7 8 9
>>
Bonus 2:
Using i and j is bad. See - Using i and j as variables in Matlab

How to assemble small n-d arrays into a larger n-d array?

The code below produces a cell array containing n (=210) 2x3x4-shaped n-d arrays.
n = prod(5:7);
makendarray = #(i) reshape(1:24, 2:4) + (i * 1000);
ndarrays = cellfun(makendarray, num2cell(1:n), 'un', 0);
For each i ∈ {1, … ,n}, the contents of the i-th n-d array are all integers whose initial digits match those of i. For example, for i = 123:
>> ndarrays{123}
ans(:,:,1) =
123001 123003 123005
123002 123004 123006
ans(:,:,2) =
123007 123009 123011
123008 123010 123012
ans(:,:,3) =
123013 123015 123017
123014 123016 123018
ans(:,:,4) =
123019 123021 123023
123020 123022 123024
I want to assemble all these n-d arrays into a larger [5x6x7x2x3x4 double] n-d array X in such a way that expressions of the form X(i,j,k,:,:,:) will correspond to one of the original smaller n-d arrays. This last requirement is the one that's giving me difficulty.
I have no problem producing the larger [5x6x7x2x3x4 double] n-d array from the smaller ones, but expressions of the form X(i,j,k,:,:,:) do not produce one of the original smaller n-d arrays.
Below is an example of one of the things I've tried; the last output should match below should match the output shown for ndarrays{123} above, but doesn't:
>> X = reshape(cell2mat(ndarrays), [5:7 2:4]);
>> [idx{1:3}] = ind2sub(5:7, 123);
>> squeeze(X(idx{:}, :, :, :))
ans(:,:,1) =
62001 62003 62005
167001 167003 167005
ans(:,:,2) =
62007 62009 62011
167007 167009 167011
ans(:,:,3) =
62013 62015 62017
167013 167015 167017
ans(:,:,4) =
62019 62021 62023
167019 167021 167023
EDIT: OK, by (pretty much blind) trial-and-error I found that the following does the trick:
tmp = cellfun(#(c) reshape(c, [1 24]), ndarrays, 'un', 0);
X = reshape(cat(1, tmp{:}), [5:7 2:4]);
IOW: linearize the subarrays before passing them to cat(1, ...). I'm surprised that it's necessary to explicitly perform this linearization step: it's what I expect MATLAB to do by default (in cell2mat, for example).
Be that as it may, is there a better (or at least clearer/easier to understand) way to achieve the same effect?
(BTW, for this problem, the initial shape of ndarray is given and not subject to change. IOW, solutions that require modifying the makesubarray function in the example do not fit the situation I'm dealing with.)
EDIT2: In reference to Luis Mendo's answer, here's the output (copy-pasted verbatim from my MATLAB workspace) of a small script (with echo on), showing the sizes of various items:
n = prod(5:7);
makendarray = #(i) reshape(1:24, 2:4) + (i * 1000);
ndarrays = cellfun(makendarray, num2cell(1:n), 'un', 0);
size(ndarrays)
ans =
1 210
size(permute(ndarrays, [2 3 4 1]))
ans =
210 1
size(cell2mat(permute(ndarrays, [2 3 4 1])))
ans =
420 3 4
echo off;
You can achieve it with a little bit of permute and reshape. The logic of this approach can be followed by observing size(X) at the end of each step (indicated in the comments):
X = cell2mat(permute(ndarrays(:), [2 3 4 1])); %// size [2 3 4 210]
X = permute(X, [4 5 6 1 2 3]); %// size [210 1 1 2 3 4]
X = reshape(X, [7 6 5 2 3 4]); %// size [7 6 5 2 3 4]
X = permute(X, [3 2 1 4 5 6]); %// size [5 6 7 2 3 4]