Stop Matlab from treating a 1xn matrix as a column vector - arrays

I'm very frustrated with MATLAB right now. Let me illustrate the problem. I'm going to use informal notation here.
I have a column cell vector of strings called B. For now, let's say B = {'A';'B';'C';'D'}.
I want to have a matrix G, which is m-by-n, and I want to replace the numbers in G with the respective elements of B... For example, let's say G is [4 3; 2 1]
Let's say I have a variable n which says how many rows of G I want to take out.
When I do B(G(1:2,:)), I get what I want ['D' 'C'; 'B' 'A']
However, if I do B(G(1:1,:)) I get ['D';'C'] when what I really want to get is ['D' 'C']
I am using 1:n, and I want it to have the same behavior for n = 1 as it does for n = 2 and n = 3. Basically, G actually is a n-by-1500 matrix, and I want to take the top n rows and use it as indexes into B.
I could use an if statement that transposes the result if n = 1 but that seems so unnecessary. Is there really no way to make it so that it stops treating my 1-by-n matrix as if it was a column vector?

According to this post by Loren Shure:
Indexing with one array C = A(B) produces output the size of B unless both A and B are vectors.
When both A and B are vectors, the number of elements in C is the number of elements in B and with orientation of A.
You are in second case, hence the behaviour you see.

To make it work, you need to maintain the output to have as many columns as in G. To achieve the same, you can do something like this -
out = reshape(B(G(1:n,:)),[],size(G,2))
Thus, with n = 1:
out =
'D' 'C'
With n = 2:
out =
'D' 'C'
'B' 'A'

I think this will only happen in 1-d case. In default, matlab will return column vector since it is the way how it stores matrix. If you want a row vector, you could just use transpose. Well in my opinion it should be fine when n > 1.

Related

Array of sets in Matlab

