Line up differently spaced data when using two axis in MATLAB? - matlab

I want to plot an x-axis (bot) that corresponds to values with another x-axis (top), against some data. I want the top and bot axis to refer to the same data point. The following code demonstrates my problem
clc; clear all;
botXaxis = [2.54 1.61 1.00 0.61 0.37];
topXaxis = [159283 97232 59354 36232 22117];
data = [10 10 10 10 10];
hplot = plot(botXaxis,data);
set(hplot,'Linestyle','none','Marker','o');
ax1 = gca;
ax1.XScale = 'log';
ax1.XTick = fliplr(botXaxis);
ax1.XLim = [min(botXaxis) max(botXaxis)];
ax1.XTickLabel = fliplr(botXaxis);
hold on
plot1 = plot([botXaxis(3) botXaxis(3)],get(ax1,'YLim'),'--','color',[0 0 0]);
ax1_pos = ax1.Position;
ax2 = axes('Position',ax1_pos,'XAxisLocation','top','YAxisLocation','right','Color','none');
ax2.XScale = 'log';
set(ax2, 'XTickMode', 'manual');
ax2.XLim = [min(topXaxis) max(topXaxis)];
ax2.XTickMode = 'auto';
ax2.XTick = fliplr(topXaxis);
hold on
plot1 = plot([topXaxis(3) topXaxis(3)],get(ax1,'YLim'),'-','color',[0 0 0]);
% Fix double tick marks
set(ax1,'box','off');
If you run it this plot appears:
I have drawn the straight lines to see if the data lines up correctly. As you can see they do not. I am wondering if there is a way to fix this? The values for data should line up with both topXaxis and botXaxis.
Thanks

Related

align axes in one figure

