MATLAB Answers

0

MatLab function for each two values in struct inside loop

Asked by Mohammed Hammad on 29 Mar 2019
Latest activity Edited by Stephen Cobeldick on 31 Mar 2019
Hello,
I have a struct with some fileds, (attached)
something like exp = {'Names', 'Date', 'X' , 'Y'}
Names = ['John', 'Adam', 'Markus',.....]
X = [ X1, X2, X3, X4,.....]
Y = [ Y1, Y2, Y3, Y4,.....]
I want to run an equation to calculate the sqrt for each two values depending on the same field (Date)
For Date (Date 1)
s12 = sqrt((X2-X1)^2+(Y2-Y1)^2);
then
s13= sqrt((X3-X1)^2+(Y3-Y1)^2);
and
s14= sqrt((X4-X1)^2+(Y4-Y1)^2);
and
s23 = sqrt((X3-X2)^2+(Y3-Y2)^2);
and
s24 = sqrt((X4-X2)^2+(Y4-Y2)^2);
and
s34 = sqrt((X4-X3)^2+(Y4-Y3)^2);
And the same for (Date2)
So I wrote a loop as follows:
for i= 1:length(example)
for j = i+1;
for u = 1:length(unique(example.dates,'rows'));
s(i) = sqrt((example(j).x-example(i).x)^2+(example(j).y-example(i).y)^2);
abr{i} = sprintf('%s-%s',strtrim(example(j).name),strtrim(example(i).name));
end
end
end
%%%%% Export results to an array (Date, Name i_Name j, S)
result = [];
for n = 1:length(unique(example.dates,'rows'));
result = [result; dates(n) abr(n) d(n)];
end
But I am sure there is something wrong because I should get s for all the pairs of the values and just for the same date each time.
The results should be something like that:
IMG_20190329_164539.jpg
Please help me to figure out how to do that.

  4 Comments

Show 1 older comment
I edited my question with more details, attached also my struct and a picture for the desired result. Thank you
You have the same names and also same dates, are you sure that your name list will be unique for each date? Otherwise you will get a row like Ronny vs Ronny for example.
My names are unique for each date (without any duplications). And that's why I need my for loop to works for each date separately.

Sign in to comment.

Products


Release

R2019a

1 Answer

Answer by Stephen Cobeldick on 29 Mar 2019
Edited by Stephen Cobeldick on 29 Mar 2019
 Accepted Answer

load example.mat
C = {example.Name};
D = {example.dates};
M = [example.x;example.y];
[U,~,idu] = unique(D);
N = max(idu);
fprintf('%-12s %-10s %-10s %s\n','Date','Name1','Name2','Result')
for k = 1:N
idf = find(idu==k);
P = nchoosek(1:numel(idf),2);
V = sqrt(sum((M(:,idf(P(:,1)))-M(:,idf(P(:,2)))).^2,1));
T = [repmat(U(k),size(V));C(idf(P(:,1)));C(idf(P(:,2)));num2cell(V)];
fprintf('%-12s %-10s %-10s %#.5g\n',T{:});
end
Which prints this in the command window:
Date Name1 Name2 Result
02-Apr-2002 John Adam 4.8237e+06
02-Apr-2002 John Markus 6.7899e+06
02-Apr-2002 John Ronny 7.4859e+06
02-Apr-2002 John Michael 3.8794e+06
02-Apr-2002 Adam Markus 5.9770e+06
02-Apr-2002 Adam Ronny 6.6450e+06
02-Apr-2002 Adam Michael 4.7198e+06
02-Apr-2002 Markus Ronny 7.3046e+05
02-Apr-2002 Markus Michael 2.9522e+06
02-Apr-2002 Ronny Michael 3.6236e+06
03-Apr-2002 Ronny Markus 5.9770e+06
03-Apr-2002 Ronny Michael 6.6450e+06
03-Apr-2002 Ronny John 4.8237e+06
03-Apr-2002 Ronny Adam 4.7198e+06
03-Apr-2002 Ronny Romio 5.8499e+05
03-Apr-2002 Ronny Sebastian 7.4353e+06
03-Apr-2002 Markus Michael 7.3046e+05
03-Apr-2002 Markus John 6.7899e+06
03-Apr-2002 Markus Adam 2.9522e+06
03-Apr-2002 Markus Romio 6.1503e+06
03-Apr-2002 Markus Sebastian 1.0072e+07
03-Apr-2002 Michael John 7.4859e+06
03-Apr-2002 Michael Adam 3.6236e+06
03-Apr-2002 Michael Romio 6.8414e+06
03-Apr-2002 Michael Sebastian 1.0753e+07
03-Apr-2002 John Adam 3.8794e+06
03-Apr-2002 John Romio 4.3132e+06
03-Apr-2002 John Sebastian 3.3172e+06
03-Apr-2002 Adam Romio 4.6077e+06
03-Apr-2002 Adam Sebastian 7.1300e+06
03-Apr-2002 Romio Sebastian 6.8562e+06
>>
And checking the last one:
>> sqrt((example(11).x-example(12).x).^2 + (example(11).y-example(12).y).^2)
ans =
6.8562e+06
>> example(11:12).Name
ans =
Romio
ans =
Sebastian
>> example(11:12).dates
ans =
03-Apr-2002
ans =
03-Apr-2002

  7 Comments

