Find the closest value in each group in a matrix column - matlab

This is another operation based on the same matrix data, as I talked about in the previous question. I have a matrix as below.
a = [1.05 2.1 3.4 1; 1.06 2.2 3.6 1; 2.04 2.3 3.8 2; 2.15 2.2 4.0 2; 1.37 2.3 3.7 1;3.12 2.1 4.1 3;3.02 2.2 4.2 3;3.42 2.3 4.5 3;3.24 2.4 4.8 3]
a =
1.0500 2.1000 3.4000 1.0000
1.0600 2.2000 3.6000 1.0000
2.0400 2.3000 3.8000 2.0000
2.1500 2.2000 4.0000 2.0000
1.3700 2.3000 3.7000 1.0000
3.1200 2.1000 4.1000 3.0000
3.0200 2.2000 4.2000 3.0000
3.4200 2.3000 4.5000 3.0000
3.2400 2.4000 4.8000 3.0000
a(:,4) is group numbers. Based on group numbers, I split the matrix data into 3 groups: 1, 2 and 3.
I would like to find the index of value closest to 2.2 in a(:,2) in each group. From the data you can see, there is a 2.2 in row 2 belong to group 1, a 2.2 in row 4 belong to group 2, and a 2.2 in row 7 belong to group 3.
My code is shown as below:
[minValue,closestIndex] = splitapply(#(x)min(abs(2.2-x)), a(:,2), findgroups(a(:,4)))
The outcome is:
minValue =
0
0
0
This is consistent as we can find from the data.
closestIndex =
2
2
2
This is supposed to be the indexes of three 2.2 in the matrix, which should be 2, 4 and 7. But the outcome is 2, 2 and 2.
What is wrong with my code?
How to solve this problem?

I think you misunderstand how this function works. It split your initial setup:
a =
1.0500 2.1000 3.4000 1.0000
1.0600 2.2000 3.6000 1.0000
2.0400 2.3000 3.8000 2.0000
2.1500 2.2000 4.0000 2.0000
1.3700 2.3000 3.7000 1.0000
3.1200 2.1000 4.1000 3.0000
3.0200 2.2000 4.2000 3.0000
3.4200 2.3000 4.5000 3.0000
3.2400 2.4000 4.8000 3.0000
to
x1=
1.0500 2.1000 3.4000 1.0000
1.0600 2.2000 3.6000 1.0000 (index = 2)
1.3700 2.3000 3.7000 1.0000
x2=
2.0400 2.3000 3.8000 2.0000
2.1500 2.2000 4.0000 2.0000 (index = 2)
x3=
3.1200 2.1000 4.1000 3.0000
3.0200 2.2000 4.2000 3.0000 (index = 2)
3.4200 2.3000 4.5000 3.0000
3.2400 2.4000 4.8000 3.0000
The indexes are examined separately so indeed - 2,2,2 is correct answer for this query. According to the documentation "The splitapply function calls func once per group" so there is not simple possibility to obtain indexes from initial matrix directly. Maybe you can workaround it by adding another column like 1 2 3 4 5 6 7 8 9 and store the number from it or something in this way but it is not so elegant and would require quite complicated func.

Related

An array of evenly spaced values when only first and last input is known

I want to create an array of N size, which has inputs evenly spaced apart. For clarification I have the first and last values of an array and want to fill the array N sized with inputs.
I have used linspace
bars = 10;
DeapA = 1;
DeapB = 10;
diameter_pin = (linspace(DeapA, DeapB, bars))
Which gives:
diamter_p =
1 2 3 4 5 6 7 8 9 10
However is there a way of doing this by filling an array as such ?
The reason I want to do this is because in more complicated versions of the above when I used the variable in an equation the values are not being calculated correctly
Not sure exactly what you are trying to do, so here are a few answers
Wrapping numbers around
bars = 10;
DeapA = 1;
DeapB = 10;
diameter_pin = reshape(linspace(DeapA, DeapB, bars*bars), bars, bars)
Gives an array where each column has equally spaced points, and the values keep increasing through the columns
diameter_pin =
1.0000 1.9091 2.8182 3.7273 4.6364 5.5455 6.4545 7.3636 8.2727 9.1818
1.0909 2.0000 2.9091 3.8182 4.7273 5.6364 6.5455 7.4545 8.3636 9.2727
1.1818 2.0909 3.0000 3.9091 4.8182 5.7273 6.6364 7.5455 8.4545 9.3636
1.2727 2.1818 3.0909 4.0000 4.9091 5.8182 6.7273 7.6364 8.5455 9.4545
1.3636 2.2727 3.1818 4.0909 5.0000 5.9091 6.8182 7.7273 8.6364 9.5455
1.4545 2.3636 3.2727 4.1818 5.0909 6.0000 6.9091 7.8182 8.7273 9.6364
1.5455 2.4545 3.3636 4.2727 5.1818 6.0909 7.0000 7.9091 8.8182 9.7273
1.6364 2.5455 3.4545 4.3636 5.2727 6.1818 7.0909 8.0000 8.9091 9.8182
1.7273 2.6364 3.5455 4.4545 5.3636 6.2727 7.1818 8.0909 9.0000 9.9091
1.8182 2.7273 3.6364 4.5455 5.4545 6.3636 7.2727 8.1818 9.0909 10.0000
You can take the transpose of this matrix if you want the rows to be increasing instead.
Repeating Rows/columns
Here, each row is the same
>> nRows = 5;
>> nCols = 10;
>> a = 1;
>> b = 10;
>> diameter_pin = repmat(linspace(a, b, nCols), nRows, 1)
diameter_pin =
1 2 3 4 5 6 7 8 9 10
1 2 3 4 5 6 7 8 9 10
1 2 3 4 5 6 7 8 9 10
1 2 3 4 5 6 7 8 9 10
1 2 3 4 5 6 7 8 9 10
Here, each column is the same
>> nRows = 5;
>> nCols = 10;
>> a = 1;
>> b = 10;
>> diameter_pin = repmat(linspace(a, b, nRows)', 1, nCols)
diameter_pin =
1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000
3.2500 3.2500 3.2500 3.2500 3.2500 3.2500 3.2500 3.2500 3.2500 3.2500
5.5000 5.5000 5.5000 5.5000 5.5000 5.5000 5.5000 5.5000 5.5000 5.5000
7.7500 7.7500 7.7500 7.7500 7.7500 7.7500 7.7500 7.7500 7.7500 7.7500
10.0000 10.0000 10.0000 10.0000 10.0000 10.0000 10.0000 10.0000 10.0000 10.0000
Equally Spaced points from top-left to bottom-right
In this matrix, each row is equally spaced with the same difference, and each column is equally spaced with the same difference. If the number of rows and columns are the same, then the entire matrix is equally spaced!
First, a rectangular matrix:
>> nRows = 10;
>> nCols = 5;
>> a = 1;
>> b = 10;
>> x = linspace(a, b, nCols);
>> y = linspace(a, b, nRows);
>> [xx, yy] = meshgrid(x, y);
>> zz = mean(cat(3, xx, yy), 3)
zz =
1.0000 2.1250 3.2500 4.3750 5.5000
1.5000 2.6250 3.7500 4.8750 6.0000
2.0000 3.1250 4.2500 5.3750 6.5000
2.5000 3.6250 4.7500 5.8750 7.0000
3.0000 4.1250 5.2500 6.3750 7.5000
3.5000 4.6250 5.7500 6.8750 8.0000
4.0000 5.1250 6.2500 7.3750 8.5000
4.5000 5.6250 6.7500 7.8750 9.0000
5.0000 6.1250 7.2500 8.3750 9.5000
5.5000 6.6250 7.7500 8.8750 10.0000
Now you can see the columns are equally spaced:
>> diff(zz)
ans =
0.5000 0.5000 0.5000 0.5000 0.5000
0.5000 0.5000 0.5000 0.5000 0.5000
0.5000 0.5000 0.5000 0.5000 0.5000
0.5000 0.5000 0.5000 0.5000 0.5000
0.5000 0.5000 0.5000 0.5000 0.5000
0.5000 0.5000 0.5000 0.5000 0.5000
0.5000 0.5000 0.5000 0.5000 0.5000
0.5000 0.5000 0.5000 0.5000 0.5000
0.5000 0.5000 0.5000 0.5000 0.5000
And the rows are equally spaced
>> diff(zz')'
ans =
1.1250 1.1250 1.1250 1.1250
1.1250 1.1250 1.1250 1.1250
1.1250 1.1250 1.1250 1.1250
1.1250 1.1250 1.1250 1.1250
1.1250 1.1250 1.1250 1.1250
1.1250 1.1250 1.1250 1.1250
1.1250 1.1250 1.1250 1.1250
1.1250 1.1250 1.1250 1.1250
1.1250 1.1250 1.1250 1.1250
1.1250 1.1250 1.1250 1.1250
If you want the entire matrix to be equally spaced, then you need the same number of rows and columns. Here is the translation of your code:
>> bars = 10;
>> DeapA = 1;
>> DeapB = 10;
>> x = linspace(DeapA, DeapB, bars);
>> diameter_pin = nan(bars, bars, 2);
>> [diameter_pin(:, :, 1), diameter_pin(:, :, 2)] = meshgrid(x, x);
>> diameter_pin = mean(diameter_pin, 3)
diameter_pin =
1.0000 1.5000 2.0000 2.5000 3.0000 3.5000 4.0000 4.5000 5.0000 5.5000
1.5000 2.0000 2.5000 3.0000 3.5000 4.0000 4.5000 5.0000 5.5000 6.0000
2.0000 2.5000 3.0000 3.5000 4.0000 4.5000 5.0000 5.5000 6.0000 6.5000
2.5000 3.0000 3.5000 4.0000 4.5000 5.0000 5.5000 6.0000 6.5000 7.0000
3.0000 3.5000 4.0000 4.5000 5.0000 5.5000 6.0000 6.5000 7.0000 7.5000
3.5000 4.0000 4.5000 5.0000 5.5000 6.0000 6.5000 7.0000 7.5000 8.0000
4.0000 4.5000 5.0000 5.5000 6.0000 6.5000 7.0000 7.5000 8.0000 8.5000
4.5000 5.0000 5.5000 6.0000 6.5000 7.0000 7.5000 8.0000 8.5000 9.0000
5.0000 5.5000 6.0000 6.5000 7.0000 7.5000 8.0000 8.5000 9.0000 9.5000
5.5000 6.0000 6.5000 7.0000 7.5000 8.0000 8.5000 9.0000 9.5000 10.0000
This matrix is equally spaced along each dimension.
Well that's all I could think of. I encourage you to check out the MATLAB help documentation for all the functions I called.
If you want to avoid using linspace (though I don't see why), you could use:
diameter_pin = DeapA : (DeapB-DeapA)/(bars-1) : DeapB;

Efficiently finding a value from one vector in another matlab: error checking if empty

I have 3 vectors: npdf, tn(:,1) and tn(:,2) and am finding the values of npdf in tn(:,2) line by line:
[npdf(1:20,1), tn(1:20,:)]
ans =
8.0000 3.0000 1.0000
11.0000 2.9167 1.0000
1.0000 3.3000 1.0000
11.0000 1.2167 1.0000
5.0000 2.8167 1.0000
1.0000 2.4000 1.0000
2.0000 2.4500 1.0000
4.0000 0.2500 1.0000
15.0000 3.7500 1.0000
15.0000 4.9167 1.0000
1.0000 2.8167 2.0000
17.0000 0.2500 2.0000
15.0000 1.0000 3.0000
4.0000 3.0000 3.0000
8.0000 0.5833 3.0000
1.0000 0.5833 3.0000
3.0000 5.0000 5.0000
11.0000 3.7500 6.0000
8.0000 3.0000 7.0000
15.0000 2.8000 7.0000
for i=1:length(npdf)
[LOCA,~]=ismember(tn(:,2),npdf(i,1,1));
dummy=find(LOCA~=0);
tpdf(i,1)=tn(randi(length(dummy),1,1),1);
end
each time it finds the value of npdf in tn(:,2) it chooses a value from tn(:,1).
Here's the problem: if it can't locate the value from npdf in tn(:,2) then I need to choose the nearest value (in magnitude) in tn(:,2) and proceed. Either that or some sort of interpolation between nearest values.. How would you do this most efficiently?
At your discretion to change the code, it doesn't look very efficient to me.
It can be done easily by using knnsearch as follows:
[idx,D]=knnsearch(tn(:,2),npdf,'K',size(tn,1));
for i=1:size(D,1)
tpdf(i,1)=tn(randi(sum(D(i,:)==min(D(i,:))),1,1),1);
end
It finds distance of each value in npdf to all the values in tn. Then it considers only the nearest value. Then it selects a random indices from tn(:,1) as per your code.

How do I append elements to a matrix using a for loop?

I have following data matrix, I want to iterate over this matrix and look at a value in the last column based on a given row and add that row - last element of that row to a new matrix.
5.1000 3.3000 1.7000 0.5000 1.0000
6.8000 3.2000 5.9000 2.3000 3.0000
5.0000 2.3000 3.3000 1.0000 2.0000
7.4000 2.8000 6.1000 1.9000 3.0000
6.5000 3.2000 5.1000 2.0000 3.0000
4.8000 3.4000 1.9000 0.2000 1.0000
4.9000 3.0000 1.4000 0.2000 1.0000
5.1000 3.8000 1.5000 0.3000 1.0000
5.1000 3.4000 1.5000 0.2000 1.0000
5.5000 2.6000 4.4000 1.2000 2.0000
This is the code that I have
M1 = [];
M2 = [];
M3 = [];
for i=1:length(currentCell)
if currentCell(1,5) == 1.00
m3Data = currentCell(1:1,1:4);
%how can I add m3Data to M1
end
end
Let your original matrix be M, then this
M1 = M(find(M(:,5)==1),1:4)
puts all the rows ending with a 1 into M1, excluding the final column. Is that what you want ?
You could do it with a for loop if you want, but I don't see any need.

extracting variable from multiple matlab files into one file

I have this project task, and I'm having problems solving it.
I took samples of the words from 1 to 10 spoken by 10 people.
From each sample I extracted each word e.g, I extracted the word 1 from all samples into different files. I now have 10 files each having the extracted first word. I want to combine these into one single array.
file = wavread( 'G:\Segmented Data\amir.wav');
t = linspace(0,8,length(file));
t2=linspace(0,.8,8820);
section1 = file(1:8820,:);
sound(section1, 11025);
figure(1),
plot(t2,section1);
I have 10 files having the above code. I want to extract the variable section from all these into a new file, and store them in an array.
Do you want to concatenate arrays?
>> a = 1.0:0.1:1.9 % your data, obtained from `wavread()`
a =
1.0000 1.1000 1.2000 1.3000 1.4000 1.5000 1.6000 1.7000 1.8000 1.9000
>> b = 2.0:0.1:2.9 % your data, obtained from `wavread()`
b =
2.0000 2.1000 2.2000 2.3000 2.4000 2.5000 2.6000 2.7000 2.8000 2.9000
>> c = 3.0:0.1:3.9 % your data, obtained from `wavread()`
c =
3.0000 3.1000 3.2000 3.3000 3.4000 3.5000 3.6000 3.7000 3.8000 3.9000
>> combined = [a; b; c] % a, b, and c in one array
combined =
1.0000 1.1000 1.2000 1.3000 1.4000 1.5000 1.6000 1.7000 1.8000 1.9000
2.0000 2.1000 2.2000 2.3000 2.4000 2.5000 2.6000 2.7000 2.8000 2.9000
3.0000 3.1000 3.2000 3.3000 3.4000 3.5000 3.6000 3.7000 3.8000 3.9000
This will, of course, only work if a, b and c are the same number of columns wide. If they are different sizes, you will have to pad them out with zeros so that they are the same size.

Is there a way to do partial average over matrix in matlab

I have a matrix (table actually) which I imported from a file:
1.0000 1.9736
4.0000 0.2016
9.0000 0.0584
10.0000 0.0495
5.0000 0.1845
2.0000 0.6873
1.0000 1.4177
2.0000 0.4699
5.0000 0.1555
10.0000 0.0435
13.0000 0.0326
8.0000 0.0860
5.0000 0.1685
4.0000 0.1956
5.0000 0.1433
8.0000 0.0675
13.0000 0.0335
13.0000 0.0327
10.0000 0.0431
9.0000 0.0582
10.0000 0.0551
13.0000 0.0308
I want to get the average of each of the occurance on left column. That is:
avg = [
1.0000 1.69565
2.0000 0.5786
4.0000 0.1978]
and so on. I could do this with a wile or for group but this is not the matlab way. So how can I do this?
a=[randi(5,10,1) rand(10,1)];
a =
4.0000 0.4387
1.0000 0.3816
2.0000 0.7655
1.0000 0.7952
1.0000 0.1869
5.0000 0.4898
4.0000 0.4456
2.0000 0.6463
5.0000 0.7094
1.0000 0.7547
[uniqueID,~,uniqueInd]=unique(a(:,1));
[uniqueID accumarray(uniqueInd,a(:,2))./accumarray(uniqueInd,1)]
ans =
1.0000 0.5296
2.0000 0.7059
4.0000 0.4422
5.0000 0.5996
If your matrix is called a, try
>> accumarray(grp2idx(a(:,1)),a(:,2),[],#mean)
ans =
1.6957
0.5786
0.1986
0.16295
0.07675
0.0583
0.0478
0.0324
Note that grp2idx is part of Statistics Toolbox. If you don't have that, you can use the unique command to get the same results.