Adjusting the x-axis scale on a bar chart in MATLAB - matlab

I am trying to adjust the scale of the x-axis so that the values are closer together, but I am not able to do so.
I need the output to be like this photo:
However, what I actually get is the photo below:
Here's the code I have written to reproduce this error:
x = [0.1 1 10 100 1000 10000];
y = [1.9904 19.8120 82.6122 93.0256 98.4086 99.4016];
figure;
bar(x,y);
ylabel('Y values');
xlabel('X values');
set(gca,'XTick', [0.1 1 10 100 1000 10000])
How can I adjust the x-axis so that it looks like the first photo?

Because your data has such huge dynamic range, and because of the linear behaviour of the x axis, your graph is naturally going to appear like that. One compromise that I can suggest is that you transform your x data so that it gets mapped to a smaller scale, then remap your x data so that it falls onto a small exponential scale. After, simply plot the data using this remapped scale, then rename the x ticks so that they have the same values as your x data. To do this, I would take the log10 of your data first, then apply an exponential to this data. In this way, you are scaling the x co-ordinates down to a smaller dynamic range. When you apply the exponential to this smaller range, the x co-ordinates will then spread out in a gradual way where higher values of x will certainly make the value go farther along the x-axis, but not too far away like you saw in your original plot.
As such, try something like this:
x = [0.1 1 10 100 1000 10000]; %// Define data
y = [1.9904 19.8120 82.6122 93.0256 98.4086 99.4016];
xplot = (1.25).^(log10(x)); %// Define modified x values
figure;
bar(xplot,y); %// Plot the bar graph on the modified scale
set(gca,'XTick', xplot); %// Define ticks only where the bars are located
set(gca,'XTickLabel', x); %// Rename these ticks to our actual x data
This is what I get:
Note that you'll have to play around with the base of the exponential, which is 1.25 in what I did, to suit your data. Obviously, the bigger the dynamic range of your x data, the smaller this exponent will have to be in order for your data to be closer to each other.
Edit from your comments
From your comments, you want the bars to be equidistant in between neighbouring bars. As such, you simply have to make the x axis linear in a small range, from... say... 1 to the total number of x values. You'd then apply the same logic where we rename the ticks on the x axis so that they are from the true x values instead. As such, you only have to change one line, which is xplot. The other lines should stay the same. Therefore:
x = [0.1 1 10 100 1000 10000]; %// Define data
y = [1.9904 19.8120 82.6122 93.0256 98.4086 99.4016];
xplot = 1:numel(x); %// Define modified x values
figure;
bar(xplot,y); %// Plot the bar graph on the modified scale
set(gca,'XTick', xplot); %// Define ticks only where the bars are located
set(gca,'XTickLabel', x); %// Rename these ticks to our actual x data
This is what I get:

Related

Matlab plot: equal distance between ticks

