Consider a matrix M and a set of subscripts stored in columns I and J. I need to access the elements designated by I & J without converting them to linear indices (i.e. using sub2ind). E.g.
M = [1 2 3;4 5 6;7 8 9];
I = [1 1 1];
J = [1 2 3];
VALS = [1 2 3];
Also, doing the following is not feasible since I & J are huge :
VALS = diag(M(I,J));
And for demonstration, this is not what I'm looking for,
VALS = M(sub2ind(size(M),I,J));
Essentially sub2ind seems to be taking a lot of time and right now I'm looking for methods to access these elements without converting the subscripts to indices. Any other way is feasible as long as it's faster than the method using sub2ind.
This may be faster than using SUB2IND:
[r,c] = size(M); % Get the size of M
vals = M(I+r.*(J-1)); % Compute a linear index with vector operations
Related
I have a situation analogous to the following
z = magic(3) % Data matrix
y = [1 2 2]' % Column indices
So,
z =
8 1 6
3 5 7
4 9 2
y represents the column index I want for each row. It's saying I should take row 1 column 1, row 2 column 2, and row 3 column 2. The correct output is therefore 8 5 9.
I worked out I can get the correct output with the following
x = 1:3;
for i = 1:3
result(i) = z(x(i),y(i));
end
However, is it possible to do this without looping?
Two other possible ways I can suggest is to use sub2ind to find the linear indices that you can use to sample the matrix directly:
z = magic(3);
y = [1 2 2];
ind = sub2ind(size(z), 1:size(z,1), y);
result = z(ind);
We get:
>> result
result =
8 5 9
Another way is to use sparse to create a sparse matrix which you can turn into a logical matrix and then sample from the matrix with this logical matrix.
s = sparse(1:size(z,1), y, 1, size(z,1), size(z,2)) == 1; % Turn into logical
result = z(s);
We also get:
>> result
result =
8
5
9
Be advised that this only works provided that each row index linearly increases from 1 up to the end of the rows. This conveniently allows you to read the elements in the right order taking advantage of the column-major readout that MATLAB is based on. Also note that the output is also a column vector as opposed to a row vector.
The link posted by Adriaan is a great read for the next steps in accessing elements in a vectorized way: Linear indexing, logical indexing, and all that.
there are many ways to do this, one interesting way is to directly work out the indexes you want:
v = 0:size(y,2)-1; %generates a number from 0 to the size of your y vector -1
ind = y+v*size(z,2); %generates the indices you are looking for in each row
zinv = z';
zinv(ind)
>> ans =
8 5 9
Does any one know how I can convert a row vector [6 8 2] into a column vector without using a builtin command. I would like to do it without for loop.A ny idea please. Some one asked me is it a home work I say no, it's part of my work. I am trying to convert MATLAB code to vhdl using hdl coder but hdl coder seems not supporting transpose function.
Some options:
R = 1:10; %// A row vector
%// using built-in transpose
C = R'; %'// be warned this finds the complex conjugate
C = R.'; %'// Just swaps the rows and columns
C = transpose(R);
%// Flattening with the colon operator
C = R(:); %// usually the best option as it also convert columns to columns...
%// Using reshape
C = reshape(R,[],1);
%// Using permute
C = permute(R, [2,1]);
%// Via pre-allocation
C = zeros(numel(R),1);
C(1:end) = R(1:end);
%// Or explicitly using a for loop (note that you really should pre-allocate using zeros for this method as well
C = zeros(numel(R),1); %// technically optional but has a major performance impact
for k = 1:numel(R)
C(k,1) = R(k); %// If you preallocated then C(k)=R(k) will work too
end
%// A silly matrix multiplication method
C = diag(ones(numel(R),1)*R)
You can use the (:) trick
t = [1 2 3];
t(:)
ans =
1
2
3
UPDATE: you should use this method only in the following case: you have a vector (not matrix) and want to make sure it is a column-vector. This method is useful, when you don't know what type (column, row) of vector a variable has.
Check this out
t = [1 2 3]'; %// column vector
t(:)
ans =
1
2
3
However
A=magic(3);
A(:)
ans =
8
3
4
1
5
9
6
7
2
I have a 3d matrix H(i,j,k) with dimensions (i=1:m,j=1:n,k=1:o). I will use a simple case with m=n=o = 2:
H(:,:,1) =[1 2; 3 4];
H(:,:,2) =[5 6; 7 8];
I want to filter this matrix and project it to an (m,n) matrix by selecting for each j in 1:n a different k in 1:0.
For instance, I would like to retrieve (j,k) = {(1,2), (2,1)}, resulting in matrix G:
G = [5 2; 7 4];
This can be achieved with a for loop:
filter = [2 1]; % meaning filter (j,k) = {(1,2), (2,1)}
for i = 1:length(filter)
G(:,i) = squeeze(H(:,i,filter(i)));
end
But I'm wondering if it is possible to avoid the for loop via some smart indexing.
You can create all the linear indices to get such an output with the expansion needed for the first dimension with bsxfun. The implementation would look like this -
szH = size(H)
offset = (filter-1)*szH(1)*szH(2) + (0:numel(filter)-1)*szH(1)
out = H(bsxfun(#plus,[1:szH(1)].',offset))
How does it work
(filter-1)*szH(1)*szH(2) and (0:numel(filter)-1)*szH(1) gets the linear indices considering only the third and second dimension elements respectively. Adding these two gives us the offset linear indices.
Add the first dimenion linear indices 1:szH(1) to offset array in elementwise fashion with bsxfun to give us the actual linear indices, which when indexed into H would be the output.
Sample run -
H(:,:,1) =
1 2
3 4
H(:,:,2) =
5 6
7 8
filter =
2 1
out =
5 2
7 4
I have a 3 dimensional (or higher) array that I want to aggregate by another vector. The specific application is to take daily observations of spatial data and average them to get monthly values. So, I have an array with dimensions <Lat, Lon, Day> and I want to create an array with dimensions <Lat, Lon, Month>.
Here is a mock example of what I want. Currently, I can get the correct output using a loop, but in practice, my data is very large, so I was hoping for a more efficient solution than the second loop:
% Make the mock data
A = [1 2 3; 4 5 6];
X = zeros(2, 3, 9);
for j = 1:9
X(:, :, j) = A;
A = A + 1;
end
% Aggregate the X values in groups of 3 -- This is the part I would like help on
T = [1 1 1 2 2 2 3 3 3];
X_agg = zeros(2, 3, 3);
for i = 1:3
X_agg(:,:,i) = mean(X(:,:,T==i),3);
end
In 2 dimensions, I would use accumarray, but that does not accept higher dimension inputs.
Before getting to your answer let's first rewrite your code in a more general way:
ag = 3; % or agg_size
X_agg = zeros(size(X)./[1 1 ag]);
for i = 1:ag
X_agg(:,:,i) = mean(X(:,:,(i-1)*ag+1:i*ag), 3);
end
To avoid using the for loop one idea is to reshape your X matrix to something that you can use the mean function directly on.
splited_X = reshape(X(:), [size(X_agg), ag]);
So now splited_X(:,:,:,i) is the i-th part
that contains all the matrices that should be aggregated which is X(:,:,(i-1)*ag+1:i*ag)) (like above)
Now you just need to find the mean in the 3rd dimension of splited_X:
temp = mean(splited_X, 3);
However this results in a 4D matrix (where its 3rd dimension size is 1). You can again turn it into 3D matrix using reshape function:
X_agg = reshape(temp, size(X_agg))
I have not tried it to see how much more efficient it is, but it should do better for large matrices since it doesn't use for loops.
I have a non-fixed dimensional matrix M, from which I want to access a single element.
The element's indices are contained in a vector J.
So for example:
M = rand(6,4,8,2);
J = [5 2 7 1];
output = M(5,2,7,1)
This time M has 4 dimensions, but this is not known in advance. This is dependent on the setup of the algorithm I'm writing. It could likewise be that
M = rand(6,4);
J = [3 1];
output = M(3,1)
so I can't simply use
output=M(J(1),J(2))
I was thinking of using sub2ind, but this also needs its variables comma separated..
#gnovice
this works, but I intend to use this kind of element extraction from the matrix M quite a lot. So if I have to create a temporary variable cellJ every time I access M, wouldn't this tremendously slow down the computation??
I could also write a separate function
function x= getM(M,J)
x=M(J(1),J(2));
% M doesn't change in this function, so no mem copy needed = passed by reference
end
and adapt this for different configurations of the algorithm. This is of course a speed vs flexibility consideration which I hadn't included in my question..
BUT: this is only available for getting the element, for setting there is no other way than actually using the indices (and preferably the linear index). I still think sub2ind is an option. The final result I had intended was something like:
function idx = getLinearIdx(J, size_M)
idx = ...
end
RESULTS:
function lin_idx = Lidx_ml( J, M )%#eml
%LIDX_ML converts an array of indices J for a multidimensional array M to
%linear indices, directly useable on M
%
% INPUT
% J NxP matrix containing P sets of N indices
% M A example matrix, with same size as on which the indices in J
% will be applicable.
%
% OUTPUT
% lin_idx Px1 array of linear indices
%
% method 1
%lin_idx = zeros(size(J,2),1);
%for ii = 1:size(J,2)
% cellJ = num2cell(J(:,ii));
% lin_idx(ii) = sub2ind(size(M),cellJ{:});
%end
% method 2
sizeM = size(M);
J(2:end,:) = J(2:end,:)-1;
lin_idx = cumprod([1 sizeM(1:end-1)])*J;
end
method 2 is 20 (small number of index sets (=P) to convert) to 80 (large number of index sets (=P)) times faster than method 1. easy choice
For the general case where J can be any length (which I assume always matches the number of dimensions in M), there are a couple options you have:
You can place each entry of J in a cell of a cell array using the num2cell function, then create a comma-separated list from this cell array using the colon operator:
cellJ = num2cell(J);
output = M(cellJ{:});
You can sidestep the sub2ind function and compute the linear index yourself with a little bit of math:
sizeM = size(M);
index = cumprod([1 sizeM(1:end-1)]) * (J(:) - [0; ones(numel(J)-1, 1)]);
output = M(index);
Here is a version of gnovices option 2) which allows to process a whole matrix of subscripts, where each row contains one subscript. E.g for 3 subscripts:
J = [5 2 7 1
1 5 2 7
4 3 9 2];
sizeM = size(M);
idx = cumprod([1 sizeX(1:end-1)])*(J - [zeros(size(J,1),1) ones(size(J,1),size(J,2)-1)]).';