I have a figure with two different axes.
I cannot manage to get the axes aligned and to make the second axis invisible... I tried a couple if things (see comments in the code), but they did not work
clearvars
close all
clc
[X1,Y1,Z1] = peaks(25);
[X2,Y2,Z2] = peaks(25);
idx = find(X2>0);
Z2(idx) = NaN;
figure
set(gcf, 'Position', [0 0 800 800])
%%title
title('')
%%Create two axes
ax1 = axes;
pcolor(X1,Y1, Z1); shading flat
view(2)
ax2 = axes;
pcolor(X2,Y2, Z2); shading flat
%%link them
linkaxes([ax1,ax2]) %<==it didn't work
%ax1.XLim=[-3 3]; %<==I also tried this
%ax2.XLim=[-3 3];
%ax1.YLim=[-3 3];
%ax2.YLim=[-3 3];
%%Hide top axes
ax2.Visible = 'off'; %<== I thought that this would work
ax2.XTick = [];
ax2.YTick = [];
%%Colormaps
colormap(ax1, bone)
colormap(ax2, jet(26))
%%Add colorbars
set([ax1,ax2],'Position',[.17 .11 .685 .815]);
cb1 = colorbar(ax1,'position',[.08 .11 .03 .815]);
set(get(cb1,'ylabel'),'String','whatever','interpreter','latex', 'fontsize',20);
cb2 = colorbar(ax2,'position',[.92 .11 .03 .815]);
set(get(cb2,'ylabel'),'String','whatever','interpreter','latex', 'fontsize',20);
caxis(ax1,[-7 7])
caxis(ax2,[-5 5])
xlabel(ax1,'stuff')
ylabel(ax1,'other stuff')
Note: I am using 2017a
I really have no idea whether your approach is correct or not since I never attempted to produce a similar plot before. What I know is that when a figure is created, it already includes a default axis. Thus, calling the function axes() twice inserts two supplementar axes in the figure... and this is the proof:
Neither ax1 nor ax2 are the cause of this problem. The mismatching axis is the third one, the default one that was created together with the figure instance. What's happening in between is kinda weird (I spent some time trying to debug everything properly, but it's still not clear how instances are being handled)... anyway I found a workaround for deleting it:
clc();
clearvars();
close all;
[X1,Y1,Z1] = peaks(25);
[X2,Y2,Z2] = peaks(25);
idx = find(X2 > 0);
Z2(idx) = NaN;
f = figure();
set(gcf,'Position',[0 0 800 800])
title('');
ax1 = axes();
pcolor(X1,Y1,Z1);
shading flat;
xlabel(ax1,'stuff')
ylabel(ax1,'other stuff')
view(2);
ax2 = axes();
pcolor(X2,Y2,Z2);
shading flat;
ax2.Visible = 'off';
colormap(ax1,bone());
colormap(ax2,jet(26));
set([ax1,ax2],'Position',[.17 .11 .685 .815]);
cb1 = colorbar(ax1,'position',[.08 .11 .03 .815]);
set(get(cb1,'ylabel'),'String','whatever','interpreter','latex', 'fontsize',20);
cb2 = colorbar(ax2,'position',[.92 .11 .03 .815]);
set(get(cb2,'ylabel'),'String','whatever','interpreter','latex', 'fontsize',20);
caxis(ax1,[-7 7]);
caxis(ax2,[-5 5]);
set(ax1,'Tag','keep');
set(ax2,'Tag','keep');
delete(findall(f,'Type','Axes','-not','Tag','keep'));
Since the axes you create are referenced into a variable, once the plotting has been performed you assign the same Tag property to both. Then, using the findall function on the figure handle, you find the third axis (which is the one without the predefined Tag) and you delete it. Result:
EDIT
After further investigations, the followin code can be used instead for producing a cleaner version of this plotting:
clc();
clearvars();
close all;
[X1,Y1,Z1] = peaks(25);
[X2,Y2,Z2] = peaks(25);
idx = find(X2 > 0);
Z2(idx) = NaN;
f = figure();
set(gcf,'Position',[0 0 800 800])
ax1 = axes();
pcolor(X1,Y1,Z1);
shading flat;
xlabel(ax1,'stuff')
ylabel(ax1,'other stuff')
view(2);
ax2 = axes();
pcolor(X2,Y2,Z2);
shading flat;
ax2.Visible = 'off';
colormap(ax1,bone());
colormap(ax2,jet(26));
set([ax1,ax2],'Position',[.17 .11 .685 .815]);
cb1 = colorbar(ax1,'position',[.08 .11 .03 .815]);
set(get(cb1,'ylabel'),'String','whatever','interpreter','latex', 'fontsize',20);
cb2 = colorbar(ax2,'position',[.92 .11 .03 .815]);
set(get(cb2,'ylabel'),'String','whatever','interpreter','latex', 'fontsize',20);
caxis(ax1,[-7 7]);
caxis(ax2,[-5 5]);

Putting one legend for many axes

