Index array columns using vectors in GNU Octave/MATLAB [duplicate] - arrays

This question already has an answer here:
Vector as column index in matrix
(1 answer)
Closed 7 years ago.
While coding in GNU Octave/MATLAB I came through this simple problem I couldn't figure out by myself: I'm trying to select some elements of a matrix by using some indexes stored in an array. Let me put it clear with an example:
Given:
A = zeros(5, 3)
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
I would like to select some elements in A matrix row-wise, by using the values in the auxiliary array B as subindices.
Ie. the following B array
B = [ 1 3 2 1 3 ]'
1
3
2
1
3
should be read as:
1 -> index '1' on first row (element [1, 1])
3 -> index '3' on second row (element [2, 3])
2 -> index '2' on third row (element [3, 2])
1 -> index '1' on fourth row (element [4, 1])
3 -> index '3' on fifth row (element [5, 3])
Therefore, if we assign value '1' to the elements selected using the aforementioned criteria, the resulting matrix would be:
1 0 0
0 0 1
0 1 0
1 0 0
0 0 1
I believe this is a simple operation and I'm convinced that there must be a way to achieve the described behaviour without having to loop across the rows in matrix A.
Thank you.
Edit: Rewrite question so that it is (hopefully) less confusing.

Your question is a bit confusing. You're saying you want to select the elements in A by using the values in the vector B as column indexes, but your example sets (not gets) new values in matrix A. I'm explaining both cases.
Consider this matrix
A = magic(5)
17 24 1 8 15
23 5 7 14 16
4 6 13 20 22
10 12 19 21 3
11 18 25 2 9
Say you want to get/set the diagonal elements of A.
Index pairs in that case are [1,1], [2,2], [3,3], [4,4] and [5,5].
To access elements as a vector, run this
A(sub2ind([5,5], (1:5)',(1:5)'))
17
5
13
21
9
To set elements run this
A(sub2ind([5,5], (1:5)',(1:5)')) = 0
0 24 1 8 15
23 0 7 14 16
4 6 0 20 22
10 12 19 0 3
11 18 25 2 0
These commands can be written as
r = 1:5
c = 1:5
A(sub2ind([max(r),max(c)], r',c'))
# to assign values
A(sub2ind([max(r),max(c)], r',c')) = 0
# and to assign different value to each index pair
A(sub2ind([max(r),max(c)], r',c')) = [20 10 50 12 99]
In your example,
r = 1:5
c = B'
A(sub2ind([max(r),max(c)], r',c')) = 1
# or simply A(sub2ind([max(r),max(B)], r',B)) = 1
1 0 0
0 0 1
0 1 0
1 0 0
0 0 1
You can read how sub2ind works here.

Related

How can I need multiply group of elements instead of one element in matrices multiplication

