Matlab Unexpected Result/Error? Replacing zeros in array with Inf - matlab

I'm getting an unexpected result from Matlab and I have no idea why. My goal is to replace values less than or equal to zero with Inf.
Here is the correct result of what I expect should happen:
C = [0 0 0 0 0 1 1 1 1 1 1];
C(C<=0)=Inf
C = Inf Inf Inf Inf Inf 1 1 1 1 1 1
But when I begin the process in a different manner, matlab replaces values <=0 with 1 instead of Inf.
A = [0 2 4 6 8 10 12 14 16 18 20];
b = 7;
E=A-b>0
E = 0 0 0 0 1 1 1 1 1 1 1
E(E<=0)=Inf
E = 1 1 1 1 1 1 1 1 1 1 1
Any idea why this is occurring? I'm guessing it has something to do with logical E=A-b>0 step, but I don't know why it is different.
My overall goal is to find the index of the closest larger value of a vector to a scalar, and it did just occur to me that I could skip the step that I think is causing the problem and get the desired outcome, like so (with A & b already defined as above):
F=A-b
F = -7 -5 -3 -1 1 3 5 7 9 11 13
F(F<=0)=Inf
F = Inf Inf Inf Inf 1 3 5 7 9 11 13
[~,ind]=min(F)
ind = 5
BUT, I still don't understand why Matlab was not giving the result I expected above (and I already finished typing the question before I realized the simple solution). So does anyone know why Matlab gives the unexpected result?
Thanks

This is because E is a logical array. Possible values are 1 or 0. If you use E=double(A-b>0) you will get the expected results.

Related

Convolution of two symbolic arrays on Matlab