I'm using three axes-Objects to scale my data on the x-axis.
My problem is that i do not know how to get a nice legend for the three plots.
I have to do this cause my real data is sampled with different sample rates.
I edited my m-file for the diagram slightly cause normally I'm reading the data out of some txt files.
In this example i used example_data 1 to 3 for my data.
In this example I'm scaling the example_data1 so it looks like the same frequency as example_data2.
I do the 'scaling' ax1.XLim = [0 length(x2)].
That's why this solution doesn't work for me: Plot with multiple axes but only one legend.
It uses set(l3,'Parent',ax2); which somehow ruins my approache to scale my data. The scaling is the only solution to my problem cause i don't know the exact relation between the two sampling rates.
my code:
example_data1 = repmat(1:100,1,10);
example_data2 = 2 * repmat(1:0.5:100.5,1,5);
example_data3 = [1:500 500:-1:1];
whole_length_data1 = length(example_data1);
% 1. step
start_of_data = 1;
end_of_data = 1000;
% data2
y2 = example_data2(start_of_data:end_of_data);
x2 = 0:length(y2)-1;
% data3
y3 = example_data3(start_of_data:end_of_data);
x3 = 0:length(y3)-1;
% data1
y1 = example_data1(1:length(example_data1));
x1 = 0:length(y1)-1;
% 2. step
start_one = 1;
y1 = example_data1(start_one:length(example_data1));
x1 = 0:length(y1)-1;
% 3.step
end_one = whole_length_data1 - 500;
y1 = example_data1(start_one:end_one);
x1 = 0:length(y1)-1;
Farbe1 = [0,1,0]*0.6; % Dunkelgrün
Farbe2 = [1,0,0]*0.8; % Dunkelrot
Farbe3 = get(groot,'DefaultAxesColorOrder') % default values
Farbe3 = Farbe3(1,:); % 1. Zeile der defaultvalues
figure(1)
% 3 axes
clf
%------------------------------------------------------------------
%-------------------------- plot1: ---------------------------
%------------------------------------------------------------------
plot(x2,y2,'green','LineWidth',2,'Color',Farbe1,...
'DisplayName','name of the first plot')
ax1 = gca;
ax1.XLim = [0 length(x2)]
ax1.YLim = [min(y2) max(y2)]
ax1.YTick = [0:25:300]
ax1.FontSize = 12;
legend('show')
%----------------------------------------------------------------
%-------------------------- plot2: --------------------------
%----------------------------------------------------------------
ax2 = axes('Position',ax1.Position);
plot(x3,y3,'blue','LineWidth',2,'Color',Farbe3,...
'DisplayName','plot2')
ax2.Color = 'none';
ax2.XTick = [];
ax2.XLim = [0 length(x3)];
ax2.YAxisLocation = 'right';
ax2.FontSize = 12;
legend('show')
%----------------------------------------------------------------
%-------------------------- plot3: -------------------------
%----------------------------------------------------------------
ax3 = axes('Position',ax1.Position);
plot(x1,y1,'red','LineWidth',2,'Color',Farbe2,...
'DisplayName','3')
ax3.XTick = [];
ax3.YTick = [];
ax3.Color = 'none';
ax3.XAxisLocation = 'top';
ax3.YAxisLocation = 'right';
ax3.XLim = [0 length(x1)];
ax3.YLim = [min(y1) max(y1)*2];
legend('show')
This results in a very bad looking legend:
I really hope somebody can help me.
Thank very much.
You can get better results by storing handles for each of your plot lines, then passing those to a single legend call:
...
h1 = plot(x2,y2,'green','LineWidth',2,'Color',Farbe1,...
'DisplayName','name of the first plot');
...
h2 = plot(x3,y3,'blue','LineWidth',2,'Color',Farbe3,...
'DisplayName','plot2');
...
h3 = plot(x1,y1,'red','LineWidth',2,'Color',Farbe2,...
'DisplayName','3');
...
hl = legend(ax3, [h1 h2 h3]); % Place legend in top-most axes
And here's the result:
Just use the real timestamps as x values:
fig = figure;
plot(x1/length(y1)*end_of_data, y1, 'LineWidth',2, 'Color',Farbe1, 'DisplayName','First plot')
hold on
plot(x2/length(y2)*end_of_data, y2, 'LineWidth',2, 'Color',Farbe2, 'DisplayName','Second plot')
plot(x3/length(y3)*end_of_data, y3, 'LineWidth',2, 'Color',Farbe3, 'DisplayName','Third plot')
legend

Matlab: Indicate on a plot when data is off the plot?

