How to combine two matrices column wise to strings separated by comma? - matlab

I have two matrices
A = [ 1 3
4 3]
B = [ 2 1
4 1 ]
I want to combine A and B to produce the string array
C = [ "1,2" "3,1"
"4,1" "3,1" ]
How can I do this in MATLAB? I tried it this way
for i = 1: 4;
for j = 1: 4;
fprintf('%0.2f,%0.2f\n',A(i,j),B(i,j) )
end
end
Appreciate your suggestions !

A = [1 3; 4 3];
B = [2 1; 4 1];
C = A + "," + B
C =
% 2×2 string array
% "1,2" "3,1"
% "4,4" "3,1"

The first thing to note, is that there is a difference between strings "string" and character arrays 'character array'. Whereas strings are one entity, the character array is an array of characters.
Thus you can make the following assignment
A(1) = "Hello";
but not
B(1) = 'Hello';
because the B(1) is one value, and 'Hello' is 5 values (H,e,l,l,o).
Secondly, you cannot use fprintf as you suggest in the comments as it only prints (as in its name) and the variable returned by fprintf is the number of characters printed. Instead, to construct the string use strcat together with num2str, such that you get:
A = rand(2); %some matrices
B = rand(2);
for i = 1:2
for j = 1:2
C(i,j) = strcat(num2str(A(i,j)),",",num2str(B(i,j)));
end
end
EDIT: If you are anyway going to interchange the comma for \pm in LaTeX, you can just do it when constructing C by using
C(i,j) = strcat(num2str(A(i,j)),"\pm",num2str(B(i,j)))
instead.

Related

Merge array rows based on the first digit in a column

