MATLAB: fprintf/sprintf to print string + matrix - matlab

I wanted to know, if it's possible to print a string followed by a matrix, written in the classic form, for example:
5 5 9
>>your matrix is M = 1 4 2
2 1 3
using fprintf/sprintf.

If your matrix doesn't have to be on the same line as the text, you can do something as simple as replacing ; with \n in the output of mat2str:
A=[1 2 3; 4 5 6; 7 8 9];
intro_str = 'Your matrix is:\n';
sprintf([intro_str strrep(mat2str(A),';','\n ')])
Your matrix is:
[1 2 3
4 5 6
7 8 9]
If, however, you want to have them on the same line, the only way I see how this can be done is by computing the amount of tabs (\t) or spaces you need on every "non-intro" line, approximately like this:
A=[1 2 3; 4 5 6; 7 8 9];
intro_str = 'Your matrix is: ';
%// ntabs = ceil(length(intro_str)/3);
%// tab_blanks = cellstr(repmat('\t',size(A,2),ntabs));
spaces = blanks(length(intro_str));
space_blanks = repmat(spaces,size(A,2),1);
mid_row = ceil(size(A,1)/2);
%// tab_blanks(mid_row) = {intro_str};
space_blanks(mid_row,:) = intro_str;
final_str = [space_blanks repmat('%u\t',size(A,1),size(A,2)) repmat('\n',size(A,1),1)]';
final_str = horzcat(final_str(:))';
sprintf(final_str,A(:))
ans =
1 4 7
Your matrix is: 2 5 8
3 6 9

Related

Auto-fill matrix without row-repetitions