Is there a way to have a mark on a plot axis for any data points off the current plot in Matlab? It is great if the method works with zoom and pan, but I can live without it.
I have several data sets that I am plotting using subplots. Each plot contains 20 data points, similar to marking where darts landed on a dart board. For most data sets, these plots are within a standard range, ie the size of the dart board, so I am using this standard for the axis range of each plot to make the subplots easily visually comparable. However, a few data sets have an outlier that is outside this standard range (typically way outside). I do not want to change the axis to show the outlier, but I want the user to be aware that there is 1 or more data points off the plot, and preferably in which direction they are off.
I thought a bit about trying to use the axis markers (set(gca, 'Ytick', xLowerLimit: markSpacing: xUpperLimit)) and adding an extra marker in a different color to indicate the location of an outlier, but I couldn't see how to do this without disrupting the regular markers and in a automated way that could allow for multiple outlier marks.
One approach is to see if there is any data that exceed your Y Axis limit and then place some text somewhere to notify the user. Options include text (puts text on axis) or uicontrol (puts text somewhere in figure window).
Here is an example using uicontrol:
% Set up figure window and axes
h.f = figure;
h.ax = axes('Units', 'Normalized', 'Position', [0.13 0.2 0.75 0.75]);
% Plot some sample data and window our axes to replicate the question
x = 1:10;
y = [1:9 20];
xmin = 0; xmax = 10;
ymin = 0; ymax = 10;
plot(h.ax, x, y);
axis(h.ax, [xmin xmax ymin ymax]);
% Generate logical matrix to find if any y data is beyond our axis limit
ymask = y > ymax;
if any(ymask) % Loop triggers if any y value is greater than ymax
str = sprintf('There are %u data points greater than current y axis limit', sum(ymask));
uicontrol('Parent', h.f, ...
'Style', 'text', ...
'Units', 'Normalized', ...
'Position', [0.01 0.01 0.9 0.05], ...
'String', str ...
);
end
Which generates the following (annotations added manually):
This can be extended to other directions with some simple copy + paste and tweaks.
Edit: see bottom for improved method using callbacks.
You can find data points exceeding the axes limits and draw them on the limits, using different symbols:
A = randn(20, 2);
x_range = [-1.5, 1.5];
y_range = [-1.5, 1.5];
figure(1);
clf;
ah = axes;
plot(ah, A(:,1), A(:,2), 'o')
hold on
set(ah, 'XLim', x_range, 'YLim', y_range)
x_lt = A(:,1) < x_range(1);
x_gt = A(:,1) > x_range(2);
y_lt = A(:,2) < y_range(1);
y_gt = A(:,2) > y_range(2);
A_out = A;
A_out(x_lt, 1) = x_range(1);
A_out(x_gt, 1) = x_range(2);
A_out(y_lt, 2) = y_range(1);
A_out(y_gt, 2) = y_range(2);
A_out = A_out(x_lt | x_gt | y_lt | y_gt, :);
plot(ah, A_out(:,1), A_out(:,2), 'rx')
This produces a plot like this:
Edit: add callbacks
If you really want to go fancy, you can add callbacks so that outliers are automatically drawn (and removed) when the figure is zoomed. Adding the same functionality for panning is left as an exercise to the reader ;)
mark_outliers.m:
function mark_outliers(fig, ax)
ah = ax.Axes;
lobj = findobj(ah, 'Type', 'Line');
x_range = ah.XLim;
y_range = ah.YLim;
ah.NextPlot = 'add';
for i_l = 1:numel(lobj)
xd = lobj(i_l).XData;
yd = lobj(i_l).YData;
x_lt = xd < x_range(1);
x_gt = xd > x_range(2);
y_lt = yd < y_range(1);
y_gt = yd > y_range(2);
outliers = x_lt | x_gt | y_lt | y_gt;
if any(outliers)
xd_out = xd;
xd_out(x_lt) = x_range(1);
xd_out(x_gt) = x_range(2);
yd_out = yd;
yd_out(y_lt) = y_range(1);
yd_out(y_gt) = y_range(2);
xd_out = xd_out(outliers);
yd_out = yd_out(outliers);
plot(ah, xd_out, yd_out, 'xr', 'Tag', 'Outliers')
end
end
delete_outliers.m:
function delete_outliers(fig, ah)
ah = ah.Axes;
delete(findobj(ah, 'Tag', 'Outliers'));
example.m:
A = randn(20, 2);
fh = figure(1);
clf;
ah = axes;
plot(ah, A(:,1), A(:,2), 'o')
h = zoom(fh);
h.ActionPreCallback=#delete_outliers;
h.ActionPostCallback=#mark_outliers;

Adding a second x-axis to my graph, which I can scale myself

