I have two matrices like this:
gt = [30 40 20 40] and
de = [32 42 20 40; 34 12 20 40; 36 84 20 40]
I want to calculate the overlap area between gt and 3 rows of de respectively and the overlap is calculated by a function I write myself. Then I want to store the result in a new column vector like
result = [result1; result2; result3].
Could you tell me how to write a vectorization codes to achieve this?
Thanks!
The vectorization can only happen inside the overlap function. The only thing you can do outside it is replicate the vector gt, using repmat or bsxfun. You don't explain how the overlap function works. I suppose it has to do with co-ordinates, so I give an example for euclidean distance which works in a similar logic.
If you had to calculate the distance between point gt = [1 2] and points de = [5 6; 10 12; 0 -1] you would define
function result = dist(x, y)
result = sum(sqrt((x(:,1) - y(:,1)).^2 + (x(:,2) - y(:,2)).^2), 2)
and you would call it replicating the gt vector
dist(de, repmat(gt, 3, 1))
Alternatively, you could use bsxfun instead of repmat, which might have better performance (depending on various factors)
The key to vectorizing is performing operations column-wise (in this specific case it could be vectorized even further, however I am writing it this way to emphasize the column-wise operations)
Related
I have several variables, all of which are numeric ranges: (intervals in rows)
a = [ 1 4; 5 9; 11 15; 20 30];
b = [ 2 6; 12 14; 19 22];
c = [ 15 22; 24 29; 33 35];
d = [ 0 3; 15 17; 23 26];
(The values in my real dataset are not integers, but are represented here as such for clarity).
I would like to find intervals in which at least 3 of the variables intersect. In the above example [20 22] and [24 26] would be two such cases.
One way to approach this is to bin my values and add the bins together, but as my values are continuous, that'd create an 'edge effect', and I'd lose time binning the values in the first place. (binning my dataset at my desired resolution would create hundreds of GB of data).
Another approach, which doesn't involve binning, would use pairwise intersections (let's call it X) between all possible combinations of variables, and then the intersection of X with all other variables O(n^3).
What are your thoughts on this? Are there algorithms/libraries which have tools to solve this?
I was thinking of using sort of a geometric approach to solve this: Basically, if I considered that my intervals were segments in 1D space, then my desired output would be points where three segments (from three variables) intersect. I'm not sure if this is efficient algorithmically though. Advice?
O(N lg N) method:
Convert each interval (t_A, t_B) to a pair of tagged endpoints ('begin', t_A), ('end', t_B)
Sort all the endpoints by time, this is the most expensive step
Do one pass through, tracking nesting depth (increment if tag is 'start', decrement if tag is 'end'). This takes linear time.
When the depth changes from 2 to 3, it's the start of an output interval.
When it changes from 3 to 2, it's the end of an interval.
Indeed, my problem is a succession of my previous problem:
1) Extract submatrices, 2) vectorize and then 3) put back
Thanks to Dan and his ideas works perfectly for the purpose.
My new problem is this:
If I have a 3D matrix, 8 by 8 by 12, e.g. A = randn(8,8,12).
Let's see the linear index of the first slice:
From Dan's solution, I understand that A[4:6, 4:6, :] can extract the corresponding parts of all slices.
However, going back to my real situations, extracting parts by actually counting rows and columns seem not suit my purpose because my matrix size is huge and I do have many sub-matrices to be extracted.
So, I prefer to work on linear index and want to ask if there are any ways to work with this possibility.
Here is my trial:
By defining sub_group = [28 29 30 36 37 38 44 45 46], then A(sub_group) can extract sub-matrix from the first slice of the 3D matrix, A.
I understand that A(sub_group + 8*8*(n-1)) can extract the sub-matrix from the nth slice.
I aim to only work with my sub_group and then extract the same part of every slice.
Most importantly, I have to put back the sub-matrices after updating their values.
So, is there are any quick syntax for matlab to work for my purpose?
I appreciate for your help.
Approach #1
For cases like this when you need to calculate linear indices, you can use bsxfun as shown here -
%// Store number of rows in A as a variable
M = size(A,1)
%// Get start and offset linear indices for the first slice and thus sub_group
start_idx = (colstart-1)*M + rowstart
offset_idx = bsxfun(#plus,[0:rowstop - rowstart]', [0:colstop-colstart]*M) %//'
sub_group = reshape(start_idx + offset_idx,1,[])
%// Calculate sub_groups for all 3D slices
all_sub_groups = bsxfun(#plus,sub_group',[0:size(A,3)-1]*numel(A(:,:,1)))
Sample run -
A(:,:,1) =
0.096594 0.52368 0.76285 0.83984 0.27019
0.84588 0.65035 0.57569 0.42683 0.4008
0.9094 0.38515 0.63192 0.63162 0.55425
0.011341 0.6493 0.2782 0.83347 0.44387
A(:,:,2) =
0.090384 0.037262 0.38325 0.89456 0.89451
0.74438 0.9758 0.88445 0.39852 0.21417
0.032615 0.52234 0.25502 0.62502 0.0038592
0.42974 0.90963 0.90905 0.5676 0.88058
rowstart =
2
rowstop =
4
colstart =
3
colstop =
5
sub_group =
10 11 12 14 15 16 18 19 20
all_sub_groups =
10 30
11 31
12 32
14 34
15 35
16 36
18 38
19 39
20 40
Approach #2
For a quick syntax based solution, sub2ind could be suggested here. The implementation would look something like this -
[X,Y] = ndgrid(rowstart:rowstop,colstart:colstop);
sub_group = sub2ind(size(A(:,:,1)),X,Y);
[X,Y,Z] = ndgrid(rowstart:rowstop,colstart:colstop,1:size(A,3));
all_sub_groups = sub2ind(size(A),X,Y,Z);
I am trying to find the Extremum of a 3-dim matrix along the 2nd dimension.
I started with
[~,index] = max(abs(mat),[],2), but I don't know how to advance from here. How is the index vector to be used together with the original matrix. Or is there a completely different solution to this problem?
To illustrate the task assume the following matrix:
mat(:,:,1) =
23 8 -4
-1 -26 46
mat(:,:,2) =
5 -27 12
2 -1 18
mat(:,:,3) =
-10 49 39
-13 -46 41
mat(:,:,4) =
30 -24 18
-40 -16 -36
The expected result would then be
ext(:,:,1) =
23
-46
ext(:,:,2) =
-27
18
ext(:,:,3) =
49
-46
ext(:,:,4) =
30
-40
I don't know how to use the index vector with mat to get the desired result ext.
1) If you want to find a maximum just along, let's say, 2d dimension, your variable index will be a matrix having dimensions (N,1,M), where N and M are number of elements of your matrix in the first and third dimensions respectively. In order to remove dummy dimensions, there is function squeeze() exist: index=squeeze(index) After that size(index) gives N,M
2) Depending on your problem, you probably need matlab function ind2sub(). First, you take a slice of your matrix, than find its maximum with linear indexing, and than you can restore your indicies with int2sub(). Here is an example for a 2D matrix:
M = randn(5,5);
[C,I] = max(M(:));
[index1,index2] = ind2sub(size(M),I);
Same method allows to find the absolute maximal element in whole 3D matrix.
Use ndgrid to generate the values along dimensions 1 and 3, and then sub2ind to combine the three indices into a linear index:
[~, jj] = max(abs(mat),[],2); %// jj: returned by max
[ii, ~, kk] = ndgrid(1:size(mat,1),1,1:size(mat,3)); %// ii, kk: all combinations
result = mat(sub2ind(size(mat), ii, jj, kk));
A fancier, one-line alternative:
result = max(complex(mat),[],2);
This works because, acccording to max documentation,
For complex input A, max returns the complex number with the largest complex modulus (magnitude), computed with max(abs(A)).
In matlab, after meeting a specific criterion, I used to return back the pixel itself and store it in the vector pixels as follows:
pixels(index) = y(i,j);
Now, I would like to return the location of those pixels. Should I do the following?
pixels(index) = i,j;
EDIT
If I want to then set those indexes to the value 1, I do the following, right?
for i=1:m
for j=1:n
y(i,j)=1
end
end
Thanks.
It is extremely inefficient to do so in a nested loop in Matlab.
Using sub2ind can help you do so much faster:
y( sub2ind( size(y), i, j ) ) = 1;
EDIT - sub2ind
What sub2ind does?
Suppose you have a matrix M of size [4 6]:
M = [ 1 5 9 13 17 21
2 6 10 14 18 22
3 7 11 15 19 23
4 8 12 16 20 24 ];
You wish to access two elements: the one at the first row and second column, and another at the fourth row and the fifth column.
In that case you have the rows you wish to access r = [ 1 4 ] and the columns you wish to access c = [ 2 5 ]. However, if you try and access
>> M( r, c )
This is a 2x2 matrix
ans =
5 17
8 20
And not the two elements you were looking for (which are 5 and 20).
What sub2ind does is convert the row/column indices you have into linear indices
>> sub2ind( size(M), r, c )
ans =
5 20
which happens to be the linear indices of the requested entries.
You can think of linear indices as the single index required to access an element in a matrix in the case that the matrix was converted to a vector stacking its columns one after the other.
A few comments:
Matlab has a few ways of indexing matrices: by row / column indices (like i and j in your question). By linear indices (like index in your question). However, the more efficient way is to use logical indexing: that is, using a matrix of the same size as y with true for the entries you wish to set / get.
So, in your example, if you could get such a logical matrix instead of index or i and j it would have been better.
Matlab has many advantages over other programing languages. One of them is its ability to perform vector/matrix operations extremely efficient. Resorting to loops, or worse, nested loops, is something that should be avoided in Matlab.
It is not a good practice to use i and j as variables in Matlab.
If you want to find the occurrence of a value y(i,j) simply evaluate
idx = (pixels == y(i,j));
Depending on your variables you can then probably do
index(idx) = 1;
I have a two-column matrix M that contains the start/end indices of a bunch of intervals:
startInd EndInd
1 3
6 10
12 12
15 16
How can I generate a vector of all the interval indices:
v = [1 2 3 6 7 8 9 10 12 15 16];
I'm doing the above using loops, but I'm wondering if there's a more elegant vectorized solution?
v = [];
for i=1:size(M,1)
v = [v M(i,1):M(i,2)];
end
Here's a vectorized solution I like to use for this particular problem, using the function cumsum:
v = zeros(1, max(endInd)+1); % An array of zeroes
v(startInd) = 1; % Place 1 at the starts of the intervals
v(endInd+1) = v(endInd+1)-1; % Add -1 one index after the ends of the intervals
v = find(cumsum(v)); % Perform a cumulative sum and find the nonzero entries
cell2mat(arrayfun(#colon,M(:,1)',M(:,2)','UniformOutput',false))
I don't have IMFILL, but on my machine this method is faster than the other suggestions and I think would beat the IMFILL method due to the use of find.
It can be made even faster if M is set up transposed (and we adjust the third and fourth arguments of arrayfun).
There's probably an even better solution I'm somehow not seeing, but here's a version using IMFILL
startInd = [1,6,12,15];
endInd = [3,10,12,16];
%# create a logical vector with starts and ends set to true to prepare for imfill
tf = false(endInd(end),1);
tf([startInd,endInd]) = true;
%# fill at startInd+1 wherever startInd is not equal endInd
tf = imfill(tf,startInd(startInd~=endInd)'+1); %' SO formatting
%# use find to get the indices
v = find(tf)' %' SO formatting
v =
1 2 3 6 7 8 9 10 12 15 16
Very weird solution IMHO creating temporary strings and using EVAL. Can be also one-liner.
tmp = cellstr(strcat(num2str(M(:,1)),{':'},num2str(M(:,2)),{' '}));
v = eval(['[' cell2mat(tmp') ']']);
I know it will probably not work on large matrix. Just for fun.