I have two arrays, A and B. The first digit of each row is the serial number.
How do I combine A and B to have an array C, such that all rows in A with the same serial number in B are concatenated horizontally?
A = [ 12345;
47542;
32673;
65436;
75343;
23496;
54765 ]
B = [ 23566;
33425;
65438;
75354 ]
y = ismember(A(:,1), B(:,1), 'rows');
t=find(y);
C= [A(t,1:12),B(t,1:12)];
I need C to be:
C = [ 12345, 00000;
23496, 23566;
32673, 33425;
47542, 00000;
54765, 00000;
65436, 00000;
75343, 75354]
My approach would be the following, extract the leading digits of both arrays and compare those:
a=num2str(A)-'0';
b=num2str(B)-'0';
[ida,idb]=ismember(a(:,1),b(:,1));
Now get the sorting index of A
[~,ids]=sort(a(:,1));
Create output array
C=zeros(size(A,1),2);
Finally assign and sort output
C(:,1)=A;
C(ida,2)=B(idb(idb>0));
%sort result
C=C(ids,:)
If it's only the first digit, we only need to check if the first digit (i.e. floor(A/1e4)) matches 0 to 9, and index accordingly...
% Add some zeros at the front to make indexing work with the unmatched ismember outputs
Az = [zero(; A]; Bz = [0; B];
% Find the indices for 0 to 9 within the first digits of A and B
[~,ia] = ismember( 0:9, floor( A/1e4 ) );
[~,ib] = ismember( 0:9, floor( B/1e4 ) );
% Assign to C and discard unmatched rows
C = [Az(ia+1), Bz(ib+1)];
C( all( C==0, 2 ), : ) = [];
Note that keeping things numeric with the floor operation should always be preferable to flipping between numeric and character data with things like num2str...
Edit
You changed the scope of the question by commenting with new data. Here is the same method, written to be more generic so it handles A and B with more columns and different magnitude IDs
% Add some zeros at the front to make indexing work with the unmatched ismember outputs
Az = [zeros(1,size(A,2)); A]; Bz = [zeros(1,size(A,2)); B];
% Function for getting first digit
f = #(x) floor(x./(10.^floor(log10(x))));
% Find the indices for 0 to 9 within the first digits of A and B
[~,ia] = ismember( 0:9, f(A(:,1)) );
[~,ib] = ismember( 0:9, f(B(:,1)) );
% Assign to C and discard unmatched rows
C = [Az(ia+1,:), Bz(ib+1,:)];
C( all( C==0, 2 ), : ) = [];
First of all, the whole script. At first glance, I couldn't find a solution without using loops.
A = [ 12345;
47542;
32673;
65436;
75343;
23496;
54765; ]
B = [ 23566;
33425;
65438;
75354; ]
A = sort(A); % Sort A and B.
B = sort(B);
A_str = int2str(A); % Convert integers to chars.
B_str = int2str(B);
A_sn = A_str(:, 1); % Extract first columns.
B_sn = B_str(:, 1); % Basically, these are the serial numbers.
C = zeros(size(A, 1), size(A, 2) * 2); % Initialize C.
C(:, 1) = A; % First column of C is just A.
for i = 1:length(A_sn) % For all serial numbers in A...
for j = 1:length(B_sn) % For all serial numbers in B...
if (A_sn(i) == B_sn(j)) % Check if serial number in B equals the serial number in A.
C(i, 2) = B(j); % If so, set i-th row in C to the corresponding value in B.
end
end
end
C
Results in:
A =
12345
47542
32673
65436
75343
23496
54765
B =
23566
33425
65438
75354
C =
12345 0
23496 23566
32673 33425
47542 0
54765 0
65436 65438
75343 75354

Repeat letters within a string in Matlab

I would like to generate a string in Matlab that looks like
"BBBBBBBBBBBBBBBBCCCCCCCCCCCCCC"
where "B" is repeated m times and "C" is repeated n times. Is there any function with inputs similar to ("B","C",n,m) doing this?
You can use the function repelem to repeat the characters the wanted number of times.
str = 'BC' %This is character vector, NOT a string
n = 4; m = 3;
res = repelem(str,[n,m])
res =
'BBBBCCC'
repmat function works on char arrays, and concatenation operators too.
So:
copyfcn = #(B,C,n,m) [repmat(B,[1 n]) repmat(C,[1,m])];
copyfcn('B','C',8,4)
ans =
'BBBBBBBCCCC'
If you want the output to be a string rather than a char array, you can just wrap it in the string() function.
copyfcn2 = #(B,C,n,m) string([repmat(B,[1 n]) repmat(C,[1,m])]);
copyfcn2('B','C',8,4)
ans =
"BBBBBBBBCCCC"
You can make your own function using repmat:
>> f = #(a,b,n,m) [repmat(a, [1 m]) repmat(b, [1 m])];
>> f('B','C',12,14)
ans =
'BBBBBBBBBBBBBBCCCCCCCCCCCCCC'

Assign multiple function outputs to a vector using indexing in MATLAB

I have a simple MATLAB function outputting multiple variables:
function [a,b] = MultipleOutputs()
a = 6;
b = 8;
end
I want to assign the two output variables to 2 certain elements in an existing vector:
x = ones(1,4);
x(2:3) = MultipleOutputs()
However, this gives me:
x =
1 6 6 1
Instead of:
x =
1 6 8 1
I have had this problem in multiple cases, was never able to find the solution.
You have 2 choices:
Concatenate the vectors after outputting them separately
[a,b] = MultipleOutputs();
x = ones(1,4);
x(2:3) = [a,b];
concatenate the vectors before outputting them
function a = MultipleOutputs()
a(1) = 6;
a(2) = 8;
end
x(2:3) = MultipleOutputs();
when you run MultipleOutputs() like that in another function, it only outputs only the first element, which in this case is a.
So eventually your statement x(2:3) = MultipleOutputs() is equivalent to x(2:3) = 6.
A simple fix would be to extract all the elements:
[a,b] = MultipleOutputs();
x(2:3) = [a b];

Push array onto 2D array (matrix) in MATLAB

For God only knows what reason, we're being asked to use MATLAB in an AI course. All I want to do is initialize an array, and push arrays onto it. In Ruby, this would be:
multi_arr = []
an_arr = [1, 2, 3, 4]
multi_arr << an_arr
Done! Unfortunately I can't find a similarly simple solution in MATLAB.
Any advice would be extremely appreciated.
EDIT: for the interested, here's the rather ungraceful solution I arrived at:
child_states = []
child_state = [0,1,2,3,4,5,6,7,8]
% returns [rows, columns]
dimensions = size(child_states)
child_states(dimensions(1)+1, 1:9) = child_state
You can append array to an array in matlab without knowing the dimensions but it won't be very efficient because matlab will allocate space for the whole array each time you do it. Here's how to do it:
arrays = [];
arr1 = [1,2];
arr2 = [3,4,5];
% append first array
arrays = [arrays ,arr1 ]
% append second array
arrays = [arrays ,arr2 ]
arrays =
1 2
arrays =
1 2 3 4 5
if each of the arrays you want to append have the same length, then you can append them as rows:
arrays = [];
arr1 = [1,2,4];
arr2 = [5,6,7];
% append first array
arrays = [arrays ; arr1 ]
% append second array
arrays = [arrays ; arr2 ]
arrays =
1 2 4
arrays =
1 2 4
5 6 7
for more of a ruby like array appending you should use cell arrays:
cells = {};
cells = [cells ,[4,5] ]
cells = [cells ,[1,1,1] ]
cells = [cells ,['hello']]
cells =
[1x2 double] [1x3 double] 'hello'
GIYF. It seems that you are looking for horzcat and vertcat. Check out MATLAB's doc at Creating and concatenating matrices.; from vertcat page:
C = vertcat(A1,...,AN) vertically concatenates arrays A1,...,AN. All arrays in the argument list must have the same number of columns.
If the inputs are multidimensional arrays, vertcat concatenates N-dimensional arrays along the first dimension. The remaining dimensions must match.
Here's a function that's supposed to do what you want: concatenate a row vector to an array regardless of size. This function will check the dimension along the second axis of input and output array and pad zero to whichever one that is smaller so they can be concatenated along the first axis.
function m = freevertcat(m, n)
if isempty(m)
m = cat(1, m, n);
else
size_m = size(m, 2);
size_n = size(n, 2);
if size_m > size_n
n(size_n+1 : size_n + size_m - size_n) = 0
elseif size_n > size_m
m(:, size_m+1 : size_m + size_n - size_m) = 0;
end
m = cat(1, m, n);
end
example usage
m = []
n = [1,2,3,4,5]
m = freevertcat(m,n)
p = [3,3,3]
m = freevertcat(m,p)
You'll get
m = 1 2 3 4 5
3 3 3 0 0

Octave appending in a 2D cell array

I'm trying to append an element at the end of a 2D cell array row. My code is:
b = cell(5, 0)
b(1) = {b(1, :), 2} % Trying to append at the end of the first row
This gives me the error: error: A(I) = X: X must have the same size as I
I've also tried various other forms, such as:
b = cell(5, 0)
b(1, end+1) = 2 % Ok, inserts 2 at [1,1]
b(2, end+1) = 3 % No, inserts 3 at [2,2] instead of [2, 1]
It seems that you are confused with cell array indexing.
If you want to append elements at the end of a row in a matrix (in your case, a cell array), you must still make sure that all rows are of the same size after the assignment, otherwise you'll trigger an error about mismatching dimensions.
Instead of b(1) = {b(1, :), 2}, the following should work:
b(1, end + 1) = 2
Alternatively, if you want to append an entire column array of cells to b, use horizontal concatenation, for example:
b = [b, {2; 3; 4; 5; 6}];
This should append a single cell at the end of each row of b.
The reason the element gets inserted at [2, 2] and not [1, 1] is that by the time you try to insert the second element, the value denoted by end has increased from 0 to 1.
The following should do what you need:
>> b = cell(5, 0)
b =
Empty cell array: 5-by-0
>> b(1,1) = {2}
b =
[2]
[]
[]
[]
[]
>> b(2,1) = {3}
b =
[2]
[3]
[]
[]
[]
>>