I am having a problem with Matlab at the moment and I hope you can help me. I have written this code below to display a graph, but now I want to add another x-axis above the image, which I can scale myself. Is there any possibility to add another line like: set(gca, 'XTick2', [bla:bla:bla]); and another label?
EDIT: I have solved a part of the problem, hope you can help me with the rest, too... I have now 2x-axes, but still a few problems.
The labels are in wrong positions, the y-label is inside the scale and I want two different labels for the two x-axes.
Also I would like to remove the scale of the lower x-axis from the upper one.
Code is also the new one:
x1 = [0, 421.441, 842.882, 1264.323, 1685.764, 2107.205, 2528.646, 2950.087, 3371.528, 3792.969, 4214.41, 4635.851, 5057.29];
y1 = [55.659, 55.856, 56.081, 56.279, 56.312, 56.169, 56.038, 55.903, 55.75, 55.604, 55.512, 55.534, 55.661];
y2 = [51.231, 51.735, 52.063, 52.152, 51.632, 51.16, 51.014, 50.911, 50.721, 50.596, 50.597, 50.858, 51.242];
y3 = [50.939, 51.381, 51.644, 51.687, 51.353, 50.944, 50.829, 50.706, 50.538, 50.43, 50.412, 50.614, 50.948];
y4 = [50.023, 50.328, 50.506, 50.535, 50.352, 50.113, 50.032, 49.938, 49.801, 49.705, 49.672, 49.801, 50.03];
plot(x1,y1, 'ks-',x1,y2, 'bx--',x1,y3, 'gd-.',x1,y4, 'c.-'),
ax1 = gca;
ax1.XLim = [0, 5075];
ax1.XTick = 0:1000:5075;
ay1 = gca;
ay1.YLim = [49.5, 56.5];
ay1.YTick = 49.5:0.5:56.5;
ax2 = axes('Position',ax1.Position,'Color','none');
ax2.XLim = [0, 360];
ax2.XTick = 0:30:360;
ax2.XAxisLocation = 'top';
ax2.YTick = [];
grid off
xlabel('Time [s]')
ylabel('Temperature [°C]')
legend('A','B','C','D')
Just use the function text,
text(x,y,'string')
where x and y are the coordinates of you new x-axis and string your blabla.
UPDATE: use this sample code and adjust it to your needs
x1 = 0:0.1:40;
y1 = 4.*cos(x1)./(x1+2);
x2 = 1:0.2:20;
y2 = x2.^2./x2.^3;
figure
line(x1,y1,'Color','r')
ax1 = gca; % current axes
ax1.XColor = 'r';
ax1.YColor = 'r';
ax1_pos = ax1.Position; % position of first axes
ax2 = axes('Position',ax1_pos,...
'XAxisLocation','top',...
'YAxisLocation','right',...
'Color','none');
which gives the plot,

MATLAB Multiple(parallel) box plots in single figure

