find and replace zeros with a function in matlab - matlab

Once again, sorry if this has been asked before and if its too specific but I'm very stuck and can't quite find a solution.
I have a matrix of say 3 members of a structure called 2, 4 and 16 (in column 1) that have values along their relative distance e.g. member 2 has values at the start, 0m, then at 0.5m then the end of its length 1.5m, where member 4 starts at 0m etc. So that my matrix looks like this:
2 0 125
2 0.5 25
2 1.5 365
4 0 25
4 0.6 57
16 0 354
16 0.2 95
16 0.8 2
and I want to create a matrix that has the overall distance along all the members 2, 4 and 16 combined:
2 0 125
2 0.5 25
2 1.5 365
4 1.5 25
4 2.1 57
16 2.1 354
16 2.3 95
16 3.1 2
is there any way to do this in matlab? Like possibly locating the first zero and adding the value above it to all the rest of the values below then find the next zero value and so on?
Please tell me if this isn't clear, I realise it's a bit confusing but not too sure how to explain it better!

I came up with the following:
idx = find(diff(M(:,1)));
v = zeros(size(M,1),1);
v(idx+1) = M(idx,2);
M(:,2) = M(:,2) + cumsum(v);
The result:
M =
2 0 125
2 0.5 25
2 1.5 365
4 1.5 25
4 2.1 57
16 2.1 354
16 2.3 95
16 2.9 2
Note the last value in the second column disagrees with what you described (2.9 vs 3.1). Either you had a typo, or I'm still not getting it...

data = [2 0 125;
2 0.5 25;
2 1.5 365;
4 0 25;
4 0.6 57;
16 0 354;
16 0.2 95;
16 0.8 2];
idx0 = find(data(:,2)==0);
idx0 = idx0(2:end); %ignore first zero of first member, doesn't need an offset
offset = data(idx0-1,2);
N = size(data,1);
for ii=1:numel(idx0)
idxs = 1:N>=idx0(ii);
data(idxs,2) = data(idxs,2) + offset(ii);
end

Related

All unique multiplication products