I have a series of numbers:
test = [1 1 1 2 2 2 3 3 3 4 4 4 5 5 5]
which I want to randomely fill into a 3x5 matrix without having the same number in the same row.
How can I do this in matlab? Potentially I could randomize the test vector and fill it into the 5x3 matrix but I don't know how to do this without getting the same number in the same row.
If you want to fill a 3-by-5 matrix with all of the values in test, making sure each row has no repeated values, you can do this very succinctly by using toeplitz to first generate an index matrix, then randomly permute the dimensions with randperm:
index = toeplitz(1:3, [3 5:-1:2]);
index = index(randperm(3), randperm(5));
And a sample index:
index =
1 5 4 2 3
4 3 2 5 1
5 4 3 1 2
If your values in test are the numbers 1 through 5, this should be all you need to do. If test could be any vector with with 5 different numbers, three of each, then you can get the unique values of your test vector and index them with index. This solution will generalize to any test vector:
test = [3 3 3 7 7 7 5 5 5 9 9 9 4 4 4]; % Sample data
uniqueValues = unique(test); % Get the unique values [3 4 5 7 9]
M = uniqueValues(index); % Use index as generated above
And the result will be guaranteed to be a reordered version of what's in test:
M =
3 9 7 4 5
7 5 4 9 3
9 7 5 3 4
You can take the unique matrix of test and pick any three elements out of it and fill in the required 5X3 matrix.
test = [1 1 1 2 2 2 3 3 3 4 4 4 5 5 5] ;
test_unique = unique(test) ;
A = zeros(5,3) ;
for i = 1:size(A,1)
A(i,:) = randsample(test_unique,3) ;
end
randsample needs a statistics toolbox, if you doesn't have it, you may use randperm as shown below.
test = [1 1 1 2 2 2 3 3 3 4 4 4 5 5 5] ;
test_unique = unique(test) ;
A = zeros(5,3) ;
for i = 1:size(A,1)
A(i,:) = test_unique(randperm(length(test_unique),3)) ;
end
If you want 3X5 matrix:
test = [1 1 1 2 2 2 3 3 3 4 4 4 5 5 5] ;
test_unique = unique(test) ;
A = zeros(3,5) ;
for i = 1:size(A,1)
A(i,:) = randsample(test_unique,5) ;
end
Here is a brute-force way of doing it
% data
test = [1 1 1 2 2 2 3 3 3 4 4 4 5 5 5];
% randomly permute the indices
indices = randperm(numel(test));
% create a random matrix
matrix = reshape(test(indices), 5, 3);
% while number of unique elements in any of the rows is other than 3
while any(arrayfun(#(x) numel(unique(matrix(x,:))), (1:size(matrix,1)).')~=3)
% keep generating random matrices
indices = randperm(numel(test));
matrix = reshape(test(indices), 5, 3);
end;
% here is the result
result=matrix;
EDIT : If you want 3x5 like you mentioned in your comment, it is a lot easier. Just one line below.
[~, result] = sort(rand(3,5),2);

How to combine matrix of different size in a cell array into a matrix in MATLAB

Similarly to How to combine vectors of different length in a cell array into matrix in MATLAB I would like to combine matrix having different dimension, stored in a cell array, into a matrix having zeros instead of the empty spaces. Specifically, I have a cell array {1,3} having 3 matrix of size (3,3) (4,3) (4,3):
A={[1 2 3; 4 5 6; 7 8 9] [1 2 3; 4 5 6; 7 8 9; 9 9 9] [1 2 3; 4 5 6; 7 8 9; 4 4 4]}
and I would like to obtain something like:
B =
1 2 3 1 2 3 1 2 3
4 5 6 4 5 6 4 5 6
7 8 9 7 8 9 7 8 9
0 0 0 9 9 9 4 4 4
I tried using cellfun and cell2mat but I do not figure out how to do this. Thanks.
Even if other answers are good, I'd like to submit mine, using cellfun.
l = max(cellfun(#(x) length(x),A))
B = cell2mat(cellfun(#(x) [x;zeros(l-length(x),3)], A, 'UniformOutput', 0));
Using bsxfun's masking capability -
%// Convert A to 1D array
A1d = cellfun(#(x) x(:).',A,'Uni',0) %//'
%// Get dimensions of A cells
nrows = cellfun('size', A, 1)
ncols = cellfun('size', A, 2)
%// Create a mask of valid positions in output numeric array, where each of
%// those numeric values from A would be put
max_nrows = max(nrows)
mask = bsxfun(#le,[1:max_nrows]',repelem(nrows,ncols)) %//'
%// Setup output array and put A values into its masked positions
B = zeros(max_nrows,sum(ncols))
B(mask) = [A1d{:}]
Sample run
Input -
A={[1 2 3 5 6; 7 8 9 3 8] [1 2 3; 4 5 6; 7 8 9; 9 9 9] [1 2 3; 4 5 6; 7 8 9; 4 4 4]}
Output -
B =
1 2 3 5 6 1 2 3 1 2 3
7 8 9 3 8 4 5 6 4 5 6
0 0 0 0 0 7 8 9 7 8 9
0 0 0 0 0 9 9 9 4 4 4
I would be surprised if this is possible in one or a few lines. You will probably have to do some looping yourself. The following achieves what you want in the specific case of incompatible first dimension lengths:
A={[1 2 3; 4 5 6; 7 8 9] [1 2 3; 4 5 6; 7 8 9; 9 9 9] [1 2 3; 4 5 6; 7 8 9; 4 4 4]}
maxsize = max(cellfun(#(x) size(x, 1), A));
B = A;
for k = 1:numel(B)
if size(B{k}, 1) < maxsize
tmp = B{k};
B{k} = zeros(maxsize, size(tmp,1));
B{k}(1:size(tmp,1),1:size(tmp,2)) = tmp;
end
end
B = cat(2, B{:});
Now B is:
B =
1 2 3 1 2 3 1 2 3
4 5 6 4 5 6 4 5 6
7 8 9 7 8 9 7 8 9
0 0 0 9 9 9 4 4 4
I would do it using a good-old for loop, which is quite intuitive I think.
Here is the commented code:
clc;clear var
A={[1 2 3; 4 5 6; 7 8 9] [1 2 3; 4 5 6; 7 8 9; 9 9 9] [1 2 3; 4 5 6; 7 8 9; 4 4 4]};
%// Find the maximum rows and column # to initialize the output array.
MaxRow = max(cell2mat(cellfun(#(x) size(x,1),A,'Uni',0)));
SumCol = sum(cell2mat(cellfun(#(x) size(x,2),A,'Uni',0)));
B = zeros(MaxRow,SumCol);
%// Create a counter to keep track of the current columns to fill
ColumnCounter = 1;
for k = 1:numel(A)
%// Get the # of rows and columns for each cell from A
NumRows = size(A{k},1);
NumCols = size(A{k},2);
%// Fill the array
B(1:NumRows,ColumnCounter:ColumnCounter+NumCols-1) = A{k};
%// Update the counter
ColumnCounter = ColumnCounter+NumCols;
end
disp(B)
Output:
B =
1 2 3 1 2 3 1 2 3
4 5 6 4 5 6 4 5 6
7 8 9 7 8 9 7 8 9
0 0 0 9 9 9 4 4 4
[max_row , max_col] = max( size(A{1}) , size(A{2}) , size(A{3}) );
A{1}(end:max_row , end:max_col)=0;
A{2}(end:max_row , end:max_col)=0;
A{3}(end:max_row , end:max_col)=0;
B=[A{1} A{2} A{3}];
for this specific problem, simply this will do:
B=cat(1,A{:});
or what I often just give a try for 2D cells, and works for your example as well:
B=cell2mat(A');
if you literally don't give a f* what dimension it will be cut in (and you're exceedingly lazy): put the same into a try-catch-block and loop over some dims as below.
function A=cat_any(A)
for dims=1:10% who needs more than 10 dims? ... otherwise replace 10 with: max(cellfun(#ndims,in),[],'all')
try, A=cat(dims,A{:}); end
if ~iscell(A), return A; end
end
disp('Couldn''t cat!') %if we can't cat, tell the user
end
Beware, this might lead to unexpected results ... but in most cases simply just worked for me.

vec2mat w/ different number of columns

Referring to Reshape row wise w/ different starting/ending elements number #Divakar came with a nice solution but, what if the number of columns is not always the same?
Sample run -
>> A'
ans =
4 9 8 9 6 1 8 9 7 7 7 4 6 2 7 1
>> out
out =
4 9 8 9 0 0
6 1 8 9 7 7
7 4 6 2 7 1
I took only the first 4 terms of A and put them in out, then fill the rest 2 empty cell with 0's. So the ncols = [4 6 6]. Unfortunately vet2mat doesn't allow vector as columns number.
Any suggestions?
You can employ bsxfun's masking capability here -
%// Random inputs
A = randi(9,1,15)
ncols = [4 6 5]
%// Initialize output arary of transposed size as compared to the desired
%// output arary size, as we need to insert values into it row-wise and MATLAB
%// follows column-major indexing
out = zeros(max(ncols),numel(ncols));
mask = bsxfun(#le,[1:max(ncols)]',ncols); %//'# valid positions mask for output
out(mask) = A; %// insert input array elements
out = out.' %//'# transpose output back to the desired output array size
Code run -
A =
5 3 7 2 7 2 4 6 8 1 9 7 5 4 5
ncols =
4 6 5
out =
5 3 7 2 0 0
7 2 4 6 8 1
9 7 5 4 5 0
You could use accumarray for that:
A = [4 9 8 9 6 1 8 9 7 7 7 4 6 2 7 1].'; %'// data
ncols = [4 6 6]; %// columns
n = max(ncols);
cs = cumsum(ncols);
ind = 1;
ind(cs+1) = 1;
ind = cumsum(ind(1:end-1)); %// `ind` tells the row for each element of A
result = accumarray(ind(:), A(:), [], #(x) {[x; zeros(n-numel(x),1)]}); %// split `A` as
%// dictated by `ind`, and fill with zeros. Each group is put into a cell.
result = [result{:}].'; %'// concatenate all cells

How do I Combine two equal sized vectors element wise in MatLab?

I have two vectors:
a = [1 3 5 7 9];
b = [2 4 6 8 10];
That I need to combine together element wise. Meaning that I need the first element of vector a, then the first element of vector b, second of a, second of b, and so forth until I get the following:
combined = [1 2 3 4 5 6 7 8 9 10]
How do I do this within MatLab?
Edit
I ran a test of the top three answers (Josh, Marc, & Kronos) and compared the time it took to run them. I ran each 100 times after doing a 10 iteration warmup. The vectors created were exactly the same size in length (16e+6) and were random values ranging from 1 to 100:
Test Results
Test: Total Time (100 runs): Avg Time Per Exec:
Josh B 21.3687 0.2137
Marc C 21.4273 0.2143
Kronos 31.1897 0.3119
It appears that both Josh's and Marc's solutions are similar in execution time.
a = [1 3 5 7 9];
b = [2 4 6 8 10];
temp = [a; b];
combined = temp(:)';
This can be done by the following:
a = [1 3 5 7 9];
b = [2 4 6 8 10];
combinedSize = size(a, 2) * 2;
combined(1:2:combinedSize) = a;
combined(2:2:combinedSize) = b;
This is obviously assuming that your vectors are exactly the same size. If by chance you want to merge two vectors that are not the same size then you can do the following:
combinedSize = max(size(a, 2), size(b, 2)) * 2;
combined = NaN(1,combinedSize);
combined(1:2:size(a,2)*2) = a;
combined(2:2:size(b,2)*2) = b;
This will place a NaN for the remaining elements of the smaller vector. For example, given the following sample vectors:
a = [1 3 5 7 9 11];
b = [2 4 6 8];
will result in the combined vector:
combined =
1 2 3 4 5 6 7 8 9 NaN 11 NaN
Place the vectors below eachother in a matrix and use reshape. For example:
>> A=[1 2 3]
A =
1 2 3
>> B=[4 5 6]
B =
4 5 6
>> C=reshape([A;B],1,size(A,2)+size(B,2))
C =
1 4 2 5 3 6
It's straightforward to generalize to more than 2 vectors.
You can also give a try to looping, for example:
a=[1 2 3 4 5];
b=[11 12 13 14 15];
for i = 1:N
{
if (i%2==0)
{ c[i] = b[i]; }
else
{ c[i] = a[i]; }
This shall work!
All the answers above only work if the two vectors have the same number of elements. The following will work even if they have different number of elements:
>>
A = [1 3 5];
B = [2 4 6 7 8];
C = [1 3 5 7 8];
D = [2 4 6];
AB = nan(1,2*max(numel(A),numel(B)));
CD = nan(1,2*max(numel(C),numel(D)));
AB(2*(1:length(A))) = A;
AB(1+2*(1:length(B))) = B;
CD(2*(1:length(C))) = C;
CD(1+2*(1:length(D))) = D;
>>
AB = AB(~isnan(AB))
CD = CD(~isnan(CD))
The result would be:
AB =
1 2 3 4 5 6 7 8
CD =
1 2 3 4 5 6 7 8

What is the simplest way to create a weight matrix bases on how frequent each element appear in the matrix?

This is the input matrix
7 9 6
8 7 9
7 6 7
Based on the frequency their appearance in the matrix (Note. these values are for explanation purpose. I didn't pre-calculate them in advance. That why I ask this question)
number frequency
6 2
7 4
8 1
9 2
and the output I expect is
4 2 2
1 4 2
4 2 4
Is there a simple way to do this?
Here's a three-line solution. First prepare the input:
X = [7 9 6;8 7 9;7 6 7];
Now do:
[a m n] = unique(X);
b = hist(X(:),a);
c = reshape(b(n),size(X));
Which gives this value for c:
4 2 2
1 4 2
4 2 4
If you also wanted the frequency matrix, you can get it with this code:
[a b']
Here is a code with for-loop (a is input matrix, freq - frequency matrix with 2 columns):
weight = zeros(size(a));
for k = 1:size(freq,1)
weight(a==freq(k,1)) = freq(k,2);
end
Maybe it can be solved without loops, but my code looks like:
M = [7 9 6 ;
8 7 9 ;
7 6 7 ;];
number = unique(M(:));
frequency = hist(M(:), number)';
map = containers.Map(number, frequency);
[height width] = size(M);
result = zeros(height, width); %allocate place
for i=1:height
for j=1:width
result(i,j) = map(M(i,j));
end
end