"what is the best method (high performance) to save the results to one array (Date, Name1, Name2, V)?"
Preallocate a cell array before the loop, add the required data, then concatenate the contents after the loop. Something like this (untested, but should get you started):
Z = cell(1,N); % preallocate
for k = 1:N
...
Z{k} = T; % add data
end
Z = [Z{:}] % concatenate
"and if the equation changed to something else (any mathematical equation for the same x,y,z), does the structure of the code will be changed or just V!"
You would need to change the line that defines V:
V = ... new calculation here!
Just keep in mind that the calculation needs to accept a dims*columns matrix, and it needs to return a 1*columns row vector (where dims represent x, y, z, etc, and columns represents the number of instances of that date). Most likely you would write vectorized code:
due to the large data that I have and it's difficult to check whether that's everything is alright, I changed my data to be as follows:
Multiple cells, each cell contains the same data but each cell express one date (unique).
For example:
Cell (02-Apr-2002) contains Date, Name, X, Y, Z
Cell (03-Apr-2002) contains Date, Name, X, Y, Z
And So on...
And I loaded them into my workspace:
myFolder ='......';
filePattern = fullfile(myFolder, '*.mat');
theFiles = dir(filePattern);
Headings = {'Date', 'Name', 'X', 'Y', 'Z'};
for i=1:length(theFiles)
eval(['load ' theFiles(i).name]);
array = cell2struct(data, Headings, 2);
end
C = {example.Name};
D = {example.dates};
M = [example.x;example.y];
[U,~,idu] = unique(D);
N = max(idu);
fprintf('%-12s %-10s %-10s %s\n','Date','Name1','Name2','Result')
%%%%% how to perform the loop to works on each file and export Z for each date %%%%%%
for n = 1:length(theFiles)
for k = 1:N
idf = find(idu==N);
P = nchoosek(1:numel(idf),2);
V = sqrt(sum((M(:,idf(P(:,1)))-M(:,idf(P(:,2)))).^2,1));
T = [repmat(U(k),size(V));C(idf(P(:,1)));C(idf(P(:,2)));num2cell(V)];
fprintf('%-12s %-10s %-10s %#.5g\n',T{:});
Z{k} = T;
end
end
I apprciate your help always
And I tried to make it something like :
for i=1:length(theFiles)
eval(['load ' theFiles(i).name]);
array = cell2struct(data, Headings, 2);
C = {example.Name};
D = {example.dates};
M = [example.x;example.y];
[U,~,idu] = unique(D);
N = max(idu);
fprintf('%-12s %-10s %-10s %s\n','Date','Name1','Name2','Result')
%%%%% how to perform the loop to works on each file and export Z for each date %%%%%%
for k = 1:N
idf = find(idu==N);
P = nchoosek(1:numel(idf),2);
V = sqrt(sum((M(:,idf(P(:,1)))-M(:,idf(P(:,2)))).^2,1));
T = [repmat(U(k),size(V));C(idf(P(:,1)));C(idf(P(:,2)));num2cell(V)];
fprintf('%-12s %-10s %-10s %#.5g\n',T{:});
Z{k} = T;
end
end
It runs good for a while with showing for each date all the required results but it stopped and it said:
Error using nchoosek (line 65) K must be an integer between 0 and N.
PS: I have around 2500 cells.
@Mohammed Hammad: your code does not make much sense to me. In particular:
1. This loop:
for i=1:length(theFiles)
eval(['load ' theFiles(i).name]);
array = cell2struct(data, Headings, 2);
end
serves absolutely no purpose whatsoever, because you do not use any indexing (or concatenation) to store the output array, and so you simply overwrite array except for the one defined on the last iteration. So every previous iteration is just a waste of time. Also that eval usage should be avoided, which you can do very simply:
load(theFiles(i).name)
or even better:
S = load(theFiles(i).name);
array = cell2struct(S.data, Headings, 2);
2. You never use the variable array for anything anyway: once you have imported that data and created array you just ignore it. Seems like a waste of time to me!
3. For some reason you are still accessing a variable named example, although this is not defined anywhere in your code.
In any case that error message is very easy to understand: you will get that error whenever numel(idf)<2, i.e. there is only one name in the entire date group. If there is only one name (for a particular date) then clearly it is not possible to generate all combinations of two names from it! You will need to decide what to do in those cases (I cannot decide how your algorithm should work), but most likely you will need to add an IF condition, something like this:
Z = cell(1,N); % preallocate. Why did you remove this??? Very bad idea!
for k = 1:N
idf = find(idu==N);
if numel(idf)>1
... your code
Z{k} = T; % add data
else
... decide what you want to do with zero/one names.
end
end
Z = [Z{:}] % concatenate
Depending on how you decide to handle the one-name groups, you might need to use indexing for the concatenation as well.

Sign in to comment.