I'm using the boxplot function in MATLAB. I need to plot boxplots for 6 different datasets for 6 'XTicks' i.e each tick in the x axis should contain 6 corresponding boxes, whiskers, median lines and set of outliers within it's domain. I tried manipulating the 'XTick' property by setting offsets for each variable, but it doesn't apply for boxplot() as it would for a normal plot(). I'm also not able to add legends.
A 3 variable equivalent of my problem would like the following:
Edit:
The following is the code snippet that needs to be modified
TreadmillData = randi([20,200],69,6);
Speeds = {'1.5mph' '2.5mph' '3.5mph' '4.5mph' '5.5mph' '6.5mph'};
DeviceColors = {'r' 'g' 'c' [0.5 0 0.5] 'b' [1 0.5 0]};
Pedometer1 = TreadmillData(1:7:end,:);
Pedometer2 = TreadmillData(2:7:end,:);
Pedometer3 = TreadmillData(3:7:end,:);
Pedometer4 = TreadmillData(4:7:end,:);
Pedometer5 = TreadmillData(5:7:end,:);
Pedometer6 = TreadmillData(6:7:end,:);
GroupedData = {Pedometer1 Pedometer2 Pedometer3 Pedometer4 Pedometer5 Pedometer6};
legendEntries = {'dev1' 'dev2' 'dev3' 'dev4' 'dev5' 'dev6'};
figure;
Xt = 20:20:120;
Xt_Offset = [-15,-10,-5,5,10,15];
for i=1:6
boxplot(GroupedData{i},'Color',DeviceColors{i});
set(gca,'XTick',Xt+Xt_Offset(i));
if i==3
set(gca,'XTickLabel',Speeds);
end
hold on;
end
xlabel('Speed');ylabel('Step Count'); grid on;
legend(legendEntries);
Any help would be appreciated!
I've made some modifications to your code. I've tested this in R2014b.
TreadmillData = randi([20,200],69,6);
Speeds = {'1.5mph' '2.5mph' '3.5mph' '4.5mph' '5.5mph' '6.5mph'};
DeviceColors = {'r' 'g' 'c' [0.5 0 0.5] 'b' [1 0.5 0]};
Pedometer1 = TreadmillData(1:7:end,:);
Pedometer2 = TreadmillData(2:7:end,:);
Pedometer3 = TreadmillData(3:7:end,:);
Pedometer4 = TreadmillData(4:7:end,:);
Pedometer5 = TreadmillData(5:7:end,:);
Pedometer6 = TreadmillData(6:7:end,:);
GroupedData = {Pedometer1 Pedometer2 Pedometer3 Pedometer4 Pedometer5 Pedometer6};
legendEntries = {'dev1' 'dev2' 'dev3' 'dev4' 'dev5' 'dev6'};
N = numel(GroupedData);
delta = linspace(-.3,.3,N); %// define offsets to distinguish plots
width = .2; %// small width to avoid overlap
cmap = hsv(N); %// colormap
legWidth = 1.8; %// make room for legend
figure;
hold on;
for ii=1:N %// better not to shadow i (imaginary unit)
%if ii~=ceil(N/2)
% labels = repmat({''},1,N); %// empty labels
%else
labels = Speeds; %// center plot: use real labels
%end
boxplot(GroupedData{ii},'Color', DeviceColors{ii}, 'boxstyle','filled', ...
'position',(1:numel(labels))+delta(ii), 'widths',width, 'labels',labels)
%// plot filled boxes with specified positions, widths, labels
plot(NaN,1,'color',DeviceColors{ii}); %// dummy plot for legend
end
xlabel('Speed'); ylabel('Step Count'); grid on;
xlim([1+2*delta(1) numel(labels)+legWidth+2*delta(N)]) %// adjust x limits, with room for legend
legend(legendEntries);
Here is a solution for plotting several boxplot. You have to group all the data in a single matrix, each group being separated by a column of Nan. After that, you can simply plot a single regular boxplot with ad-hoc options such as colors and labels.
The following example uses 2 groups of 3, so 7 columns. The 4 first lines of data:
0.6993 0.0207 -0.7485 NaN 0.5836 -0.1763 -1.8468
-0.0494 -1.5411 0.8022 NaN 2.7124 -0.0636 -2.3639
0.9134 0.7106 -0.1375 NaN -0.2200 -0.2528 -0.8350
-0.5655 1.3820 0.6038 NaN -0.7563 -0.9779 0.3789
And the code:
figure('Color', 'w');
c = colormap(lines(3));
A = randn(60,7); % some data
A(:,4) = NaN; % this is the trick for boxplot
C = [c; ones(1,3); c]; % this is the trick for coloring the boxes
% regular plot
boxplot(A, 'colors', C, 'plotstyle', 'compact', ...
'labels', {'','ASIA','','','','USA',''}); % label only two categories
hold on;
for ii = 1:3
plot(NaN,1,'color', c(ii,:), 'LineWidth', 4);
end
title('BOXPLOT');
ylabel('MPG');
xlabel('ORIGIN');
legend({'SUV', 'SEDAN', 'SPORT'});
set(gca, 'XLim', [0 8], 'YLim', [-5 5]);