Related
have tried searching but can't find anything to help, maybe my problem is too simple! Anyway, I'm running a nested FOR loop, but the array I save my results to only keeps the last "run" of results. Can someone please help me to store/concatenate the results ?
clc
clear
n = 2;
for aa = 1:n
aa = inputdlg({'Depth from','Depth to','Outer Diameter','Nominal Weight'},'1',[1 7;1 7;1 30;1 30]);
x = [str2num(aa{1}),str2num(aa{2}),str2num(aa{3}),str2num(aa{4})]
end
and the results
x =
1 2 3 4
x =
5 6 7 8
I cannot use the first one,
want to save all the results and save every iteration in a single variable
That is not the way to save the result into an array in a for loop; separate the looping variable and the array you are storing values in:
clc
clear
n = 2;
x = zeros(n, 4);
for k = 1:n
aa = inputdlg({'Depth from','Depth to','Outer Diameter','Nominal Weight'},'1',[1 7;1 7;1 30;1 30]);
x(k, :) = [str2double(aa{1}),str2double(aa{2}),str2double(aa{3}),str2double(aa{4})];
end
Then if you display x you get something like:
x =
1 2 3 4
5 6 7 8
Assuming you entered 1 to 4 and then 5 to 8 in the dialog.
I want to show 2dim. Surface plots for different combinations of 2 parameters of a 3- or higher-dimensional array in matlab. The data for the non-shown dimensions are integrated (i.e. summed in the remaining dimensions). I am using surf(), and for parameter combinations other than (1,2) (eg. (1,3), (2,3) ...) I have to rearrange the data matrices in order to make it work.
I am looking for an alternative command (or shorter code) which does this work.
Here's the code:
a=zeros(3,3,2);
a(:,:,1) = [1 2 3 ;4 5 6; 7 8 9; 10 11 12]; % // data matrix
a(:,:,2) = -[1 2 3 ;4 5 6; 7 8 9; 10 11 12]*2; % // data matrix
ai=[[1 2 3 4]' [5 6 7 0]' [8 9 0 0]']; % // parameter vector
mat12 = sum(a,3);
surf(ai(1:3,2),ai(1:4,1),mat12)
aux13 = sum(a,2);
for i = 1:2; mat13(:,i) = aux13(:,:,i);
surf(ai(1:2,3),ai(1:4,1),mat13)
aux23 = sum(a,1);
for i = 1:2; mat23(i,:) = aux23(:,:,i);
surf(ai(1:3,2),ai(1:2,3),mat23)
In other words, I am looking for a way to use surf for matrices mat13 and mat23 without the aux13, aux23 variables and the for loop.
First your example doesn't run because you declare a=zeros(3,3,2); as a matrix [3x3x2] but you immediately try to populate it as a [4x3x2] matrix, so I had to adjust your first line to: a=zeros(4,3,2);
If I run your code with that adjustment, your auxiliary variable and for loops are to reform/reshape a matrix stripped of it's singleton dimension. Matlab provide a handy function for that : squeeze.
For example, your variable aux13 is of dimension [4x1x2], then mat13=squeeze(aux13); achieve the same thing than your for loop. Your matrix mat13 is now of dimension [4x2].
Since no for loop is needed, you can completely bypass your auxiliary variable by calling squeeze directly on the result of your summation: mat13=squeeze( sum(a,2) );
Full example, the code below does exactly the same than your code sample:
mat12 = sum(a,3);
surf(ai(1:3,2),ai(1:4,1),mat12)
mat13 = squeeze( sum(a,2) ) ;
surf(ai(1:2,3),ai(1:4,1),mat13)
mat23 = squeeze( sum(a,1) ) ;
mat23 = mat23.' ; %'// <= note the "transpose" operation here
surf(ai(1:3,2),ai(1:2,3),mat23)
Note that I had to transpose mat23 to make it match the one in your example.
sum(a,1) is [1x3x2] => squeeze that and you obtain a [3x2] matrix but your code arrange the same values in a [2x3] matrix, so the use of the transpose. The transpose operator has a shorthand notation .'.
I used it in the example in a separate line just to highlight it. Once understood you can simply write the full operation in one line:
mat23 = squeeze(sum(a,1)).' ;
The way you write your loops isn't exactly MATLAB syntax. Below is the correct loop syntax shown.
On line 2 and 3, you are trying to load (4x3)-matrices into (3x3)-matrices. That is why you get a subscript error. You could resolve it by making the zeros-matrix bigger. Here's some Syntax fixed:
a=zeros(4,3,2);
a(:,:,1) = [1 2 3 ;4 5 6; 7 8 9; 10 11 12]; % // data matrix
a(:,:,2) = -[1 2 3 ;4 5 6; 7 8 9; 10 11 12]*2; % // data matrix
ai=[[1 2 3 4]' [5 6 7 0]' [8 9 0 0]']; % // parameter vector
mat12 = sum(a,3);
surf(ai(1:3,2),ai(1:4,1),mat12)
aux13 = sum(a,2);
for i = 1:2 mat13(:,i) = aux13(:,:,i);
surf(ai(1:2,3),ai(1:4,1),mat13)
end
aux23 = sum(a,1);
for i = 1:2 mat23(i,:) = aux23(:,:,i);
surf(ai(1:3,2),ai(1:2,3),mat23)
end
Now, what are you exactly trying to do inside those loops?
A =[1;2;3;4;5]
B= [10 1 ;11 2;19 5]
I want to get
D = [1 10 ;2 11; 3 -9; 4 -9; 5 19]
That is, if something in A doesn't exist in B(:,2), 2nd column of D should be -9.
If something in A exists in B(:,2), I want to put the corresponding row of 1st column of B in 2nd column of D.
I know how to do it with a mix of ismember and for and if. But I need a more elegant method which doesn't use "for" to speed it up.
Unless I'm missing something (or A is not a vector of indices), this is actually much simpler and doesn't require ismember or find at all, just direct indexing:
D = [A zeros(length(A),1)-9];
D(B(:,2),2) = B(:,1)
which for your example matrices gives
D =
1 10
2 11
3 -9
4 -9
5 19
For general A:
A =[1;2;3;4;6]; % Note change in A
B= [10 1 ;11 2;19 5];
[aux, where] = ismember(A,B(:,2));
b_where = find(where>0);
D = [(1:length(A)).' repmat(-9,length(A),1)];
D(b_where,2) = B(where(b_where),1);
This gives
D = [ 1 10
2 11
3 -9
4 -9
5 -9 ]
I am not sure how efficient this solution is, but it avoids using any loops so at least it would be a place to start:
D = [A -9*ones(size(A))]; % initialize your result
[tf, idx] = ismember(A, B(:,2)); % get indices of matching elements
idx(idx==0) = []; % trim the zeros
D(find(tf),2) = B(idx,1); % set the matching entries in D to the appropriate entries in B
disp(E)
You allocate your matrix ahead of time to save time later on (building up matrices dynamically is really slow in MATLAB). The ismember call is returning the true-false vector tf showing which elements of A correspond to something in B, as well as the associated index of what they correspond to in idx. The problem with idx is that it contains a zero any time ft is zero, which is why we have the line idx(idx==0) = []; to clear out these zeros. Finally, find(tf) is used to get the indices associated with a match on the destination (rows of D in this case), and we set the values at those indices equal to the corresponding value in B that we want.
First create a temporary matrix:
tmp=[B; -9*ones(size(A(~ismember(A,B)))), A(~ismember(A,B))]
tmp =
10 1
11 2
19 5
-9 3
-9 4
Then use find to obtain the first row index with a match to elements of A in the second column of tmp (there is always a match by design).
D=[A, arrayfun(#(x) tmp(find(tmp(:,2)==x,1,'first'),1),A)];
D =
1 10
2 11
3 -9
4 -9
5 19
As an alternative for the second step, you could simply sort tmp based on its second column:
[~,I]=sort(tmp(:,2));
D=[tmp(I,2), tmp(I,1)]
D =
1 10
2 11
3 -9
4 -9
5 19
I'm a total beginner to matlab and I'm currently writing a script for extracting data from a thermographic video.
Firstly the video is cut in separate frames. The first frame is opened as a sample picture to define the coordinates of sampling points. The goal is then to select the rgb values of those defined coordinates from a set of frames and save them into a matrix.
Now I have a problem separating the matrix to n smaller matrices.
e.g I'm defining the number of points to be selected to n=2 , with a picture count of 31. Now it returns a matrix stating the rgb codes for 31 pictures, each at 2 points, in a 62x3 double matrix...
Now I want to extract the 1st, 3rd, 5th....etc... row to a new matrix...this should be done in a loop, according to the number of n points...e.g 5 points on each picture equals 5 matrices, containing values of 31 pictures....
this is an extract of my code to analyse the pictures, it returns the matrix 'values'
files = dir('*.jpg');
num_files = numel(files);
images = cell(1, num_files);
cal=imread(files(1).name);
n = input('number of selection points?: ');
imshow(cal);
[x,y] = ginput(n);
eval(get(1,'CloseRequestFcn'))
%# x = input('x-value?: '); manual x selection
%# y = input('y-value?: '); manual y selection
for k = 1:num_files
images{k} = imread(files(k).name);
end
matrix=cell2mat(images);
count=(0:size(matrix,1):size(matrix,1)*num_files);
for k = 1:num_files
a(k)= mat2cell(impixel(matrix,x+count(k),y));
end
values = cat(1,a{:})
Easy fix
Do you mean that if you have:
n = 2;
k = 2; % for example
matrix = [1 2 3;
4 5 6;
7 8 9;
8 7 6];
you want it to become
b{1} = [1 2 3;
7 8 9];
b{2} = [4 5 6;
8 7 6];
This can be easily done with:
for ii = 1:n
b{ii} = matrix(1:n:end,:);
end
Better fix
Of course it's also possible to just reshape your data matrix and use that instead of the smaller matrices: (continuing with my sample data ^^)
>> d=reshape(matrix',3,2,[]);
>> squeeze(d(:,1,:))
ans =
1 7
2 8
3 9
>> squeeze(d(:,2,:))
ans =
4 8
5 7
6 6
Good practice
Or, my preferred choice: save the data immediately in an easy to access way. Here I think it will be an matrix of size: [num_files x num_points x 3]
If you want all the first points:
rgb_data(:,1,:)
only the red channel of those points:
rgb_data(:,1,1)
and so on.
I think this is possible with this:
rgb_data = zeros(num_files, num_points, 3);
for kk = 1:num_files
rgb_data(kk,:,:) = impixel(images{kk},x+count(k),y);
end
But I don't understand the complete meaning of your code (eg: why matrix=cell2mat(images) ??? and then of course:
count=(0:size(matrix,1):size(matrix,1)*num_files);
is just count=0:num_files;
so I'm not sure what would come out of impixel(matrix,x+count(k),y) and I used images{k} :)
I need to generate some 5x6 matrices in MATLAB. They need to consist of randomly generated integers in the range 1-6, however, an integer cannot occur more than once in a particular row or column.
Here is the script I am currently using to generate random 5x6 matrices:
mat=zeros(5,6);
rows=5;
columns=6;
for i=1:rows
for j=1:columns
mat(i,j)=round(rand*(high-low)+low);
end
end
disp(mat)
But I don't know how to insert the rule about repeats into this.
I'm sure this is a relatively simple problem but I'm very new to MATLAB and haven't been able to generate something that satisfies these conditions. I'd be greatful for any assistance anyone can give.
Try this:
m = zeros(5,6);
for row = 1:5
flag = 1;
while(flag)
flag = 0;
R = randperm(6);
for testRow = 1:row
flag = flag + sum(m(testRow,:) == R);
end;
if (~flag)
m(row,:) = R;
end;
end;
end;
m
Don't try to fill the matrix with completely random ints all at once. The likelihood of that being a valid puzzle grid is vanishingly low.
Instead, use the same method as used by Sudoku generators - start with a blank matrix and fill in elements one at a time, as restricted by your rules.
Where you have more than one choice for the entry, pick one of them at random.
You might progress something like this (4x4 example for brevity - allowable numbers 1-4)
x x x x
x x x x
x x x x
x x x x
Pick first number by dice roll: 3.
3 x x x
x x x x
x x x x
x x x x
Pick second number from list of allowable numbers: [1, 2, 4].
3 1 x x
x x x x
x x x x
x x x x
Pick third number from list of allowable numbers, [1, 4]:
3 1 4 x
x x x x
x x x x
x x x x
And so on.
If your "list of allowable numbers" at some insertion step is an empty set, then your matrix can't be salvaged and you may need to start again.
Also a 10x10 matrix with 5 unique integers is clearly impossible - insert some logic to test for this trivial error case.
Edit: Since it's not homework in the traditional sense, and since it was an interesting problem....
function arena = generate_arena(num_rows, num_cols, num_symbols)
% Generate an "arena" by repeatedly calling generate_arena_try
% until it succeeds.
arena = 0;
number_of_tries = 0;
while ~(arena)
arena = generate_arena_try(num_rows, num_cols, num_symbols);
number_of_tries = number_of_tries + 1;
end
sprintf('Generating this matrix took %i tries.', number_of_tries)
end
function arena = generate_arena_try(num_rows, num_cols, num_symbols)
% Attempts to generate a num_rows by num_cols matrix of random integers
% from the range 1:num_symbols, with no symbols repeated in each row or
% column.
%
% returns 0 on failure, or the random matrix if it succeeds.
arena = zeros(num_rows, num_cols);
symbols = 1:num_symbols;
for n = 1:num_rows
for m = 1:num_cols
current_row = arena(n,:);
current_col = arena(:,m);
% find elements in $symbols that are not in the current row or col
choices = setdiff ( symbols, [current_row current_col'] );
if isempty(choices)
arena = 0;
return;
end
% Pick one of the valid choices at random.
arena(n,m) = choices(randi(length(choices)));
end
end
return;
end
Invocation and output are like:
>> generate_arena(5,6,6)
ans =
Generating this matrix took 5 tries.
ans =
2 3 6 4 5 1
6 1 5 3 4 2
1 5 4 2 6 3
4 6 2 1 3 5
3 4 1 5 2 6
Don't say I never gave you nothing. ;)
Here's another way of doing it:
Start off with a known valid solution, say this one:
>> A = mod(meshgrid(1:size) - meshgrid(1:size)', size) + 1
A =
1 2 3 4 5 6
6 1 2 3 4 5
5 6 1 2 3 4
4 5 6 1 2 3
3 4 5 6 1 2
2 3 4 5 6 1
Then swap rows and columns at random. You can prove that each swap preserves the "no-repeats" property in each row and column.
Say you swap row 1 and row 2. You haven't changed the contents of the rows, so the "no repeats in each row" property remains true. Similarly, you haven't changed the contents of any of the columns - just the ordering - so the "no repeats in each column" property also remains true.
Here is what I came up with:
function arena = gen_arena_2 (size)
arena = mod(meshgrid(1:size) - meshgrid(1:size)', size) + 1;
%repeatedly shuffle rows and columns
for i = 1:10
arena = arena(:,randperm(size))'; %shuffle columns and transpose
end
end
Example usage:
>> gen_arena_2(6)
ans =
3 5 4 2 1 6
6 2 1 5 4 3
5 1 6 4 3 2
4 6 5 3 2 1
1 3 2 6 5 4
2 4 3 1 6 5
I'm not sure this is probably "as random" as the other way - but this way is fast and it doesn't need any logic to detect a failure (because it will (provably) always produce a correct result.)