I'd like to obtain all unique products for a given vector.
For example, given a:
a = [4,10,12,3,6]
I want to obtain a matrix that contains the results of:
4*10
4*12
4*3
4*6
10*12
10*3
10*6
12*3
12*6
3*6
Is there a short and/or quick way of doing this in MATLAB?
EDIT: a may contain duplicate numbers, giving duplicate products - and these must be kept.
Given:
a =
4 10 12 3 6
Construct the matrix of all pairwise products:
>> all_products = a .* a.'
all_products =
16 40 48 12 24
40 100 120 30 60
48 120 144 36 72
12 30 36 9 18
24 60 72 18 36
Now, construct a mask to keep only those values below the main diagonal:
>> mask = tril(true(size(all_products)), -1)
mask =
0 0 0 0 0
1 0 0 0 0
1 1 0 0 0
1 1 1 0 0
1 1 1 1 0
and apply the mask to the product matrix:
>> unique_products = all_products(mask)
unique_products =
40
48
12
24
120
30
60
36
72
18
If you have the Statistics Toolbox, you can abuse pdist, which considers only one of the two possible orders for each pair:
result = pdist(a(:), #times);
One option involves nchoosek, which returns all combinations of k elements out of a vector, each row is one combination. prod computes the product of rows or columns:
a = [4,10,12,3,6];
b = nchoosek(a,2);
b = prod(b,2); % 2 indicates rows
Try starting with this. Have the unique function filter out the result of multiplying a by itself.
b = unique(a*a')

Embedding an array into another

I have two arrays. The first one is a consecutive sequential one, like:
seq1 =
1 0
2 0
3 0
4 0
5 0
6 0
7 0
8 0
9 0
10 0
...continues
The second one is like:
seq2 =
2 250
3 260
5 267
6 270
8 280
10 290
13 300
18 310
20 320
21 330
...continues
I need to embed seq2 into seq1 in such a way that I end up with the sequence:
seq3 =
1 0
2 250
3 260
4 260
5 267
6 270
7 270
8 280
9 280
10 290
11 290
... continues
I could do this with loops but the arrays are really big so I don't want to use two for loops, it is taking too long. How can I do this in a vectorised manner?
I think this does what you want:
[~, jj, vv] = find(sum(bsxfun(#le, seq2(:,1), seq1(:,1).'), 1));
seq3 = seq1;
seq3(jj,2) = seq2(vv,2);
How it works
The required index is obtained by computing how many values in the first column of seq2 are less than or equal to each value in the first column or seq1 (code sum(bsxfun(#le, ...), 1)). This will be used to select the appropriate entries from the second column of seq2 and write them into the result. But before that, the value 0 in this index needs to be discarded. This is done using the three-output version of find (code [~, jj, vv] = find(...)).
If your second column of data is always increasing, you can solve this easily with accumarray and cummax:
seq = [seq1; seq2];
seq3 = cummax(accumarray(seq(:, 1), seq(:, 2), [], #max));
seq3 = [(1:numel(seq3)).' seq3];
And here's what you get for your sample inputs:
seq3 =
1 0
2 250
3 260
4 260
5 267
6 270
7 270
8 280
9 280
10 290
11 290
12 290
13 300
14 300
15 300
16 300
17 300
18 310
19 310
20 320
21 330
How it works...
After concatenating seq1 and seq2, accumarray collects all the values in the second column that have the same value in the first column (i.e. [0 250] for the value 2), then gets the maximum value of each set. The function cummax is then used to fill any zero values with the previous non-zero value. Finally, an index column is added to the new sequence.

Comparing two array and combining them with condition

I have these matrices:
I1 = [60 30 15 35 20 -25 30 5 45 25 -10 40 10];
I2 = [60 30 60 35 20 60 30 60 45 25 60 40 60];
A= 0:12 ;
I want this:
Ir=[60 30 15 NaN 60 35 20 -25 NaN 60 30 5 NaN 60 45 25 -10 NaN 60 40 10 NaN 60]
Ar= [0 1 2 2 2 3 4 5 5 5 6 7 7 7 8 9 10 10 10 11 12 12 12]
How:
When I1 and I2 are same, proceed. When different, use elements of both I1 and I2 and insert an NaN between them.
and Ar is such that use element of A and proceed when elements of I1 and I2 are same. But when different, repeat the value of A 3 times. 1st for I1 and then for NaN and then for I2.
Can't figure out a way to do this. How do I do this?
A more vectorized way to do this would be to create an augmented matrix where the top row is I1, the bottom row is I2 and the middle row is full of NaN. After, create a logical matrix which is the same size as this augmented matrix you just created and you would adjust the columns of this matrix where you'd set the bottom two rows of this logical matrix to 0 for each corresponding column in this matrix where I1 == I2. Once you're done, use this logical matrix and index the augmented matrix. The advantage of this indexing is that it accesses the elements in column major format, which is exactly what you're after. We will only sample the top row of the augmented matrix unless the elements in I1 and I2 don't equal each other. If that's the case, we sample the entire column which includes both I1, nan and I2. Because we accessed by columns, your desired output is a row so you'll need to transpose the result when you're done.
To create the index vector, you would do the same thing but you would create a matrix of IDs where we have three rows where each row is the index ID array A. You'll also need to transpose this result after you index into A:
aug = [I1; nan(1,numel(I1)); I2];
V = true(size(aug));
V(2:3, I1 == I2) = false;
Ir = aug(V).';
ID = repmat(A, 3, 1);
Ar = ID(V).';
We get:
>> format compact
>> Ir
Ir =
Columns 1 through 17
60 30 15 NaN 60 35 20 -25 NaN 60 30 5 NaN 60 45 25 -10
Columns 18 through 23
NaN 60 40 10 NaN 60
>> Ar
Ar =
Columns 1 through 17
0 1 2 2 2 3 4 5 5 5 6 7 7 7 8 9 10
Columns 18 through 23
10 10 11 12 12 12
I can provide you with pseudo-code.
for each value in A
if I1 at [current A value] equals I2 at [current A value]
add I1 at [current A value] to end of Ir
add [current A value] to end of Ar
else
add I1 at [current A value] to end of Ir
add NaN to Ir
add I2 at [current A value] to end of Ir
add [current A value] to Ar 3 times
If your problem was conceptual hopefully this is enough.
Let me know if you have any further questions.
You can use two indices and solve the problem. The first index ii goes over the input vectors, and the second index k grows as the computation proceeds. A simple code using for-loop and if-statement looks like this:
I1 = [60 30 15 35 20 -25 30 5 45 25 -10 40 10];
I2 = [60 30 60 35 20 60 30 60 45 25 60 40 60];
Ir = [];
Ar = [];
k = 1;
for ii=1:length(I1)
if I1(ii)==I2(ii)
Ir(k) = I1(ii);
Ar(k) = ii-1;
k = k + 1;
else
Ir(k:k+2) = [I1(ii) NaN I2(ii)];
Ar(k:k+2) = ii-1;
k = k + 3;
end
end
Will result in:
Ir =
60 30 15 NaN 60 35 20 -25 NaN 60 30 5 NaN 60 45 25 -10 NaN 60 40 10 NaN 60
Ar =
0 1 2 2 2 3 4 5 5 5 6 7 7 7 8 9 10 10 10 11 12 12 12

Combination of arrays in Matlab

I have a problem on a part of a program and I would appreciate some help.
My main objective is to use all possible pairs in two arrays. With some help i managed to get this
A = nchoosek(0:15, 2)
arr1 = A(:,1);
arr2 = A(:,2);
Result = arr1.*arr2 + arr1.^2 + arr2.^2;
I want to use all the combinations in arr1 and arr2 to solve the result equation and print out the result like this:
arr1 arr2 Result
0 0 0
1 1 3
2 0 4
and so on.. but not all the combinations are used when I try this approach. What should I do to get all the possible combinations?
Matlab has meshgrid function to eliminate loops for this purpose, for example
>> a1=[1:4];
>> a2=[0:3];
>> [x1,x2]=meshgrid(a1,a2);
>> r=x1.*x2+x1.^2+x2.^2;
or to use square once
>> r1=(x1+x2).^2-x1.*x2;
UPDATE: for your case you use 0:15 values, using them will result with
>> a1=[0:15];a2=[0:15];
>> [x1,x2]=meshgrid(a1,a2);
>> r=-x1.*x2+(x1+x2).^2;
>> size(r)
ans =
16 16
UPDATE 2 Note that your method doesn't create all pairs, for example (0,0) or (1,1) won't be there also only of one of the (x,y) (y,x) pairs will be there for x!=y values. Other than double loops the preferred approach is what I proposed. You can gather the results in a matrix in the form you want easily as well
>> n=size(r,1);
>> R=[reshape(x1,1,n*n); reshape(x2,1,n*n); reshape(r,1,n*n)]'
R =
0 0 0
0 1 1
0 2 4
0 3 9
0 4 16
0 5 25
0 6 36
0 7 49
...
15 6 351
15 7 379
15 8 409
15 9 441
15 10 475
15 11 511
15 12 549
15 13 589
15 14 631
15 15 675

Find closest matching distances for a set of points in a distance matrix in Matlab

I have a matrix of measured angles between M planes
0 52 77 79
52 0 10 14
77 10 0 3
79 14 3 0
I have a list of known angles between planes, which is an N-by-N matrix which I name rho. Here's is a subset of it (it's too large to display):
0 51 68 75 78 81 82
51 0 17 24 28 30 32
68 17 0 7 11 13 15
75 24 7 0 4 6 8
78 28 11 4 0 2 4
81 30 13 6 2 0 2
82 32 15 8 4 2 0
My mission is to find the set of M planes whose angles in rho are nearest to the measured angles.
For example, the measured angles for the planes shown above are relatively close to the known angles between planes 1, 2, 4 and 6.
Put differently, I need to find a set of points in a distance matrix (which uses cosine-related distances) which matches a set of distances I measured. This can also be thought of as matching a pattern to a mold.
In my problem, I have M=5 and N=415.
I really tried to get my head around it but have run out of time. So currently I'm using the simplest method: iterating over every possible combination of 3 planes but this is slow and currently written only for M=3. I then return a list of matching planes sorted by a matching score:
function [scores] = which_zones(rho, angles)
N = size(rho,1);
scores = zeros(N^3, 4);
index = 1;
for i=1:N-2
for j=(i+1):N-1
for k=(j+1):N
found_angles = [rho(i,j) rho(i,k) rho(j,k)];
score = sqrt(sum((found_angles-angles).^2));
scores(index,:)=[score i j k];
index = index + 1;
end
end;
end
scores=scores(1:(index-1),:); % was too lazy to pre-calculate #
scores=sortrows(scores, 1);
end
I have a feeling pdist2 might help but not sure how. I would appreciate any help in figuring this out.
There is http://www.mathworks.nl/help/matlab/ref/dsearchn.html for closest point search, but that requires same dimensionality. I think you have to bruteforce find it anyway because it's just a special problem.
Here's a way to bruteforce iterate over all unique combinations of the second matrix and calculate the score, after that you can find the one with the minimum score.
A=[ 0 52 77 79;
52 0 10 14;
77 10 0 3;
79 14 3 0];
B=[ 0 51 68 75 78 81 82;
51 0 17 24 28 30 32;
68 17 0 7 11 13 15;
75 24 7 0 4 6 8;
78 28 11 4 0 2 4;
81 30 13 6 2 0 2;
82 32 15 8 4 2 0];
M = size(A,1);
N = size(B,1);
% find all unique permutations of `1:M`
idx = nchoosek(1:N,M);
K = size(idx,1); % number of combinations = valid candidates for matching A
score = NaN(K,1);
idx_triu = triu(true(M,M),1);
Atriu = A(idx_triu);
for ii=1:K
partB = B(idx(ii,:),idx(ii,:));
partB_triu = partB(idx_triu);
score = norm(Atriu-partB_triu,2);
end
[~, best_match_idx] = min(score);
best_match = idx(best_match_idx,:);
The solution of your example actually is [1 2 3 4], so the upperleft part of B and not [1 2 4 6].
This would theoretically solve your problem, and I don't know how to make this algorithm any faster. But it will still be slow for large numbers. For example for your case of M=5 and N=415, there are 100 128 170 583 combinations of B which are a possible solution; just generating the selector indices is impossible in 32-bit because you can't address them all.
I think the real optimization here lies in cutting away some of the planes in the NxN matrix in a preceding filtering part.