I have this 3D plot that I'm making but the data are not linear. This implies that on my plot, the distance between the ticks that I want to show is not equal. How can I adapt the scale of the x and y axis so that this is the case, i.e. so that the axis gets divided into equal parts with the current ticks?
I want the same ticks and tick labels, but that they just have an equal distance in between them on the axes, in stead of small between 0.1 and 0.5 and large between 1 and 5.
The current plot looks like this:
RMSEval = xlsread('RMSEvalues.xlsx');
X = RMSEval(:,1);
Y = RMSEval(:,2);
Z = RMSEval(:,3);
figure(1);
xi = linspace(min(X),max(X),30);
yi= linspace(min(Y),max(Y),30);
[XI,YI] = meshgrid(xi,yi);
ZI = griddata(X,Y,Z,XI,YI);
contourf(XI,YI,ZI);
colormap('jet');
xticks([1e-13 5e-13 1e-12 5e-12 1e-11]);
yticks([1e-18 5e-18 1e-17 5e-17 1e-16]);
colorbar;
A plot where the distance between 0.1 and 0.5 is the same as between 1 and 5 would be a log plot, specifically a log-log plot since you want it on both axes. One way to achieve this would be to transform the X and Y values of your data logarithmically and modify the tick mark labels to match the untransformed values rather than the logarithmic ones you're actually plotting.
A rough guess at a solution is below. I say a rough guess because without posting the data you're importing from the xlsx file or a paired down version of that data (as in an MWE) I can't actually test it.
RMSEval = xlsread('RMSEvalues.xlsx');
X = log(RMSEval(:,1));
Y = log(RMSEval(:,2));
Z = RMSEval(:,3);
figure(1);
xi = linspace(min(X),max(X),30);
yi= linspace(min(Y),max(Y),30);
[XI,YI] = meshgrid(xi,yi);
ZI = griddata(X,Y,Z,XI,YI);
contourf(XI,YI,ZI);
colormap('jet');
xticks(log([1e-13 5e-13 1e-12 5e-12 1e-11]));
xticklabels(cellfun(#num2str,num2cell(),'UniformOutput',false));
yticks(log([1e-18 5e-18 1e-17 5e-17 1e-16]));
yticklabels(cellfun(#num2str,num2cell([1e-18 5e-18 1e-17 5e-17 1e-16]),'UniformOutput',false));
colorbar;

Plotting Bar graph with logarithmic x axis

I have a matrix with 2 columns that I would like to display in a bar graph. The y axis should be in intervals of 5 and the x axis in a logarithmic scale of base 10.
As an example I have a matrix [45660 0 ; 3213 5 ; 45631 10 ; 45631 15 ; 300 20]
I found a way to display these points with a logarithmic scale on the x axis, but I would like it to be a bar graph to make it easier to see which 'y' value the points are corresponding to. The width of each bar can be from the x axis value of one point to the x axis value of the next.
PeakCount1=[Load_range Load_count];
semilogx(PeakCount1(:,2),PeakCount1(:,1),'.','MarkerSize',15);
xlabel('Number of Peaks')
ylabel('Load Range (kN)')
title('Number of Peaks Occurring Within Maximum Ice Load Range');
saveas(gcf,'PeakCount_Loadlevels','bmp')
What about using
stem(PeakCount1(:,2),PeakCount1(:,1),'LineWidth',10.0);
set(gca,'XScale','log');
Instead using stem you can use any plot function you like, just insert the second line to make the x axis log.

How to make a vector that follows a certain trend?

I have a set of data with over 4000 points. I want to exclude grooves from them, ideally from the point from which they start. The data look for example like this:
The problem with this is the noise I get at the top of the plateaus. I have an idea, in which I would take an average value of the most common within some boundaries (again, ideally sth like the red line here:
and then I would construct a temporary matrix, which would fill up one by one with Y if they are less than this average. If the Y(i) would rise above average, the matrix would find its minima and compare it with the global minima. If the temporary matrix's minima wouldn't be sth like 80% of the global minima, it would be discarded as noise.
I've tried using mean(Y), interpolating and fitting it in a polynomial (the green line) - none of those method would cut it to the point I would be satisfied.
I need this to be extremely robust and it doesn't need to be quick. The top and bottom values can vary a lot, as well as the shape of the plateaus. The groove width is more or less the same.
Do you have any ideas? Again, the point is to extract the values that would make the groove.
How about a median filter?
Let's define some noisy data similar to yours, and plot it in blue:
x = .2*sin((0:9999)/1000); %// signal
x(1000:1099) = x(1000:1099) + sin((0:99)/50*pi); %// noise: spike
x(5000:5199) = x(5000:5199) - sin((0:199)/100*pi); %// noise: wider spike
x = x + .05*sin((0:9999)/10); %// noise: high-freq ripple
plot(x)
Now apply the median filter (using medfilt2 from the Image Processing Toolbox) and plot in red. The parameter k controls the filter memory. It should chosen to be large compared to noise variations, and small compared to signal variations:
k = 500; %// filter memory. Choose as needed
y = medfilt2(x,[1 k]);
hold on
plot(y, 'r', 'linewidth', 2)
In case you don't have the image processing toolbox and can't use medfilt2 a method that's more manual. Skip the extreme values, and do a curve fit with sin1 as curve type. Note that this will only work if the signal is in fact a sine wave!
x = linspace(0,3*pi,1000);
y1 = sin(x) + rand()*sin(100*x).*(mod(round(10*x),5)<3);
y2 = 20*(mod(round(5*x),5) == 0).*sin(20*x);
y = y1 + y2; %// A messy sine-wave
yy = y; %// Store the messy sine-wave
[~, idx] = sort(y);
y(idx(1:round(0.15*end))) = y(idx(round(0.15*end))); %// Flatten out the smallest values
y(idx(round(0.85*end):end)) = y(idx(round(0.85*end)));%// Flatten out the largest values
[foo goodness output] = fit(x.',y.', 'sin1'); %// Do a curve fit
plot(foo,x,y) %// Plot it
hold on
plot(x,yy,'black')
Might not be perfect, but it's a step in the right direction.

Matlab Boxplots

I'd like to create a quasi boxplot graph as shown on pages 15/16 of the attached report.
comisef.eu/files/wps031.pdf
Ideally I only want to show the median, the maximum and minimum values as in the report.
I would also like to have similar spacing to that shown in the report.
Currently I have two matrices with the all the necessary values stored in them but have no idea how to do this in matlab.
The boxplot function gives too much data (outliers etc) which makes the resulting graph look confused especially when I try to plot 200 on one page as in the original report.
Is there another function that can so the same thing as in the report in matlab?
Baz
OK here is some test data each row represents 10 sets of estimations of a data set, and each column represents the test number for a given observation.
As boxplot works on the columns of the input matrix you will need to transpose the matrix.
Is it possible to turn outliers and the inter-quartile ranges off? Ideally I just want to see the maximum, minimum and median values?
You can repeat the data below to get up to 200. Or I can send more data if necessary.
0.00160329732202511 0.000859407819412016 0.000859407819411159 0.0659939338995606 0.000859407819416322 0.000859407819416519 2.56395024851142e-15 2.05410662537078e-14 0.000859407819416209
1.67023155116586e-06 8.88178419700125e-16 1.67023155115637e-06 0.000730536218639616 1.67023155105582e-06 3.28746017489609e-15 4.41416632660789e-15 1.67023155094400e-06 1.67023155097567e-06
1.42410590843629e-06 1.42410590840224e-06 1.76149166727218e-15 5.97790925044131e-15 1.42410590843863e-06 2.87802701599909e-15 9.31529385335274e-16 9.17306727455842e-16 0.000820358763518906
8.26849110292527e-16 3.23505095414772e-15 4.38139485761850e-07 4.38139485938112e-07 4.38139485981887e-07 0.000884647755317917 3.72611754134110e-15 4.38139485974329e-07 4.38139485923219e-07
0.000160661751819407 0.000870787937135265 0.000870787937136209 1.16934122581182e-15 9.02860049358913e-16 1.18053134896556e-15 1.40433338743068e-15 0.000870787937135929 1.13510916297112e-15
1.16934122581182e-15 3.80292342262841e-05 3.80292342263200e-05 0.00284904319356532 1.74649997619656e-15 3.80292342264024e-05 0.00284904319356537 1.01267920724547e-15 0.00284904319356540
0.100091800399985 0.100091773169254 0.100091803903140 0.000770464183529358 0.100091812455930 3.49996706323281e-05 3.49996706323553e-05 1.05090687851466e-15 0.100091846333800
0.00100555294602561 0.00100555294601056 0.105365907420183 0.000121078082591672 9.02860049358913e-16 0.000121078082591805 4.49679158258033e-15 7.77684615168284e-16 0.000121078082591693
0.122539456858702 0.000363547764643498 0.000363547764643509 0.122516928568610 0.0101487499394213 0.122408366511784 0.000363547764643519 1.13510916297112e-15 0.122521393586646
0.000460749357561036 0.000460749357560646 3.27600489447913e-13 1.18053134896556e-15 0.000460749357561239 1.54689304063675e-15 0.000460749357560827 0.000460749357561205 1.16934122581182e-15
Instead of using boxplot, I suggest just drawing lines from the min to the max and making a mark at the median. Boxplot draws boxes from the 25 to 75 percentile, which doesn't sound like what you want. Something like this:
% fake data
nPoints = 100;
data = 10*rand(10, nPoints);
% find statistics
minData = min(data, [], 1);
maxData = max(data, [], 1);
medData = median(data);
% x coordinates of each line. Change this to change the spacing.
x = 1:nPoints;
figure
hold on
%plot lines
line([x; x], [minData; maxData])
% plot cross at median
plot(x, medData, '+')
EDIT: To have horizontal lines and a second axis you can do something like this:
figure
h1 = subplot(1,2,1);
h2 = subplot(1,2,2);
% left subplot
axes(h1)
hold on
%plot lines
line([minData; maxData], [x; x])
% plot cross at median
plot(medData, x, '+')
% link the axes so they will have the same limits
linkaxes([h1,h2],'y')
% turn off ticks on y axis.
set(h2, 'YTick', [])
I think it's a question of playing with the settings. You can try:
boxplot(X, 'plotstyle', 'compact', 'colors', 'k', 'medianstyle', 'line', 'outliersize', 0);
Explanation:
'plotstyle', 'compact': makes the boxes filled and the lines undashed
'colors', 'k': color is black
'medianstyle', 'line': the median is marked by a line
'outliersize', 0: if outlier size is zero, you don't see them
Other you can try:
'orientation', 'vertical': this flips the orientation, depends on your data
'whisker', 10 (or higher): this sets the maximum whisker length as a function of the interquartile limits (if you crank it up, it will eventually default to max and min values), I wasn't sure if this is what you wanted. Right now, it goes to the 25th and 75th percentile values.
The spacing is going to depend on how much data you have. If you edit with some data, I can try it out for you.

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(:));