How to display labels above a histogram bin? - matlab

I have an array a(30,2) where the first column is a unique sample number and the second column is a value assigned to the sample. I plot a histogram of the 2nd column:
hist(a(:,2))
I have N bins and the y-axis tells me how many samples have a value of x,
but no information about which samples are in which bin.
How do I plot, above each bin, a list of samples (the number from the first column of my array a) that fall into each bin?

As was shown by both #Jonas and #Itamar Katz, the idea is to use HISTC to get the bin indices that each sample belongs to, then use BAR to plot the result (note that we use the 'histc' mode of display for the BAR function). My answer below is a variation of #Jonas's post:
[EDITED]
%# random data
a = [(1:30)' rand(30,1)]; %'#
%# compute edges (evenly divide range into bins)
nBins = 10;
edges = linspace(min(a(:,2)), max(a(:,2)), nBins+1);
%# compute center of bins (used as x-coord for labels)
bins = ( edges(1:end-1) + edges(2:end) ) / 2;
%# histc
[counts,binIdx] = histc(a(:,2), edges);
counts(end-1) = sum(counts(end-1:end)); %# combine last two bins
counts(end) = []; %#
binIdx(binIdx==nBins+1) = nBins; %# also fix the last bin index
%# plot histogram
bar(edges(1:end-1), counts, 'histc')
%#bar(bins, counts, 'hist') %# same thing
ylabel('Count'), xlabel('Bins')
%# format the axis
set(gca, 'FontSize',9, ...
'XLim',[edges(1) edges(end)], ... %# set x-limit to edges
'YLim',[0 2*max(counts)], ... %# expand ylimit to accommodate labels
'XTick',edges, ... %# set xticks on the bin edges
'XTickLabel',num2str(edges','%.2f')) %'# round to 2-digits
%# add the labels, vertically aligned on top of the bars
hTxt = zeros(nBins,1); %# store the handles
for b=1:nBins
hTxt(b) = text(bins(b), counts(b)+0.25, num2str(a(b==binIdx,1)), ...
'FontWeight','bold', 'FontSize',8, 'EdgeColor','red', ...
'VerticalAlignment','bottom', 'HorizontalAlignment','center');
end
%# set the y-limit according to the extent of the text
extnt = cell2mat( get(hTxt,'Extent') );
mx = max( extnt(:,2)+extnt(:,4) ); %# bottom+height
ylim([0 mx]);
If the ticks on the x-axis become too crowded, you can display them rotated with an angle using XTICKLABEL_ROTATE function (submission on FEX).

First, create a histogram using HISTC as suggested by #Itamar Katz. To make the bins the same as with HIST, you need to properly calculate bin edges. Then you can plot the distribution and add the labels using TEXT and NUM2STR.
%# get the edges, bin centers
nBins = 10;
edges = linspace(min(a(:,2),max(a(:,2),nBins+1); %# edges go from minimum to maximum of distribution
bins = (edges(1:end-1)+edges(2:end))/2;
%# get the counts and the bin-index
[counts,binIdx] = histc(a(:,2),edges);
%# plot the counts and bins (not edges) with `bar`
figure
bar(bins,counts);
%# Set the axes limits such that you have enough space for the labels
ylim([0,2*max(counts)]);
%# add the labels. Vertically align such that the text goes from the y-coordinate
%# down (as opposed to being centered on the y-coordinate).
for b = 1:nBins
text(bins(b),counts(b)*2,num2str(a(b==binIdx,1)),'VerticalAlignment','top')
end

Use histc, which return an index for each entry, to which bin did it 'fall':
[n, bin] = histc (a(:, 2), bins);
Then the samples above the k'th bin is:
a(bin==k, 1);
Pay attention, you must specify the bins' boundaries yourself (unlike hist which uses the mid-value between boundaries).

Related

Calculate the pixel distance to three defined pixel in matlab

I want to classify pixels of one tiff image according to pixel's RGB colour. The input is an image and three predefined colours for water(r0,g0,b0), forest(r1,g1,b1) and building(r2,g2,c2). The classification is based on the distance between image pixel and these three colors. If a pixel is closet to the water, the pixel is water and changed it the water RGB. The distance is calculated as (one sample) sqrt((x-r0)^2+(y-g0)^2+(z0-b0)^2)
The sample implementation is:
a=imread(..);
[row col dim] = size(a); % dim =3
for i=1:row
for j=1:col
dis1=sqrtCal(a(i,j,:)-water)
dis2=sqrtCal(a(i,j,:)-forest)
dis3=sqrtCal(a(i,j,:)-build)
a(i,j,:) = water;
if dis2< dis1
dis1 = dis2
a(i,j,:) = forest
end
dis3=sqrt(a(i,j,:)-build)
if dis3<dis1
a(i,j,:) = build
end
end
end
This implementation should work. The problem is that the two for loops is not a good choice.
So is there any good alternatives in the Matlab? The
D = pdist(X,distance)
Seems not appliable for my case.
I think this does what you want. The number of reference colors is arbitrary (3 in your example). Each reference color is defined as a row of the matrix ref_colors. Let MxN denote the number of pixels in the original image. Thus the original image is an MxNx3 array.
result_index is an MxN array which for each original pixel contains the index of the closest reference color.
result is a MxNx3 array in which each pixel has been assigned the RBG values of the closest reference color.
im = rand(10,12,3); %// example image
ref_colors = [ .1 .1 .8; ... %// water
.3 .9 .2; ... %// forest
.6 .6 .6 ]; %// build: example reference colors
dist = sum(bsxfun(#minus, im, permute(ref_colors, [3 4 2 1])).^2, 3);
%// 4D array containing the distance from each pixel to each reference color.
%// Dimensions are: 1st: pixel row, 2nd: pixel col, 3rd: not used,
%// 4th: index of reference color
[~, result_index] = min(dist, [], 4); %// compute arg min along 4th dimension
result = reshape(ref_colors(result_index,:), size(im,1), size(im,2), 3);
This one is another bsxfun based solution, but stays in 3D and could be more efficient -
%// Concatenate water, forest, build as a 2D array for easy processing
wfb = cat(2,water(:),forest(:),build(:))
%// Calculate square root of squared distances & indices corresponding to min vals
[~,idx] = min(sum(bsxfun(#minus,reshape(a,[],3),permute(wfb,[3 1 2])).^2,2),[],3)
%// Get the output with the minimum from the set of water, forest & build
a_out = reshape(wfb(:,idx).',size(a))
If you have the statistics toolbox installed, you can use this version based on knnsearch, that scales well for a large number of colors.
result_index = knnsearch(ref_colors, reshape(im,[],3));
result = reshape(ref_colors(result_index,:), size(im));

Pixel subtraction

I am working on code that select set of pixels randomly from gray images, then comparing the intensity of each 2 pixels by subtracting the intensity of pixel in one location from another one in different location.
I have code do random selection, but I am not sure of this code and I do not know how to do pixels subtraction?
thank you in advance..
{
N = 100; % number of random pixels
im = imread('image.bmp');
[nRow,nCol,c] = size(im);
randRow = randi(nRow,[N,1]);
randCol = randi(nCol,[N,1]);
subplot(2,1,1)
imagesc(im(randRow,randCol,:))
subplot(2,1,2)
imagesc(im)
}
Parag basically gave you the answer. In order to achieve this vectorized, you need to use sub2ind. However, what I would do is generate two sets of rows and columns. The reason why is because you need one set for the first set of pixels and another set for the next set of pixels so you can subtract the two sets of intensities. Therefore, do something like this:
N = 100; % number of random pixels
im = imread('image.bmp');
[nRow,nCol,c] = size(im);
%// Generate two sets of locations
randRow1 = randi(nRow,[N,1]);
randCol1 = randi(nCol,[N,1]);
randRow2 = randi(nRow,[N,1]);
randCol2 = randi(nCol,[N,1]);
%// Convert each 2D location into a single linear index
%// for vectorization, then subtract
locs1 = sub2ind([nRow, nCol], randRow1, randCol1);
locs2 = sub2ind([nRow, nCol], randRow2, randCol2);
im_subtract = im(locs1) - im(locs2);
subplot(2,1,1)
imagesc(im_subtract);
subplot(2,1,2)
imagesc(im);
However, the above code only assumes that your image is grayscale. If you want to do this for colour, you'll have to do a bit more work. You need to access each channel and subtract on a channel basis. The linear indices that were defined above are just for a single channel. As such, you'll need to offset by nRow*nCol for each channel if you want to access the same corresponding locations in the next channels. As such, I would use sub2ind in combination with bsxfun to properly generate the right values for vectorizing the subtraction. This requires just a slight modification to the above code. Therefore:
N = 100; % number of random pixels
im = imread('image.bmp');
[nRow,nCol,c] = size(im);
%// Generate two sets of locations
randRow1 = randi(nRow,[N,1]);
randCol1 = randi(nCol,[N,1]);
randRow2 = randi(nRow,[N,1]);
randCol2 = randi(nCol,[N,1]);
%// Convert each 2D location into a single linear index
%// for vectorization
locs1 = sub2ind([nRow, nCol], randRow1, randCol1);
locs2 = sub2ind([nRow, nCol], randRow2, randCol2);
%// Extend to as many channels as we have
skip_ind = permute(0:nRow*nCol:(c-1)*nRow*nCol, [1 3 2]);
%// Create 3D linear indices
locs1 = bsxfun(#plus, locs1, skip_ind);
locs2 = bsxfun(#plus, locs2, skip_ind);
%// Now subtract the locations
im_subtract = im(locs1) - im(locs2);
subplot(2,1,1)
imagesc(im_subtract);
subplot(2,1,2)
imagesc(im);

MATLAB - Pixelize a plot and make it into a heatmap

I have a matrix with x and y coordinates as well as the temperature values for each of my data points. When I plot this in a scatter plot, some of the data points will obscure others and therefore, the plot will not give a true representation of how the temperature varies in my data set.
To fix this, I would like to decrease the resolution of my graph and create pixels which represent the average temperature for all data points within the area of the pixel. Another way to think about the problem that I need to put a grid over the current plot and average the values within each segment of the grid.
I have found this thread - Generate a heatmap in MatPlotLib using a scatter data set - which shows how to use python to achieve the end result that I want. However, my current code is in MATLAB and even though I have tried different suggestions such as heatmap, contourf and imagesc, I can't get the result I want.
You can "reduce the resolution" of your data using accumarray, where you specify which output "bin" each point should go in and specify that you wish to take a mean over all points in that bin.
Some example data:
% make points that overlap a lot
n = 10000
% NOTE: your points do not need to be sorted.
% I only sorted so we can visually see if the code worked,
% see the below plot
Xs = sort(rand(n, 1));
Ys = rand(n, 1);
temps = sort(rand(n, 1));
% plot
colormap("hot")
scatter(Xs, Ys, 8, temps)
(I only sorted by Xs and temps in order to get the stripy pattern above so that we can visually verify if the "reduced resolution" worked)
Now, suppose I want to decrease the resolution of my data by getting just one point per 0.05 units in the X and Y direction, being the average of all points in that square (so since my X and Y go from 0 to 1, I'll get 20*20 points total).
% group into bins of 0.05
binsize = 0.05;
% create the bins
xbins = 0:binsize:1;
ybins = 0:binsize:1;
I use histc to work out which bin each X and Y is in (note - in this case since the bins are regular I could also do idxx = floor((Xs - xbins(1))/binsize) + 1)
% work out which bin each X and Y is in (idxx, idxy)
[nx, idxx] = histc(Xs, xbins);
[ny, idxy] = histc(Ys, ybins);
Then I use accumarray to do a mean of temps within each bin:
% calculate mean in each direction
out = accumarray([idxy idxx], temps', [], #mean);
(Note - this means that the point in temps(i) belongs to the "pixel" (of our output matrix) at row idxy(1) column idxx(1). I did [idxy idxx] as opposed to [idxx idxy] so that the resulting matrix has Y == rows and X == columns))
You can plot like this:
% PLOT
imagesc(xbins, ybins, out)
set(gca, 'YDir', 'normal') % flip Y axis back to normal
Or as a scatter plot like this (I plot each point in the midpoint of the 'pixel', and drew the original data points on too for comparison):
xx = xbins(1:(end - 1)) + binsize/2;
yy = ybins(1:(end - 1)) + binsize/2;
[xx, yy] = meshgrid(xx, yy);
scatter(Xs, Ys, 2, temps);
hold on;
scatter(xx(:), yy(:), 20, out(:));

How can I create a plot like this in MATLAB?

I am trying to plot the ranges of different cellular base stations in MATLAB, like this:
But I can't figure out how to do it.
Here's an example of how you can create a plot like this. Note that I created sample data for the plot by randomly generating the positions of cellular base stations using uniformly distributed pseudorandom numbers:
%# Initializations:
minRange = 0; %# Lower x and y range
maxRange = 3.5; %# Upper x and y range
resolution = 1000; %# The number of data points on the x and y axes
cellRange = linspace(minRange, maxRange, resolution);
[x, y] = meshgrid(cellRange); %# Create grids of x and y coordinates
cellCoverage = zeros(size(x)); %# Initialize the image matrix to zero
%# Create the sample image data:
numBases = 200;
cellRadius = 0.75;
for iBase = 1:numBases
point = rand(1,2).*(maxRange - minRange) + minRange;
index = ((x - point(1)).^2 + (y - point(2)).^2) <= cellRadius^2;
cellCoverage(index) = cellCoverage(index) + 1;
end
%# Create the plot:
imagesc(cellRange, cellRange, cellCoverage); %# Scaled plot of image data
axis equal; %# Make tick marks on each axis equal
set(gca, 'XLim', [minRange maxRange], ... %# Set the x axis limit
'YLim', [minRange maxRange], ... %# Set the y axis limit
'YDir', 'normal'); %# Flip the y axis direction
xlabel('X-distance (km)'); %# Add an x axis label
ylabel('Y-distance (km)'); %# Add a y axis label
colormap(jet); %# Set the colormap
colorbar; %# Display the color bar
And here's the resulting plot:
Note also that the data in the image matrix cellCoverage contains no noise and has no smoothing applied, which is why the edges appear sharper than the original image in the post (which I'm guessing is generated from real data, not "fake" sample data like I used here).
Use "image"
image(x),colormap(hsv) <- where x is a matrix of cellular intensities(x,y)
You need to get the coordinate of each station then create a circle polygon around it (with a given radius), then convert this polygon into a grid. Then you sum up these grids (matrices) on top of each other. For speed, instead of using polygons you can also define which cells will be covered by a station, like all cells within 5 rows or columns of a station get the value.
You can also apply a 2D Gaussian filter to your matrix, where only the cells containing a station have a value of 1. The bandwidth of your Gaussian kernel will be your coverage radius (range). http://www.mathworks.ch/help/toolbox/images/ref/fspecial.html

How can I change the color of a specific bin in a histogram?

I wrote a code in MATLAB that plots a histogram. I need to color one of the bins in a different color than the other bins (let's say red). Does anybody know how to do it? For example, given:
A = randn(1,100);
hist(A);
How would I make the bin that 0.7 belongs to red?
An alternative to making two overlapping bar plots like Jonas suggests is to make one call to bar to plot the bins as a set of patch objects, then modify the 'FaceVertexCData' property to recolor the patch faces:
A = randn(1,100); %# The sample data
[N,binCenters] = hist(A); %# Bin the data
hBar = bar(binCenters,N,'hist'); %# Plot the histogram
index = abs(binCenters-0.7) < diff(binCenters(1:2))/2; %# Find the index of the
%# bin containing 0.7
colors = [index(:) ... %# Create a matrix of RGB colors to make
zeros(numel(index),1) ... %# the indexed bin red and the other bins
0.5.*(~index(:))]; %# dark blue
set(hBar,'FaceVertexCData',colors); %# Re-color the bins
And here's the output:
I guess the easiest way is to draw the histogram first and then just draw the red bin over it.
A = randn(1,100);
[n,xout] = hist(A); %# create location, height of bars
figure,bar(xout,n,1); %# draw histogram
dx = xout(2)-xout(1); %# find bin width
idx = abs(xout-0.7) < dx/2; %# find the bin containing 0.7
hold on;bar([xout(idx)-dx,xout(idx),xout(idx)+dx],[0,n(idx),0],1,'r'); %# plot red bar