I have two arrays:
p1=[sym(1) sym(2)]
p2=[sym(3) sym(4)]
I want to do the convolution of those two lists using conv function.
Matlab outputs the following:
Error using conv2
Invalid data type. First and second arguments must be numeric or logical.
Error in conv (line 43)
c = conv2(a(:),b(:),shape);
Can anyone help me how to deal with that?
Edit 1: i have not symbolic math toolbox so i demonstrated a matrix-wise operation on numeric values to calculate conv, i think this should do the trick on symbolic values either.
the conv function just accept numeric values.
there is a way to calculate conv matrix-wise
i demonstrate it with an example:
assume u and v are as follows :
u =
1 2 1 3
v =
2 7 1
>> conv(u,v)
ans =
2 11 17 15 22 3
instead we could first calculate u'*v, then do some rearranging and summing to calculate conv:
so first :
>> c=u'*v
c=
2 7 1
4 14 2
2 7 1
6 21 3
then we do some rearranging:
>> d=[c;zeros(3,3)]
d =
2 7 1
4 14 2
2 7 1
6 21 3
0 0 0
0 0 0
0 0 0
>>e= reshape(d(1:end-3),[6,3])
e=
2 0 0
4 7 0
2 14 1
6 7 2
0 21 1
0 0 3
and finally adding values together :
>> sum(e,2)
ans =
2
11
17
15
22
3
you can write your own code to use "v size" to do it(add (numel(v)*numel(v)) zeros to end of u'*v and so on.)

Transform a matrix to a stacked vector where all zeroes after the last non-zero value per row are removed

I have a matrix with some zero values I want to erase.
a=[ 1 2 3 0 0; 1 0 1 3 2; 0 1 2 5 0]
>>a =
1 2 3 0 0
1 0 1 3 2
0 1 2 5 0
However, I want to erase only the ones after the last non-zero value of each line.
This means that I want to retain 1 2 3 from the first line, 1 0 1 3 2 from the second and 0 1 2 5 from the third.
I want to then store the remaining values in a vector. In the case of the example this would result in the vector
b=[1 2 3 1 0 1 3 2 0 1 2 5]
The only way I figured out involves a for loop that I would like to avoid:
b=[];
for ii=1:size(a,1)
l=max(find(a(ii,:)));
b=[b a(ii,1:l)];
end
Is there a way to vectorize this code?
There are many possible ways to do this, here is my approach:
arotate = a' %//rotate the matrix a by 90 degrees
b=flipud(arotate) %//flips the matrix up and down
c= flipud(cumsum(b,1)) %//cumulative sum the matrix rows -and then flip it back.
arotate(c==0)=[]
arotate =
1 2 3 1 0 1 3 2 0 1 2 5
=========================EDIT=====================
just realized cumsum can have direction parameter so this should do:
arotate = a'
b = cumsum(arotate,1,'reverse')
arotate(b==0)=[]
This direction parameter was not available on my 2010b version, but should be there for you if you are using 2013a or above.
Here's an approach using bsxfun's masking capability -
M = size(a,2); %// Save size parameter
at = a.'; %// Transpose input array, to be used for masked extraction
%// Index IDs of last non-zero for each row when looking from right side
[~,idx] = max(fliplr(a~=0),[],2);
%// Create a mask of elements that are to be picked up in a
%// transposed version of the input array using BSXFUN's broadcasting
out = at(bsxfun(#le,(1:M)',M+1-idx'))
Sample run (to showcase mask usage) -
>> a
a =
1 2 3 0 0
1 0 1 3 2
0 1 2 5 0
>> M = size(a,2);
>> at = a.';
>> [~,idx] = max(fliplr(a~=0),[],2);
>> bsxfun(#le,(1:M)',M+1-idx') %// mask to be used on transposed version
ans =
1 1 1
1 1 1
1 1 1
0 1 1
0 1 0
>> at(bsxfun(#le,(1:M)',M+1-idx')).'
ans =
1 2 3 1 0 1 3 2 0 1 2 5

Creating a variable with unequal rows

I want to create a variable that finds a pattern (let's say [1 1]) in different rows of a matrix (A). Of course there aren't an equal number of occurrences of this string in each row.
A = [ 0 0 0 1 1
1 1 1 0 0
0 1 0 1 1
1 1 1 0 0
0 1 0 0 1
1 0 1 1 1
0 1 0 1 0
1 1 1 0 1];
I could do:
for i = 1:n
var(i,:) = strfind(A(i,:),[1 1]);
end
but then both sides of the equation won't be equal.
ERROR: ??? Subscripted assignment dimension mismatch.
I try to preallocate. I create a matrix with what I think would be the maximum number of occurrences of this string in each row of matrix A (let's say 50).
for i = 1:n
var(i, :) = NaN(1,50)
end
That's followed by the previous bit of code and it's no good either.
I've also tried:
for i = 1:n
var(i,1:numel(strfind(A(i,:),[1 1])) = strfind(A(i,:),[1 1])
end
Error: The expression to the left of the equals sign is not a valid
target for an assignment.
How should I go about doing this?
The output I expect is a matrix var(i,:) that gives me the position in the matrix where each of these patterns occur. It works fine for just one row.
For example:
var(1,:) = [1 2 5 8 10 22 48]
var(2,:) = [2 3 4 7 34 45 NaN]
var(3,:) = [4 5 21 32 33 NaN]
Thanks!
In your first try: you tried to build a matrix with different length of rows.
In your second try: you pre-allocated, but then run it over by re-definning var(i,:), while you tried to put there your desired result.
In your third try: unfortunately you just missed one brackets- ) at the end of left expression.
This code suppose to work (what you did at 2nd and 3rd attempts, with pre-allocate and fixed brackets):
var=NaN(1,50);
for i = 1:n
var(i,1:numel(strfind(A(i,:),[1 1]))) = strfind(A(i,:),[1 1])
end

Split an array in MATLAB

I have an array of integer numbers, and I want to split this array where 0 comes and a function that give me points of split.
Example: Array : 0 0 0 1 2 4 5 6 6 0 0 0 0 0 22 4 5 6 6 0 0 0 4 4 0
The function must return these numbers:
[ 3 10 ;14 20 ;22 25 ]
These numbers are index of start and end of nonzero numbers.
Here's a simple vectorized solution using the functions DIFF and FIND:
>> array = [0 0 0 1 2 4 5 6 6 0 0 0 0 0 22 4 5 6 6 0 0 0 4 4 0]; %# Sample array
>> edgeArray = diff([0; (array(:) ~= 0); 0]);
>> indices = [find(edgeArray > 0)-1 find(edgeArray < 0)]
indices =
3 10
14 20
22 25
The above code works by first creating a column array with ones indicating non-zero elements, padding this array with zeroes (in case any of the non-zero spans extend to the array edges), and taking the element-wise differences. This gives a vector edgeArray with 1 indicating the start of a non-zero span and -1 indicating the end of a non-zero span. Then the function FIND is used to get the indices of the starts and ends.
One side note/nitpick: these aren't the indices of the starts and ends of the non-zero spans like you say. They are technically the indices just before the starts and just after the ends of the non-zero spans. You may actually want the following instead:
>> indices = [find(edgeArray > 0) find(edgeArray < 0)-1]
indices =
4 9
15 19
23 24
Try this
a = [0 0 0 1 2 4 5 6 6 0 0 0 0 0 22 4 5 6 6 0 0 0 4 4 0];
%#Places where value was zero and then became non-zero
logicalOn = a(1:end-1)==0 & a(2:end)~=0;
%#Places where value was non-zero and then became zero
logicalOff = a(1:end-1)~=0 & a(2:end)==0;
%#Build a matrix to store the results
M = zeros(sum(logicalOn),2);
%#Indices where value was zero and then became non-zero
[~,indOn] = find(logicalOn);
%#Indices where value was non-zero and then became zero
[~,indOff] = find(logicalOff);
%#We're looking for the zero AFTER the transition happened
indOff = indOff + 1;
%#Fill the matrix with results
M(:,1) = indOn(:);
M(:,2) = indOff(:);
%#Display result
disp(M);
On the theme, but with a slight variation:
>>> a= [0 0 0 1 2 4 5 6 6 0 0 0 0 0 22 4 5 6 6 0 0 0 4 4 0];
>>> adjust= [0 1]';
>>> tmp= reshape(find([0 diff(a== 0)])', 2, [])
tmp =
4 15 23
10 20 25
>>> indices= (tmp- repmat(adjust, 1, size(tmp, 2)))'
indices =
4 9
15 19
23 24
As gnovice already pointed out on the positional semantics related to indices, I'll just add that, with this solution, various schemes can be handled very straightforward manner, when calculating indices. Thus, for your request:
>>> adjust= [1 0]';
>>> tmp= reshape(find([0 diff(a== 0)])', 2, []);
>>> indices= (tmp- repmat(adjust, 1, size(tmp, 2)))'
indices =
3 10
14 20
22 25

Am I using histc wrong, or is this MATLAB's fault?

Ok, here's some code in MATLAB:
data = [1 1.5 2 3 4 4.5 5 6 7 7 7 0 0 0];
histc(data, [1:1:5])
histc(data, [1:1:5, inf])
histc(data, [-inf, 1:1:5])
which outputs the following:
ans = 2 1 1 2 1
ans = 2 1 1 2 5 0
ans = 3 2 1 1 2 1
My question is, why does MATLAB return a useless 0 when you use inf in the bin size (to mean >= 5 in this case)?
Won't it always be zero? The help says the output will always be the same length as the bin size, but isn't that a bad spec in this case?
That's actually the correct behavior of HISTC. When you use the syntax:
n = histc(x,edges);
then, from the documentation:
n(k) counts the value x(i) if edges(k)
<= x(i) < edges(k+1). The last bin
counts any values of x that match
edges(end).
Therefore, the last edge value you give returns the count of how many things exactly match it. When inf is the last edge value, that counts 0 (i.e. there are no infs in the data). When 5 is the last edge value, it exactly matches 1 value in the data.