I have a vector a = 1111000011100001110000100100 and I have to compute two values based on it: p00 and p11.
p00 is the number of times 00 is occurring in the vector, divided by the total number of zeros. For example, in the above code then number of times 00 is occurring is 8/16 (total number of zeros).
Similarly, p11 is the number of occurrences of 11 divided by the total number of ones.
How can this be implemented in Matlab?
The safest and most generic way to do it is using regular expressions, because of the way they match runs.
a = [1 1 1 1 0 0 0 0 1 1 1 0 0 0 0 1 1 1 0 0 0 0 1 0 0 1 0 0]
s = char(a + '0');
p00 = numel(regexp(s, '00')) / sum(a == 0)
p11 = numel(regexp(s, '11')) / sum(a == 1)
NOTE:
I was tempted to do something along the following lines:
a = [1 1 1 1 0 0 0 0 1 1 1 0 0 0 0 1 1 1 0 0 0 0 1 0 0 1 0 0]
n = numel(a);
p00 = sum(a(1:n-1) == 0 & a(2:n) == 0) / sum(a == 0)
p11 = sum(a(1:n-1) == 1 & a(2:n) == 1) / sum(a == 1)
But this won't give the correct result, because it counts the sequence 0 0 0 0 as 3, rather than 2.
I would add the vector to itself shifted with one element to the right. The number of two-s will be the number of 11-s. The number of 0-s will be the number of 00-s. I think this is a natural solution in MATLAB.
Alternatively you could implement finite state machines to parse your vector.
a2 = a(2:end)+a(1:end-1);
p11 = length(find(a2 == 2))/length(find(a));
p00 = length(find(a2 == 0))/length(find(a==0));
The proposed solution was wrong!!!
Here is one that should work, but is not very efficient (but faster than the regexp solution):
d0=0; i=1;
while i<length(a)
if (a(i) == 0 & a(i)==a(i+1)) d0 = d0+1; i = i+1; end;
i=i+1;
end
p00 = d0/sum(a == 0)
Related
For example I have a logial vector in MATLAB:
idx1 = [0 0 0 1 1 1 0 0 1 1 1 0 0 0 0 1 1 1 1 1 1 0 0 0 0 0 0 0 1 1 1 0 0]
And I want to get numbers (count each block) of such blocks: 1 1 1, i.e. such block contain N elements == 1 (N "1"). idx1 - array, and his dimension can be any, for example 3820000.
How count many blocks (sequences of ones) occur in the entire array idx1?
counts_idx = 0;
init_counts_idx = 0;
arr = 0;
for i = 1:length(idx1) -1
for kk = 1 : length(idx1) - 1
if idx1(kk + 1) == 1
init_counts_idx = init_counts_idx + 1;
arr = init_counts_idx;
else
init_counts_idx = counts_idx;
end
C = {i,arr};
end
end
I try to using cells...
You can calculate the start and end indices of each block by diff([0 idx1 0]). Then, use this information to calculate block lengths Ns. Finally express the result as a cell array using the function C = mat2cell(A,rowDist).
idx1 = [0 0 0 1 1 1 0 0 1 1 1 0 0 0 0 1 1 1 1 1 1 0 0 0 0 0 0 0 1 1 1 0 0];
diffs = diff([0 idx1 0]);
% find start index of the blocks
loc = find(diffs == 1);
% calc block lengths by subtracting end - start indices
Ns = find(diffs == -1) - loc;
C = mat2cell([loc' Ns'],ones(size(loc)))
4×1 cell array
{[ 4 3]}
{[ 9 3]}
{[16 6]}
{[29 3]}
If you are interested only in the number of such blocks, length(loc) will give you the answer, it is similar to bwconncomp(idx1).NumObjects.
bwconncomp(idx1).NumObjects
See bwconncomp()
I don't find a solution to this problem
In my initialization I define an array "R" with certain number of values (the boolean flag with have the same length too). Later in my code I do an addition with a boolean flag.
Do you have an idea how to "update" this addition without editing it manually?
The part of the code i want to improve is
( (R(5)*B(i,5))+ (R(1)*B(i,1)) + (R(3)*B(i,3)) +(R(4)*B(i,4)) +(R(2)*B(i,2)) )
Thank you in advance for you answear
the code :
% i reflects the time
% bollean flag type double
B(1,:)=[0 0 0 0 0];
B(2,:)=[0 0 0 0 0];
B(3,:)=[0 0 0 0 1];
B(4,:)=[0 0 0 1 0];
B(5,:)=[0 0 0 1 1];
%info1
E(1)=0;
E(2)=0;
E(3)=10;
E(4)=20;
E(5)=40;
%info2
R = [1/30 1/30 1/30 1/30 1/30];
for i=1:5
for k=1:5
if E(i)>0
powerload_R2(i,k)= ( ( R(k))/( (R(5)*B(i,5))+ (R(1)*B(i,1)) + (R(3)*B(i,3)) +(R(4)*B(i,4)) +(R(2)*B(i,2)) ) ) *B(i,k)*E(i)+0; % fonctionnel
else
powerload_R2(i,k)= 0;
end
end
end
'end'
results
%results
powerload_R2(i,k)=
0 0 0 0 0
0 0 0 0 0
0 0 0 0 10
0 0 0 20 0
0 0 0 20 20
Your code could be greatly simplified. As #AnderBiguri has mentioned, this long line (R(5)*B(i,5))+ (R(1)*B(i,1)) + (R(3)*B(i,3)) +(R(4)*B(i,4)) +(R(2)*B(i,2)) is just the sum of the product of R elements with the corresponding elements of the ith row of B, or simply dot(R,B(i,:)).
Also you can initialize powerload_R2 = zeros(5) and alter only those rows corresponding to E > 0. This way, you only have to iterate find(E > 0) times over the rows of powerload_R2 and you don't need the inner k loop. That said, loops are not as evil these days as they used to be on the early years of MATLAB, so use the most natural way to write your algorithm before thinking about vectorization for speed.
% i reflects the time
% boolean flag type double
B = [0 0 0 0 0
0 0 0 0 0
0 0 0 0 1
0 0 0 1 0
0 0 0 1 1];
% info1
E = [0 0 10 20 40];
% info2
R(1:5) = 1/30;
powerload_R2 = zeros(5);
for i = find(E > 0)
powerload_R2(i,:) = R ./ dot(R,B(i,:)) .* B(i,:)*E(i); % fonctionnel
end
I have two matrices in MATLAB. Each one is filled with 1 and 0 at different positions. I want to compare each element:
If there is a 1 match, I want it to record as True Positive.
If there is a 0 match, I want it to record as True Negative.
If one says 1 and the other says 0, I want to record as False Positive.
If one says 0 and the other says 1, I want to record as False Negative.
I tried just comparing the two matrices:
idx = A == B
But, that gives me a simple match, not telling me when there is a True Positive or Negative, etc.
Is there any specific function I could use, or any alternative?
You could just add the matrices in a prescribed way....
a = [1 0 1 0
1 1 0 0
0 0 1 1];
b = [1 0 0 0
0 0 0 1
0 0 1 0];
C = a + 2*b;
% For pairs [a,b] we expect
% [0,0]: C = 0, true negative
% [1,0]: C = 1, false positive
% [0,1]: C = 2, false negative
% [1,1]: C = 3, true positive
% C =
% [ 3 0 1 0
% 1 1 0 2
% 0 0 3 1 ]
If you have the Statistics and Machine Learning toolbox and you only want a summary, you might just need the function confusionmat.
From the docs:
C = confusionmat(group,grouphat) returns the confusion matrix C determined by the known and predicted groups in group and grouphat. [...]. C is a square matrix with size equal to the total number of distinct elements in group and grouphat. C(i,j) is a count of observations known to be in group i but predicted to be in group j.
For example:
a = [1 0 1 0
1 1 0 0
0 0 1 1];
b = [1 0 0 0
0 0 0 1
0 0 1 0];
C = confusionmat( a(:), b(:) );
% C =
% [ 5 1
% 4 2]
% So for each pair [a,b], we have 5*[0,0], 2*[1,1], 4*[1,0], 1*[0,1]
A similar function for those with the Neural Network Toolbox instead would be confusion.
You could just use bitwise operators to produce the four different values:
bitor(bitshift(uint8(b),1),uint8(a))
Produces an array with
0 : True Negative
1 : False Negative (a is true but b is false)
2 : False Positive (a is false but b is true)
3 : True Positive
One naive approach would be four comparisons, case by case:
% Set up some artificial data
ground_truth = randi(2, 5) - 1
compare = randi(2, 5) - 1
% Determine true positives, false positives, etc.
tp = ground_truth & compare
fp = ~ground_truth & compare
tn = ~ground_truth & ~compare
fn = ground_truth & ~compare
Output:
ground_truth =
1 0 1 0 0
0 1 1 0 1
1 1 0 1 0
0 1 0 1 1
0 0 0 1 0
compare =
0 1 1 0 1
0 1 1 1 0
1 1 0 0 1
1 1 1 0 0
1 1 1 1 1
tp =
0 0 1 0 0
0 1 1 0 0
1 1 0 0 0
0 1 0 0 0
0 0 0 1 0
fp =
0 1 0 0 1
0 0 0 1 0
0 0 0 0 1
1 0 1 0 0
1 1 1 0 1
tn =
0 0 0 1 0
1 0 0 0 0
0 0 1 0 0
0 0 0 0 0
0 0 0 0 0
fn =
1 0 0 0 0
0 0 0 0 1
0 0 0 1 0
0 0 0 1 1
0 0 0 0 0
That works, because 0 and 1 (or any positive value) are alternative representations for true and false.
To keep your main code clean, set up a separate function, say my_stats.m
function [tp, fp, tn, fn] = my_stats(ground_truth, compare)
% Determine true positives, false positives, etc.
tp = ground_truth & compare;
fp = ~ground_truth & compare;
tn = ~ground_truth & ~compare;
fn = ground_truth & ~compare;
end
and call it in your main code:
% Set up some artificial data
ground_truth = randi(2, 5) - 1
compare = randi(2, 5) - 1
[tp, fp, tn, fn] = my_stats(ground_truth, compare)
Hope that helps!
I found that I can use the find method and set two conditions, then just find the numbers of the element in each variable
TruePositive = length(find(A==B & A==1))
TrueNegative = length(find(A==B & A==0))
FalsePositive = length(find(A~=B & A==1))
FalseNegative = length(find(A~=B & A==0))
The confusionmatrix() method suggested by #Wolfie is also really neat, especially if you use the confusionchart() which provides a nice visualisation.
I have a cell array (11000x500) with three different type of elements.
1) Non-zero doubles
2) zero
3) Empty cell
I would like to find all occurances of a non-zero number between two zeros.
E.g. A = {123 13232 132 0 56 0 12 0 0 [] [] []};
I need the following output
out = logical([0 0 0 0 1 0 1 0 0 0 0 0]);
I used cellfun and isequal like this
out = cellfun(#(c)(~isequal(c,0)), A);
and got the follwoing output
out = logical([1 1 1 0 1 0 1 0 0 1 1 1]);
I need help to perform the next step where i can ignore the consecutive 1's and only take the '1's' between two 0's
Could someone please help me with this?
Thanks!
Here is a quick way to do it (and other manipulations binary data) using your out:
out = logical([1 1 1 0 1 0 1 0 0 1 1 1]);
d = diff([out(1) out]); % find all switches between 1 to 0 or 0 to 1
len = 1:length(out); % make a list of all indices in 'out'
idx = [len(d~=0)-1 length(out)]; % the index of the end each group
counts = [idx(1) diff(idx)]; % the number of elements in the group
elements = out(idx); % the type of element (0 or 1)
singles = idx(counts==1 & elements==1)
and you will get:
singles =
5 7
from here you can continue and create the output as you need it:
out = false(size(out)); % create an output vector
out(singles) = true % fill with '1' by singles
and you get:
out =
0 0 0 0 1 0 1 0 0 0 0 0
You can use conv to find the elements with 0 neighbors (notice that the ~ has been removed from isequal):
out = cellfun(#(c)(isequal(c,0)), A); % find 0 elements
out = double(out); % cast to double for conv
% elements that have more than one 0 neighbor
between0 = conv(out, [1 -1 1], 'same') > 1;
between0 =
0 0 0 0 1 0 1 0 0 0 0 0
(Convolution kernel corrected to fix bug found by #TasosPapastylianou where 3 consecutive zeros would result in True.)
That's if you want a logical vector. If you want the indices, just add find:
between0 = find(conv(out, [1 -1 1], 'same') > 1);
between0 =
5 7
Another solution, this completely avoids your initial logical matrix though, I don't think you need it.
A = {123 13232 132 0 56 0 12 0 0 [] [] []};
N = length(A);
B = A; % helper array
for I = 1 : N
if isempty (B{I}), B{I} = nan; end; % convert empty cells to nans
end
B = [nan, B{:}, nan]; % pad, and collect into array
C = zeros (1, N); % preallocate your answer array
for I = 1 : N;
if ~any (isnan (B(I:I+2))) && isequal (logical (B(I:I+2)), logical ([0,1,0]))
C(I) = 1;
end
end
C = logical(C)
C =
0 0 0 0 1 0 1 0 0 0 0 0
In Matlab I have a vectors that looks like this:
0 0 1 1 0 0 0 1 1 0 0 0 0 1 1 1 0 0 0 0 1 0 1
What I want to do now is to count the number of 1 in this vector. Consecutive 1s count as 1. Additionally, I want also to calculate the average and median numbers of 0s between 1s. So for this example:
1s: 5
Median 0s: 3.5
Average 0s: 3
I solved that with a brute force method, that is investigate each element in a loop and check the previous as well as the next element. But I'm sure there has to be a solution that is way faster. Any idea?
Given the data in vector v,
v = [ 0 0 1 1 0 0 0 1 1 0 0 0 0 1 1 1 0 0 0 0 1 0 1 ]; % data
compute as follows:
w = [ 1 v 1 ]; % auxiliary vector
runs_zeros = find(diff(w)==1)-find(diff(w)==-1); % lenghts of runs of 0's
% Desired results:
number_ones = length(runs_zeros)-1+v(1)+v(end);
% For average and median, don't count first run if v(1) is 0,
% or last run if v(end) is 0:
average_runs_zeros = mean(runs_zeros(2-v(1):end-1+v(end)));
median_runs_zeros = median(runs_zeros(2-v(1):end-1+v(end)));
This is faster than #TryHard's solution because it doesn't require converting to strings
Okay, so this seems to be working
>> a=[0 0 1 1 0 0 0 1 1 0 0 0 0 1 1 1 0 0 0 0 1 0 1];
>> %Remove traling and leading zeros
>> y = a(find(a,1,'first'):find(a,1,'last'));
>> q = diff([0 a 0] == 1);
>> v = find(q == -1) - find(q == 1);
>> length(v) % Consecutive Ones
ans =
5
>> q = diff([0 ~y 0] == 1);
>> v = find(q == -1) - find(q == 1);
>> v
v =
3 4 4 1
>> median(v)
ans =
3.5000
>> mean(v)
ans =
3
You can do it as follows:
dat=[0 0 1 1 0 0 0 1 1 0 0 0 0 1 1 1 0 0 0 0 1 0 1];
str = regexprep(num2str(dat),' ','');
[n1 istart1 iend1] = regexp(str,'[1]+','match','start','end');
[n0 istart0 iend0] = regexp(str(min(istart1):max(iend1)),'[0]+','match','start','end');
% number of strings of `1`s
length(n1)
% property of intercalated strings of `0`s
median([iend0-istart0+1])
mean([iend0-istart0+1])