Is there a way to create an array of sets in Matlab.
Eg: I have:
a = ones(10,1);
b = zeros(10,1);
I need c such that c = [(1,0); (1,0); ...], i.e. each set in c has first element from a and 2nd element from b with the corresponding index.
Also is there some way I can check if an unknown set (x,y) is in c.
Can you all please help me out? I am a Matlab noob. Thanks!
There are not sets in your understanding in MATLAB (I assume that you are thinking of tuples on Python...) But there are cells in MATLAB. That is a data type that can store pretty much anything (you may think of pointers if you are familiar with the concept). It is indicated by using { }.
Knowing this, you could come up with a cell of arrays and check them using cellfun
% create a cell of numeric arrays
C = {[1,0],[0,2],[99,-1]}
% check which input is equal to the array [1,0]
lg = cellfun(#(x)isequal(x,[1,0]),C)
Note that you access the address of a cell with () and the content of a cell with {}. [] always indicate arrays of something. We come to this in a moment.
OK, this was the part that you asked for; now there is a bonus:
That you use the term set makes me feel that they always have the same size. So why not create an array of arrays (or better an array of vectors, which is a matrix) and check this matrix column-wise?
% array of vectors (there is a way with less brackets but this way is clearer):
M = [[1;0],[0;2],[99;-1]]
% check at which column (direction ",1") all rows are equal to the proposed vector:
lg = all(M == [0;2],1)
This way is a bit clearer, better in terms of memory and faster.
Note that both variables lg are arrays of logicals. You can use them directly to index the original variable, i.e. M(:,lg) and C{lg} returns the set that you are looking for.
If you would like to get logical value regarding if p is in C, maybe you can try the code below
any(sum((C-p).^2,2)==0)
or
any(all(C-p==0,2))
Example
C = [1,2;
3,-1;
1,1;
-2,5];
p1 = [1,2];
p2 = [1,-2];
>> any(sum((C-p1).^2,2)==0) # indicating p1 is in C
ans = 1
>> any(sum((C-p2).^2,2)==0) # indicating p2 is not in C
ans = 0
>> any(all(C-p1==0,2))
ans = 1
>> any(all(C-p2==0,2))
ans = 0

Sub2ind with three dimension - access all of dimension

Say A is a 3x4x5 array. I am given a vector a, say of dimension 2 and b of dimension 2. If I do A(a,b,:) it will give 5 matrices of dimensions 2x2. I instead want the piecewise vectors (without writing a for loop).
So, I want the two vectors of A which are given by (a's first element and b's first element) and (a's second element and b's second element)
How do I do this without a for loop? If A were two dimensions I could do this using sub2ind. I don't know how to access the entire vectors.
You can use sub2ind to find the linear index to the first element of each output vector: ind = sub2ind(size(A),a,b). To get the whole vectors, you can't do A(ind,:), because the : has to be the 3rd dimension. However, what you can do is reshape A to be 2D, collapsing the first two dimensions into one. We have a linear index to the vectors we want, that will correctly index the first dimension of this reshaped A:
% input:
A = rand(3,4,5);
a = [2,3];
b = [1,2];
% expected:
B = [squeeze(A(a(1),b(1),:)).';squeeze(A(a(2),b(2),:)).']
% solution:
ind = sub2ind(size(A),a,b);
C = reshape(A,[],size(A,3));
C = C(ind,:)
assert(isequal(B,C))
You can change a and b to be 3d arrays just like A and then the sub2ind should be able to index the whole matrix. Like this:
Edit: Someone pointed out a bug. I have changed it so that a correction gets added. The problem was that ind1, which should have had the index number for each desired element of A was only indexing the first "plane" of A. The fix is that for each additional "plane" in the z direction, the total number of elements in A in the previous "planes" must be added to the index.
A=rand(3,4,5);
a=[2,3];
b=[1,2];
a=repmat(a,1,1,size(A,3));
b=repmat(b,1,1,size(A,3));
ind1=sub2ind(size(A),a,b);
correction=(size(A,1)*size(A,2))*(0:size(A,3)-1);
correction=permute(correction,[3 1 2]);
ind1=ind1+repmat(correction,1,2,1);
out=A(ind1)

Comparing two arrays of pixel values, and store any matches

I want to compare the pixel values of two images, which I have stored in arrays.
Suppose the arrays are A and B. I want to compare the elements one by one, and if A[l] == B[k], then I want to store the match as a key value-pair in a third array, C, like so: C[l] = k.
Since the arrays are naturally quite large, the solution needs to finish within a reasonable amount of time (minutes) on a Core 2 Duo system.
This seems to work in under a second for 1024*720 matrices:
A = randi(255,737280,1);
B = randi(255,737280,1);
C = zeros(size(A));
[b_vals, b_inds] = unique(B,'first');
for l = 1:numel(b_vals)
C(A == b_vals(l)) = b_inds(l);
end
First we find the unique values of B and the indices of the first occurrences of these values.
[b_vals, b_inds] = unique(B,'first');
We know that there can be no more than 256 unique values in a uint8 array, so we've reduced our loop from 1024*720 iterations to just 256 iterations.
We also know that for each occurrence of a particular value, say 209, in A, those locations in C will all have the same value: the location of the first occurrence of 209 in B, so we can set all of them at once. First we get locations of all of the occurrences of b_vals(l) in A:
A == b_vals(l)
then use that mask as a logical index into C.
C(A == b_vals(l))
All of these values will be equal to the corresponding index in B:
C(A == b_vals(l)) = b_inds(l);
Here is the updated code to consider all of the indices of a value in B (or at least as many as are necessary). If there are more occurrences of a value in A than in B, the indices wrap.
A = randi(255,737280,1);
B = randi(255,737280,1);
C = zeros(size(A));
b_vals = unique(B);
for l = 1:numel(b_vals)
b_inds = find(B==b_vals(l)); %// find the indices of each unique value in B
a_inds = find(A==b_vals(l)); %// find the indices of each unique value in A
%// in case the length of a_inds is greater than the length of b_inds
%// duplicate b_inds until it is larger (or equal)
b_inds = repmat(b_inds,[ceil(numel(a_inds)/numel(b_inds)),1]);
%// truncate b_inds to be the same length as a_inds (if necessary) and
%// put b_inds into the proper places in C
C(a_inds) = b_inds(1:numel(a_inds));
end
I haven't fully tested this code, but from my small samples it seems to work properly and on the full-size case, it only takes about twice as long as the previous code, or less than 2 seconds on my machine.
So, if I understand your question correctly, you want for each value of l=1:length(A) the (first) index k into B so that A(l) == B(k). Then:
C = arrayfun(#(val) find(B==val, 1, 'first'), A)
could give you your solution, as long as you're sure that every element will have a match. The above solution would fail otherwise, complaning that the function returned a non-scalar (because find would return [] if no match is found). You have two options:
Using a cell array to store the result instead of a numeric array. You would need to call arrayfun with 'UniformOutput', false at the end. Then, the values of A without matches in B would be those for which isempty(C{i}) is true.
Providing a default value for an index into A with no matches in B (e.g. 0 or NaN). I'm not sure about this one, but I think that you would need to add 'ErrorHandler', #(~,~) NaN to the arrayfun call. The error handler is a function that gets called when the function passed to arrayfun fails, and may either rethrow the error or compute a substitute value. Thus the #(~,~) NaN. I am not sure that it would work, however, since in this case the error is in arrayfun and not in the passed function, but you can try it.
If you have the images in arrays A & B
idx = A == B;
C = zeros(size(A));
C(idx) = A(idx);

Is it always possible to order a multi-dimensional array in all dimensions? How?

Suppose, I have an n-dimensional array of integers (for n=1 it's a vector, for n=2 it's a rectangular matrix, for n=3 it's a parallelepiped, etc). I need to reorder elements of the array so that elements in each row, column, etc are in a non-decreasing order.
Is it possible for any input array?
Is the required ordering unique for any input array? I just realized that the answer for this question in general is no, e.g. for square matrices.
Is the required ordering unique for any input array that has different lengths in all dimensions?
What is the fastest algorithm to produce the required ordering?
Is it possible for any input array?
Yes, if we will look on the array as a single dimension array, with the same number of elements, and then sort it, by traversing it back to the original n-dimensions array, it remains sorted, since for each i1,....,i_k,...,i_m: for all i_k < i_k':
i_1 + n1*i_2 + n2^2*i_3 + .... (n_k-1)^(k-1)(i_k) + ... < i_1 + n1*i_2 + n2^2*i_3 + .... (n_k-1)^(k-1)(i_k') + ...
Thus (the array is ordered):
arr[i_1 + n1*i_2 + n2^2*i_3 + .... (n_k-1)^(k-1)(i_k) + ...] < arr[ i_1 + n1*i_2 + n2^2*i_3 + .... (n_k-1)^(k-1)(i_k') + ...]
Thus (back to original array):
arr[i_1][i_2]...[i_k]... < arr[i_1][i_2]...[i_k']...
As for the 2nd question:
Is the required ordering unique for any input array that has different
lengths in all dimensions?
No:
1 1 1 3
3 4 1 4
5 6 5 6
What is the fastest algorithm to produce the required ordering?
One solution is suggested already: regard it is a big long array and sort it.
Complexity is O(n_1*n_2*...*n_m*log(n_1*n_2*...*n_m))
My gut says if you could do it faster, you could sory faster then O(nlogn), but I have no proof for this claim, so it might be wrong.
Let me elaborate more about Alptigin Jalayr's idea.
Suppose we have rows sorted, so for the following data, we have a <= b and c <= d.
. .
..., a, ..., b, ...
. .
..., c, ..., d, ...
. .
When a is greater than c, i.e. c <a, then swap of them gives us c < b since a <= b, and a <=d since b <= d (if b > d, we swap b and d as well). In a word, sorting rows first and then columns next can give you the desired matrix.

How can I combine these two arrays into a matrix?

In MATLAB, if I define 2 matrices like:
A = [1:10];
B = [1:11];
How do I make matrix C with column 1 equal to A and column 2 equal to B? I cannot find any answers online. Sorry if I used the wrong MATLAB terminology for this scenario.
Well, to accomplish this you first need to make sure that A and B are the same length. In your example, A has 10 elements and B has 11, so that won't work.
However, assuming A and B have the same number of elements, this will do the trick:
C = [A(:) B(:)];
This first reshapes A and B into column vectors using single-colon indexing, then concatenates them horizontally.
if A,B same length, then can just type
C=[A' B']

Resources