Using hist in Matlab to compute occurrences - arrays

I am using hist to compute the number of occurrences of values in a matrix in Matlab.
I think I am using it wrong because it gives me completely weird results. Could you help me to understand what is going on?
When I run this piece of code I get countsB as desired
rng default;
B=randi([0,3],10,1);
idxB=unique(B);
countsB=(hist(B,idxB))';
i.e.
B=[3;3;0;3;2;0;1;2;3;3];
idxB=[0;1;2;3];
countsB=[2;1;2;5];
When I run this other piece of code I get wrong results for countsA
A=ones(524288,1)*3418;
idxA=unique(A);
countsA=(hist(A,idxA))';
i.e.
idxA=3148;
countsA=[zeros(1709,1); 524288; zeros(1708,1)];
What am I doing wrong?

To add to the other answers: you can replace hist by the explicit sum:
idxA = unique(A);
countsA = sum(bsxfun(#eq, A(:), idxA(:).'), 1);

idxA is a scalar, which means the number of bins in this context.
setting idxA as a vector instead e.g. [0,3418] will get you a hist with bins centered at 0 and 3418, similarly to what you got with idxB, which was also a vector

I think it has to do with:
N = HIST(Y,M), where M is a scalar, uses M bins.
and I think you are assuming it would do:
N = HIST(Y,X), where X is a vector, returns the distribution of Y
among bins with centers specified by X.
In other words, in the first case matlab is assuming that you are asking for 3418 bins

Related

How to select part of complex vector in Matlab

This is probably a trivial question, but I want to select a portion of a complex array in order to plot it in Matlab. My MWE is
n = 100;
t = linspace(-1,1,n);
x = rand(n,1)+1j*rand(n,1);
plot(t(45):t(55),real(x(45):x(55)),'.--')
plot(t(45):t(55),imag(x(45):x(55)),'.--')
I get an error
Error using plot
Vectors must be the same length.
because the real(x(45):x(55)) bit returns an empty matrix: Empty matrix: 1-by-0. What is the easiest way to fix this problem without creating new vectors for the real and imaginary x?
It was just a simple mistake. You were doing t(45):t(55), but t is generated by rand, so t(45) would be, say, 0.1, and t(55), 0.2, so 0.1:0.2 is only 0.1. See the problem?
Then when you did it for x, the range was different and thus the error.
What you want is t(45:55), to specify the vector positions from 45 to 55.
This is what you want:
n = 100;
t = linspace(-1,1,n);
x = rand(n,1)+1j*rand(n,1);
plot(t(45:55),real(x(45:55)),'.--')
plot(t(45:55),imag(x(45:55)),'.--')

a faster way to compute the error of a vector

For a given vector $(x_1,x_2,\ldots, x_n)$ I am trying to compute
I wrote the following code
for l = 1:n
for k = 1:n
error = error + norm(x(i)-x(j))
end
end
This code is not fast, especially when $n$ is large. I am aware that I am double counting actually... But how may I avoid it? How can I speed up my code?
Thank you!
You can do it with bsxfun, which is fast:
d = (abs(bsxfun(#minus, x, x.')));
result = sum(d(:));
Or alternatively use pdist with 'cityblock' distance (which for one-dimensional observations reduces to absolute difference). This computes each distance once, so you need to multiply the sum by 2:
result = 2*sum(pdist(x(:),'cityblock'));
How about a simple speed up?
for a=1:n
for b=a+1:n
error = error + 2*norm(x(a)-x(b))
end
end
For a scalar, norm just gives abs.
So,
error = sum(abs( bsxfun(#minus, error,error') ))
will do the same thing.
also check out pdist which will do this for vectors, using vector norms, in an even faster way.

Matlab: average each element in 2D array based on neighbors [duplicate]

I've written code to smooth an image using a 3x3 averaging filter, however the output is strange, it is almost all black. Here's my code.
function [filtered_img] = average_filter(noisy_img)
[m,n] = size(noisy_img);
filtered_img = zeros(m,n);
for i = 1:m-2
for j = 1:n-2
sum = 0;
for k = i:i+2
for l = j:j+2
sum = sum+noisy_img(k,l);
end
end
filtered_img(i+1,j+1) = sum/9.0;
end
end
end
I call the function as follows:
img=imread('img.bmp');
filtered = average_filter(img);
imshow(uint8(filtered));
I can't see anything wrong in the code logic so far, I'd appreciate it if someone can spot the problem.
Assuming you're working with grayscal images, you should replace the inner two for loops with :
filtered_img(i+1,j+1) = mean2(noisy_img(i:i+2,j:j+2));
Does it change anything?
EDIT: don't forget to reconvert it to uint8!!
filtered_img = uint8(filtered_img);
Edit 2: the reason why it's not working in your code is because sum is saturating at 255, the upper limit of uint8. mean seems to prevent that from happening
another option:
f = #(x) mean(x(:));
filtered_img = nlfilter(noisy_img,[3 3],f);
img = imread('img.bmp');
filtered = imfilter(double(img), ones(3) / 9, 'replicate');
imshow(uint8(filtered));
Implement neighborhood operation of sum of product operation between an image and a filter of size 3x3, the filter should be averaging filter.
Then use the same function/code to compute Laplacian(2nd order derivative, prewitt and sobel operation(first order derivatives).
Use a simple 10*10 matrix to perform these operations
need matlab code
Tangentially to the question:
Especially for 5x5 or larger window you can consider averaging first in one direction and then in the other and you save some operations. So, point at 3 would be (P1+P2+P3+P4+P5). Point at 4 would be (P2+P3+P4+P5+P6). Divided by 5 in the end. So, point at 4 could be calculated as P3new + P6 - P2. Etc for point 5 and so on. Repeat the same procedure in other direction.
Make sure to divide first, then sum.
I would need to time this, but I believe it could work a bit faster for larger windows. It is sequential per line which might not seem the best, but you have many lines where you can work in parallel, so it shouldn't be a problem.
This first divide, then sum also prevents saturation if you have integers, so you might use the approach even in 3x3 case, as it is less wrong (though slower) to divide twice by 3 than once by 9. But note that you will always underestimate final value with that, so you might as well add a bit of bias (say all values +1 between the steps).
img=imread('camraman.tif');
nsy-img=imnoise(img,'salt&pepper',0.2);
imshow('nsy-img');
h=ones(3,3)/9;
avg=conv2(img,h,'same');
imshow(Unit8(avg));

How to find the maximum of multiple arrays in MATLAB?

Let's say we have an array x. We can find the maximum value of this array as follows:
maximum = max(x);
If I have two arrays, let's say x and y, I can find the array that contains the maximum value by using the command
maximum_array = max(x, y);
Let's say that this array is y. Then, I can find the maximum value by using the max command with argument y, as before with x:
maximum_value = max(y);
This two-step procedure could be performed with the following compact, one-liner command:
maximum_value = max(max(x, y));
But what happens when we have more than 2 arrays? As far as I know, the max function does not allow to compare more than two arrays. Therefore, I have to use max for pairs of arrays, and then find the max among the intermediate results (which involves also the use of additional variables). Of course, if I have, let's say, 50 arrays, this would be - and it really is - a tedius process.
Is there a more efficient approach?
Approach #1
Concatenate column vector versions of them along dim-2 with cat and then use maximium values with max along dim-2 to get the max.
Thus, assuming x, y and z to be the input arrays, do something like this -
%// Reshape all arrays to column vectors with (:) and then use cat
M = cat(2,x(:),y(:),z(:))
%// Use max along dim-2 with `max(..,[],2)` to get column vector
%// version and then reshape back to the shape of input arrays
max_array = reshape(max(M,[],2),size(x))
Approach #2
You can use ndims to find the number of dimensions in the input arrays and then concatenate along the dimension that is plus 1 of that dimension and finally find max along it to get the maximum values array. This would avoid all of that reshaping back and forth and thus could be more efficient and a more compact code as well -
ndimsp1 = ndims(x)+1 %// no. of dimensions plus 1
maxarr = max(cat(ndimsp1,x,y,z),[],ndimsp1) %// concatenate and find max
I think the easiest approach for a small set of arrays is to column-ify and concatenate:
maxValue = max([x(:);y(:)]);
For a large number of arrays in some data structure (e.g. a cell array or a struct), I simple loop would be best:
maxValue = max(cellOfMats{1}(:));
for k = 2:length(cellOfMats)
maxValue = max([maxValue;cellOfMats{k}(:)]);
end
For the pathological case of a large number of separate arrays with differing names, I say "don't do that" and put them in a data structure or use eval with a loop.

MATLAB: Defining n subsets of a matrix

I have a 1974x1 vector, Upper, and I am trying to break the information up into individual arrays of 36 items each. So, I used length to find that there are 1974 items and then divided by 36 and used the floor function. I cannot figure out how to do it all with n.
Here is my logic: I am defining n in an attempt to find the number of subsets that need to be defined. Then, I am trying to have subsetn become subset1, subset2,...,subset36. However, MATLAB only definies the matrix subsetn as a 1x36 matrix. However, this matrix contains what subset1 is supposed to contain(1...36). Do you guys have any advice for a newbie? What am I doing wrong?
binSize = 36;
nData = length(Upper);
nBins = floor(nData/36);
nDiscarded = nData - binSize*nBins;
n=1:binSize;
subsetn= [(n-1)*binSize+1:n*binSize];
You can create a 54x36 array where the nth column is your nth subset.
subsetArray=reshape(x(1:binSize*nBins),[],nBins);
You can access the nth subset as subsetArray(:,n)
Sorry in advance if I misunderstood what you want to do.
I think the following little trick might do what you want (it's hacky, but I'm no Matlab expert):
[a, b] = meshgrid(0:nBins-1, 0:binSize-1)
inds = a*binSize + b + 1
Now inds is a nBins*binSize matrix of indices. You can index Upper with it like
Upper(inds)
which should give you the subsets as the columns in the resulting matrix.
Edit: on seeing Yoda's answer, his is better ;)

Resources