Suppose I have a matrix of dimension [4x4], and a vector of [16x1], I need to multiply every 4 element in the vector in one element in the matrix, (instead of multiplying element in row by element in vector), how can I do that using loop ?
For example here below, the results of the first four elements in the resulted vector as shown in the below example, then the same thing for the second, third and fourth rows in the matrix. :
So the results in that example is supposed to be [16x1]
Thank you
Using kron you can use this one-liner:
%A = [1 2 3 4; 5 6 7 8; 9 10 11 12; 13 14 15 16];
%v = [2 2 2 2 0 0 0 0 1 1 1 1 3 3 3 3].';
sum(kron(A,ones(4,4)).'.*v).'/4
I use the kronecker tensor product to "replicate" 4x4 time the A matrice. After that it's pure algebra.
This is just matrix multiplication in disguise... If your tall vector was a matrix of the same size as the matrix shown, where each highlighted block is a row, it's matrix multiplication. We can set this up, then reshape back into a vector.
You can use indexing to turn this into simple matrix multiplication. A question I answered already today details how the below indexing works using bsxfun, then we just reshape at the end:
% Setup
A = [1 2 3 4; 5 6 7 8; 9 10 11 12; 13 14 15 16];
v = [2 2 2 2 0 0 0 0 1 1 1 1 3 3 3 3].';
% Matrix mutliplication
r = numel(v)/size(A,1);
b = A * v( bsxfun( #plus, (1:r:numel(v)).', 0:r-1 ) );
% Make result a column vector
b = reshape( b.', [], 1 );
See if this is what you want:
A = [1 2 3 4; 5 6 7 8; 9 10 11 12; 13 14 15 16];
v = [2 2 2 2 0 0 0 0 1 1 1 1 3 3 3 3].';
r = reshape(sum(bsxfun(#times, permute(A, [3 2 1]), permute(reshape(v, 1, [], size(A,2)), [2 3 1])), 2), [], 1);
which gives
r =
17
17
17
17
41
41
41
41
65
65
65
65
89
89
89
89
There are details that I assumed, but this shoudl do the trick:
A=reshape(1:16,4,4).';
b=repelem([2,0,1,3],1,4).';
c=[];
for row=1:size(A,1)
c=[ c; sum(reshape(repelem(A(row,:),4).*b.',4,[]),2)];
end
I am assuming here that your demo for the vector is just a bad example and that you wont have repeated values, otherwise an easier version can be achieved by just not doing 3/4ths of the multiplications.
If you do not have access to repelem, have a look at alterative codes that do the same thing:Element-wise array replication in Matlab

Number 0's and 1's blocks in a binary vector

In MATLAB, there is the bwlabel function, that given a binary vector, for instance x=[1 1 0 0 0 1 1 0 0 1 1 1 0] gives (bwlabel(x)):
[1 1 0 0 0 2 2 0 0 3 3 3 0]
but what I want to obtain is
[1 1 2 2 2 3 3 4 4 5 5 5 6]
I know I can negate x to obtain (bwlabel(~x))
[0 0 1 1 1 0 0 2 2 0 0 0 3]
But how can I combine them?
All in one line:
y = cumsum([1,abs(diff(x))])
Namely, abs(diff(x)) spots changes in the binary vector, and you gain the output with the cumulative sum.
You can still do it using bwlabel by vertically concatenating x and ~x, using 4-connected components for the labeling, then taking the maximum down each column:
>> max(bwlabel([x; ~x], 4))
ans =
1 1 2 2 2 3 3 4 4 5 5 5 6
However, the solution from Bentoy13 is probably a bit faster.
x=[1 1 0 0 0 1 1 0 0 1 1 1 0];
A = bwlabel(x);
B = bwlabel(~x);
if x(1)==1
tmp = A>0;
A(tmp) = 2*A(tmp)-1;
tmp = B>0;
B(tmp) = 2*B(tmp);
C = A+B
elseif x(1)==0
tmp = A>0;
A(tmp) = 2*A(tmp);
tmp = B>1;
B(tmp) = 2*B(tmp)-1;
C = A+B
end
C =
1 1 2 2 2 3 3 4 4 5 5 5 6
You know the first index should remain 1, but the second index should go from 1 to 2, the third from 2 to 3 etc; thus even indices should be doubled and odd indices should double minus one. This is given by A+A-1 for odd entries, and B+B for even entries. So a simple check for whether A or B contains the even points is sufficient, and then simply add the two arrays.
I found this function that does exactly what i wanted:
https://github.com/davidstutz/matlab-multi-label-connected-components
So, clone the repository and compile in matlab using mex :
mex sp_fast_connected_relabel.cpp
Then,
labels = sp_fast_connected_relabel(x);

Keep n largest values per column and set the rest to zero in MATLAB without a loop

I have a matrix of unsorted numbers and I want to keep the n largest (not necessarily unique) values per column and set the rest to zero.
I figured out how to do it with a loop:
a = [4 8 12 5; 9 2 6 18; 11 3 9 7; 8 9 12 4]
k = 2
for n = 1:4
[y, ind] = sort(a(:,n), 'descend');
a(ind(k+1:end),n) = 0;
end
a
which gives me:
a =
4 8 12 5
9 2 6 18
11 3 9 7
8 9 12 4
k =
2
a =
0 8 12 0
9 0 0 18
11 0 0 7
0 9 12 0
However when I try to eliminate the loop, I can't seem to get the indexing right, because this:
a = [4 8 12 5; 9 2 6 18; 11 3 9 7; 8 9 12 4]
k = 2
[y, ind] = sort(a, 'descend');
b = ind(k+1:end,:)
a(b) = 0
which gives me this: (which is not what I wanted to do)
a =
4 8 12 5
9 2 6 18
11 3 9 7
8 9 12 4
k =
2
b =
4 3 3 1
1 2 2 4
a =
0 8 12 5
0 2 6 18
0 3 9 7
0 9 12 4
Am I indexing this wrong? Do I have to use the loop?
I referenced this question to get started but it wasn't exactly what I was trying to do: How to find n largest elements in an array and make the other elements zero in matlab?
You're very close. ind in the sort function gives you the row locations for each column where that particular value would appear in the sorted output. You need to do some additional work if you want to index into the matrix properly and eliminate the entries. You know that for each column of I, that tells you that we need to eliminate those entries from that particular column. Therefore, what I would do is generate column-major linear indices using each column of I to be the rows we need to eliminate.
Try doing this:
a = [4 8 12 5; 9 2 6 18; 11 3 9 7; 8 9 12 4];
k = 2;
[y, ind] = sort(a, 'descend');
%// Change here
b = sub2ind(size(a), ind(k+1:end,:), repmat(1:size(a,2), size(a,1)-k, 1));
a(b) = 0;
We use sub2ind to help us generate our column major indices where the rows are denoted by the values in ind after the kth element and the columns we need are for each column in this matrix. There are size(a,1)-k rows remaining after you truncate out the k values after sorting, and so we generate column values that go from 1 up to as many columns as we have in a and as many rows as there are remaining.
We get this output:
>> a
a =
0 8 12 0
9 0 0 18
11 0 0 7
0 9 12 0
Here's one using bsxfun -
%// Get descending sorting indices per column
[~, ind] = sort(a,1, 'descend')
%// Get linear indices that are to be set to zeros and set those in a to 0s
rem_ind = bsxfun(#plus,ind(n+1:end,:),[0:size(a,2)-1]*size(a,1))
a(rem_ind) = 0
Sample run -
a =
4 8 12 5
9 2 6 18
11 3 9 7
8 9 12 4
n =
2
ind =
3 4 1 2
2 1 4 3
4 3 3 1
1 2 2 4
rem_ind =
4 7 11 13
1 6 10 16
a =
0 8 12 0
9 0 0 18
11 0 0 7
0 9 12 0

How can I find all the cells that have the same values in a multi-dimensional array in octave / matlab

How can I find all the cells that have the same values in a multi-dimensional array?
I can get it partially to work with result=A(:,:,1)==A(:,:,2) but I'm not sure how to also include A(:,:,3)
I tried result=A(:,:,1)==A(:,:,2)==A(:,:,3) but the results come back as all 0 when there should be 1 correct answer
which is where the number 8 is located in the same cell on all the pages of the array. Note: this is just a test the repeating number could be found multiple times and as different numbers.
PS: I'm using octave 3.8.1 which is like matlab
See code below:
clear all, tic
%graphics_toolkit gnuplot %use this for now it's older but allows zoom
A(:,:,1)=[1 2 3; 4 5 6; 7 9 8]; A(:,:,2)=[9 1 7; 6 5 4; 7 2 8]; A(:,:,3)=[2 4 6; 8 9 1; 3 5 8]
[i j k]=size(A)
for ii=1:k
maxamp(ii)=max(max(A(:,:,ii)))
Ainv(:,:,ii)=abs(A(:,:,ii)-maxamp(ii));%the extra max will get the max value of all values in array
end
%result=A(:,:,1)==A(:,:,2)==A(:,:,3)
result=A(:,:,1)==A(:,:,2)
result=double(result); %turns logical index into double to do find
[row col page] = find(result) %gives me the col, row, page
This is the output it gives me:
>>>A =
ans(:,:,1) =
1 2 3
4 5 6
7 9 8
ans(:,:,2) =
9 1 7
6 5 4
7 2 8
ans(:,:,3) =
2 4 6
8 9 1
3 5 8
i = 3
j = 3
k = 3
maxamp = 9
maxamp =
9 9
maxamp =
9 9 9
result =
0 0 0
0 1 0
1 0 1
row =
3
2
3
col =
1
2
3
page =
1
1
1
Use bsxfun(MATLAB doc, Octave doc) and check to see if broadcasting the first slice is equal across all slices with a call to all(MATLAB doc, Octave doc):
B = bsxfun(#eq, A, A(:,:,1));
result = all(B, 3);
If we're playing code golf, a one liner could be:
result = all(bsxfun(#eq, A, A(:,:,1)), 3);
The beauty of the above approach is that you can have as many slices as you want in the third dimension, other than just three.
Example
%// Your data
A(:,:,1)=[1 2 3; 4 5 6; 7 9 8];
A(:,:,2)=[9 1 7; 6 5 4; 7 2 8];
A(:,:,3)=[2 4 6; 8 9 1; 3 5 8];
B = bsxfun(#eq, A, A(:,:,1));
result = all(B, 3);
... gives us:
>> result
result =
0 0 0
0 0 0
0 0 1
The above makes sense since the third row and third column for all slices is the only value where every slice shares this same value (i.e. 8).
Here's another approach: compute differences along third dimension and detect when all those differences are zero:
result = ~any(diff(A,[],3),3);
You can do
result = A(:,:,1) == A(:,:,2) & A(:,:,1) == A(:,:,3);
sum the elements along the third dimension and divide it with the number of dimensions. We get back the original value if the values are the same in all dimension. Otherwise a different (e.g. a decimal) value. Then find the location where A and the summation are equal over the third dimension.
all( A == sum(A,3)./size(A,3),3)
ans =
0 0 0
0 0 0
0 0 1
or
You could also do
all(A==repmat(sum(A,3)./size(A,3),[1 1 size(A,3)]),3)
where repmat(sum(A,3)./size(A,3),[1 1 size(A,3)]) would highlight the implicit broadcasting of this when compared with A.
or
you skip the broadcasting altogether and just compare it with the first slice of A
A(:,:,1) == sum(A,3)./size(A,3)
Explanation
3 represents the third dimension .
sum(A,3) means that we are taking the sum over the third dimension.
Then we divide that sum by the number of dimensions.
It's basically the average value for that position in the third dimension.
If you add three values and then divide it by three then you get the original value back.
For example, A(3,3,:) is [8 8 8]. (8+8+8)/3 = 8.
If you take another example, i.e. the value above, A(2,3,:) = [6 4 1].
Then (6+4+1)/3=3.667. This is not equal to A(2,3,:).
sum(A,3)./size(A,3)
ans =
4.0000 2.3333 5.3333
6.0000 6.3333 3.6667
5.6667 5.3333 8.0000
Therefore, we know that the elements are not the same
throughout the third dimension. This is just a trick I use
to determine that. You also have to remember that
sum(A,3)./size(A,3) is originally a 3x3x1 matrix
that will be automatically expanded (i.e. broadcasted) to a
3x3x3 matrix when we do the comparison with A (A == sum(A,3)./size(A,3)).
The result of that comparison will be a logical array with 1 for the positions that are the same throughout the third dimension.
A == sum(A,3)./size(A,3)
ans =
ans(:,:,1) =
0 0 0
0 0 0
0 0 1
ans(:,:,2) =
0 0 0
1 0 0
0 0 1
ans(:,:,3) =
0 0 0
0 0 0
0 0 1
Then use all(....,3) to get those. The result is a 3x3x1
matrix where a 1 indicates that the value is the same in the
third dimension.
all( A == sum(A,3)./size(A,3),3)
ans =
0 0 0
0 0 0
0 0 1

Algorithm: Find smallest contiguous array containing K 0's in array of 1's and 0's

I have array of 1's and 0's only. Now I want to find smallest contiguous subset/subarray which contains at least K 0's.
Example
Array is 1 1 0 1 1 0 1 1 0 0 0 0 1 0 1 1 0 0 0 1 1 0 0 1 0 0 0
and K(6) should be 0 0 1 0 1 1 0 0 0 or 0 0 0 0 1 0 1 1 0....
My Solution
Array: 1 1 0 1 1 0 1 1 0 0 0 0 1 0 1 1 0 0 0 1 1 0 0
Index: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
Sum: 1 2 2 3 4 4 5 6 6 6 6 6 7 7 8 9 9 9 9 10 11 11 11
Diff(I-S): 0 0 1 1 1 2 2 2 3 4 5 6 6 7 7 7 8 9 10 10 10 11 12
For K(6)
Start with 9-15 = Store difference in diff.
Next increase difference
8-15(Difference in index)
8-14(Compare Difference in index)
So on keep moving to find element with least elements...
I am looking for better algorithm for this solution.
I believe you could do it with a rolling window like:
In the given array, find the first occurance of 0 (say at index i).
Keep on scanning until you've k 0's included in your window (say, the window ends at index j) Record the window Length(say j-i+1=L).
Now, discard the left-most 0 at index i, and keep scanning till you get next 0 (say at index i'
Extend the right-end of the window situated at j to j' to make the count of 0's = k again.
If the new window-length L'=j'-i'+1 is smaller update it.
Keep on repeating the above procedure till j hits the end of array.
No extra space needed and It's O(N) time-complexity, as an element would be scanned at max twice.
With extra O(k) memory , you can do it in O(n) time.Here is the java code.What you are doing is , if a[i]==0 then you check where the queue's first element points to.and if the differnce in positions is less than minimum, then you update the answer.
Queue<Integer> queue =new LinkedList<Integer>();
int i=0;
while(queue.size()<k&&i<n)
{
if(a[i]==0)
{
queue.add(i);
}
i++;
}
if(i==n&&queue.size()<k)
System.out.println("Insufficient 0''s");
int ans=i-1-queue.peek();
for(int j=i;j<n;j++)
{
if(a[i]==0)
{
queue.poll();
queue.add(i);
ans=Math.min(ans,i-queue.peek());
}
}
System.out.println(ans);
EDIT :Explanation
We maintain a queue which consist of all the positions which have a 0 and we limit the queue size to be k. So initially in the while loop we fill the queue with the first k indexes. If ofcourse the queue size is less than k after seeing all elements , then it's impossible. After that , we keep going to all the left over elements .Each time we see a 0 , we calcualte the length of the subsequence ,(i-queue.peek()) and find the minimum .Also we remove the first element , and add the latest index again maintaining the queue size
fully working python code:
>>> A = "1 1 0 1 1 0 1 1 0 0 0 0 1 0 1 1 0 0 0 1 1 0 0".split()
>>> A = map(int, A)
>>> zero_positions = [i for i, x in enumerate(A) if x == 0]
>>> k = 3
>>> containing_k_zeros_intervals = zip(zero_positions, zero_positions[k:])
>>> min(b - a for a, b in containing_k_zeros_intervals)
3
Scan the array from the starting to find the index till which we get k zeros.
Have two pointers.
Now ptr1 is at the index where first zero is seen.
start = ptr1
ptr2 is at the index where we have found k 0's.
end = ptr2;
a)increment ptr1.
b ) find the index from ptr2+1 until we find k 0's.
c) Say at ptr3 we find K 0's. If ptr3-ptr1 < (end-start) update indexes start and end.
Repeat steps a -c until the end of the list.
At the end, start and end will have indexes where there are k 0's.

Resources