Strange behavior when modifying logical arrays [duplicate] - arrays

This question already has an answer here:
Assigning value to array by logical indexing doesn't work
(1 answer)
Closed 5 years ago.
I have an array that contains a bunch of logical values, it looks like:
test = [1 1 1 1 1 0 0 1 1 0 0 ...].
If I want to change a normal array of scalar values - lets say
a = [1 2 3 4]
I could do:
a(a == 1) = 5
and the result would be
[5 2 3 4]
As expected.
However if I do:
test(test == 0) = 5
I get back something unexpected:
[1 1 1 1 1 1 1 1 1 1 1 1 1 1....
All of the 0s have been changed to 1!
I suspect this is because the array is filled with logicals, and because of typechecking MATLAB coerces any value that is not 1 or 0 to the closest logical value - but I want to confirm. This is surely strange.

This is because your array is boolean, and 5 evaluates to true in boolean, which displays as 1. In English, your code test(test == 0) = 5 translates to "set all False values to True". The result is an all-true array, i.e. all ones.

Related

Given an input array, output the minimum number of swaps for sorting the array [duplicate]

This question already has answers here:
Minimum number of swaps needed to change Array 1 to Array 2?
(8 answers)
Closed 7 years ago.
Given an input array I want to calculate the minimum number of swaps to sort the array.
I thought that its equal to inversion count but its not as depicted below:
array [6,4,3]
output: 1, just swap 6 with 3. But the inversions are actually 3.
So given an input array is there an efficient algorithm to determine the minimum number of swaps. I know that selection sort has minimum number of swaps but I also know that it is not efficient.
You can use this method:
Say this is your input array: [4 6 1 8 2 7]
Then, in sorted array, positions of elements will be, respectively: 3 4 1 6 2 5
Create a visit array: [0 0 0 0 0 0]
Start with 1'st index in position array, mark it as visited, jump to the position it represents, until the value of visit in visit[] is set.
Then, once a cycle is complete, chose the next element whose visit value is unset.
For a cycle of k elements, add k-1 to the swap counter.
Step by step in this example will be:
visiting index in position[] visit[]
position[1] = 3 [1 0 0 0 0 0]
position[3] = 1 [1 0 1 0 0 0]
Now, a cycle is formed. Since this cycle has 2 elements, add 1 to swap counter. Now we start with index 2.
position[2] = 4 [1 1 1 0 0 0]
position[4] = 6 [1 1 1 1 0 0]
position[6] = 5 [1 1 1 1 0 1]
position[5] = 2 [1 1 1 1 1 1]
Since visit[2] is set, a cycle is formed. This cycle has 4 elements, so, add 3 to swap counter.
See which next index in visit is still unset. (None). Stop here.
So, your answer is 1 + 3 = 4

"Logical Indexing with a Smaller Array" in matlab [duplicate]

This question already has an answer here:
Linear indexing, logical indexing, and all that
(1 answer)
Closed 7 years ago.
The matlab help page for matrix indexing says:
Logical Indexing with a Smaller Array
In most cases, the logical indexing array should have the same number
of elements as the array being indexed into, but this is not a
requirement. The indexing array may have smaller (but not larger)
dimensions:
A = [1 2 3;4 5 6;7 8 9]
A =
1 2 3
4 5 6
7 8 9
B = logical([0 1 0; 1 0 1])
B =
0 1 0
1 0 1
isequal(numel(A), numel(B))
ans =
0
A(B)
ans =
4
7
8
What kind of crazy rule is matlab using here?
To understand this behavior, it's necessary to understand how matrices are stored in memory. Matlab stores matrices using column major layout. This means the 2d matrix:
A = 1 2 3
4 5 6
7 8 9
is stored in memory as one dimensional array going down the columns of A:
A = { array = [1 4 7 2 5 8 3 6 9]
n_rows = 3
n_cols = 3 }
The matrix B:
B = 0 1 0
1 0 1
is stored in memory as:
B = { array = [0 1 1 0 0 1]
n_rows = 2
n_cols = 3 }
Let's put the underlying representations next to eachother:
A.array = [1 4 7 2 5 8 3 6 9]
B.array = [0 1 1 0 0 1]
Using logical indexing, A(B) gives you [4, 7, 8] If you think a bit deeper, what's causing the unintuitive result is the combination of: (1) Matlab uses column major layout and (2) the number of columns in A and B are different.
Note: I'm using pseudo code here. A.array isn't valid code etc...
Bonus:
You can see what happens when the reshape command is called. The underlying data array doesn't change, just the n_rows and n_cols associated with the data array.

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

How can I calculate the length of groups of consecutive ones in binary vector? [duplicate]

This question already has answers here:
How to count number of 1 and 0 in the matrix?
(1 answer)
Finding islands of zeros in a sequence
(6 answers)
Closed 7 years ago.
I have a series of binary vectors (time x 1) in which 1s represent a connection between two variables at a given point in time. The connections between the two variables are sporadic, and I would like to know how 'long' each connection between the two variables exists for.
e.g. if the vector for a given set of variables is:
[0 0 1 1 1 0 0 0 1 0 0 1 1 1 1 1 1 1 ]
Then I would like to create a new variable which contains the length of contiguous 1s in each instance. From the above example, the new variable would look like this:
[3,1,7]
As the first time that a 1 arose, it was there for 3 consecutive time points, whereas the next time it was only there for 1 time point and finally, the connection was in the data for 7 consecutive time points.
If there is a good way to solve this, I'd love some help.
Cheers
Mac
diff and cumsum give a good pair!
a = [0 0 1 1 1 0 0 0 1 0 0 1 1 1 1 1 1 1 ]
b = cumsum([a 0])
c = diff( [0 b(diff([a 0]) == -1) ] )
%// or
c = diff( [0 b(~(diff([a 0]) + 1)) ] )
c =
3 1 7

Element-wise array replication according to a count [duplicate]

This question already has answers here:
Repeat copies of array elements: Run-length decoding in MATLAB
(5 answers)
Closed 8 years ago.
My question is similar to this one, but I would like to replicate each element according to a count specified in a second array of the same size.
An example of this, say I had an array v = [3 1 9 4], I want to use rep = [2 3 1 5] to replicate the first element 2 times, the second three times, and so on to get [3 3 1 1 1 9 4 4 4 4 4].
So far I'm using a simple loop to get the job done. This is what I started with:
vv = [];
for i=1:numel(v)
vv = [vv repmat(v(i),1,rep(i))];
end
I managed to improve by preallocating space:
vv = zeros(1,sum(rep));
c = cumsum([1 rep]);
for i=1:numel(v)
vv(c(i):c(i)+rep(i)-1) = repmat(v(i),1,rep(i));
end
However I still feel there has to be a more clever way to do this... Thanks
Here's one way I like to accomplish this:
>> index = zeros(1,sum(rep));
>> index(cumsum([1 rep(1:end-1)])) = 1;
index =
1 0 1 0 0 1 1 0 0 0 0
>> index = cumsum(index)
index =
1 1 2 2 2 3 4 4 4 4 4
>> vv = v(index)
vv =
3 3 1 1 1 9 4 4 4 4 4
This works by first creating an index vector of zeroes the same length as the final count of all the values. By performing a cumulative sum of the rep vector with the last element removed and a 1 placed at the start, I get a vector of indices into index showing where the groups of replicated values will begin. These points are marked with ones. When a cumulative sum is performed on index, I get a final index vector that I can use to index into v to create the vector of heterogeneously-replicated values.
To add to the list of possible solutions, consider this one:
vv = cellfun(#(a,b)repmat(a,1,b), num2cell(v), num2cell(rep), 'UniformOutput',0);
vv = [vv{:}];
This is much slower than the one by gnovice..
What you are trying to do is to run-length decode. A high level reliable/vectorized utility is the FEX submission rude():
% example inputs
counts = [2, 3, 1];
values = [24,3,30];
the result
rude(counts, values)
ans =
24 24 3 3 3 30
Note that this function performs the opposite operation as well, i.e. run-length encodes a vector or in other words returns values and the corresponding counts.
accumarray function can be used to make the code work if zeros exit in rep array
function vv = repeatElements(v, rep)
index = accumarray(cumsum(rep)'+1, 1);
vv = v(cumsum(index(1:end-1))+1);
end
This works similar to solution of gnovice, except that indices are accumulated instead being assigned to 1. This allows to skip some indices (3 and 6 in the example below) and remove corresponding elements from the output.
>> v = [3 1 42 9 4 42];
>> rep = [2 3 0 1 5 0];
>> index = accumarray(cumsum(rep)'+1, 1)'
index =
0 0 1 0 0 2 1 0 0 0 0 2
>> cumsum(index(1:end-1))+1
ans =
1 1 2 2 2 4 5 5 5 5 5
>> vv = v(cumsum(index(1:end-1))+1)
vv =
3 3 1 1 1 9 4 4 4 4 4

Resources