Related
I'm trying to make a stack plot from a table, to present several variables with the same x-axis. however, I want the x-axis to be logarithmic. I couldn't find any way in stackplot documentation. Does anyone have any suggestions on how I can solve this?
I have tried using subplots instead, however, that way my graphs would not fit all on one page and I would have a lot of white space between the subplots. Therefore, I chose stackplot to make it more nice and less space-consuming.
tbl = readtable('usage.csv');
newYlabels = {'Heating (kWh/year)','Cooling (kWh/year)','Electricity (kWh/year)'};
stackedplot(tbl,[{2:16},{17:27},{28:35}],'XVariable',[1],'DisplayLabels',newYlabels);
Here is the output of the code:
Here is an image of what I'm trying to make, but the x-axis needs to be the real variable (\beta) in logarithmic scale
stackedplot has unfortunately no logarithmic axes option, and since it creates a StackedAxes instead of a normal Axes object, there is no way to changes this.
If the only reason you want to use stackedplot is to have less white-space, you might want to check out tight_subplot on the Matlab FEX. This would allow you to just do: set(ax, 'XScale', 'log').
You can however take the log of your x-data, and add that to the table:
tbl = readtable('outages.csv'); % sample data
tbl = sortrows(tbl, 'OutageTime'); % sort by date
% make x vector; for example just row numbers
x = (1:size(tbl,1)).';
xlog = log10(x);
% add x to table
tbl.Xlog = xlog;
tbl.X = x;
% plot normal x
f = figure(1); clf;
s = stackedplot(tbl, {'Loss'}, 'XVariable', 'X');
xlabel('rows');
% plot log(x)
f = figure(2); clf;
s = stackedplot(tbl, {'Loss'}, 'XVariable', 'Xlog');
xlabel('log(rows)')
Normal:
Log:
I'm trying to digitize this image using MATLAB:
I have the following script:
%// Get data from plot
clear all; close all;
%// Input
fname = 'Fig15a.PNG';
xvec = [1e3:1:1e8];
yvec = [1e-4:1:1e-1];
xt = [1e3 1e4 1e5 1e6 1e7 1e8];
yt = [1e-4 1e-3 1e-2 1e-1];
%// Read and plot the image
im = imread(fname);
figure(1), clf
im = im(end:-1:1,:,:);
image(xvec,yvec,im)
axis xy;
grid on;
%// Set ticks
set(gca,'xtick',xt,'ytick',yt); %// Match tick marks
%// Collect data
[x,y] = ginput; %// Click on points, and then hit ENTER to finish
%// Plot collected data
hold on; plot(x,y,'r-o'); hold off;
%// Then save data as:
save Fig15a.mat x y
The script works fine
Is there a way I can change the x and y axes to a log scale ?
I have tried adding the following code in different places without luck:
%// Set Log scale on x and y axes
set(gca,'XScale','log','YScale','log');
Below's a proof of concept that should get you on the right track. I have replaced things in your original code with what I consider "good practices".
function q36470836
%% // Definitions:
FIG_NUM = 36470836;
%% // Inputs:
fname = 'http://i.stack.imgur.com/2as4t.png';
xt = logspace(3,8,6);
yt = logspace(-4,-1,4);
%% // Init
figure(FIG_NUM); clf
% Read and plot the image
im = imread(fname);
hIMG = imshow(im); axis image;
%// Set ticks
hDigitizer = axes('Color','none',...
'XLim',[xt(1) xt(end)],'YLim',[yt(1) yt(end)],...
'XScale','log','YScale','log',...
'Position',hIMG.Parent.Position .* [1 1 696/785 (609-64+1)/609]);
uistack(hDigitizer,'top'); %// May be required in some cases
grid on; hold on; grid minor;
%// Collect data:
[x,y] = ginput; %// Click on points, and then hit ENTER to finish
%// Plot collected data:
scatter(x,y,'o','MarkerEdgeColor','r');
%// Save data:
save Fig15a.mat x y
Here's an example of what it looks like:
Few notes:
xt, yt may be created in a cleaner fashion using logspace.
It is difficult (possibly impossible) to align the digitization grid with the image correctly, which would inevitably result in errors in your data. Though this can be helped in the following scenarios (for which you will require a vector graphics editor, such as the freeware InkScape):
If, by any chance, you got this image from a PDF file, where it appears as a vector image (you can test this by zooming in as much as you like without the chart becoming pixelated; this seems to be your case from the way the .png looks), you would be better off saving it as a vector image and then you have two options:
Exporting the image to a bitmap with a greatly increased resolution and then attempting the digitization procedure again.
Saving the vector image as .svg then opening the file using your favorite text editor and getting the exact coordinates of the points.
If the source image is a bitmap (as opposed to vector graphic), you can "trace the bitmap", thus converting it to vectoric, then #GOTO step 1.
This solution doesn't (currently) support resizing of the figure.
The magic numbers appearing in the Position setting are scaling factors explained in the image below (and also size(im) is [609 785 3]). These can technically be found using "primitive image processing" but in this case I just hard-coded them explicitly.
You can plot in double logarithmic scale with
loglog(x,y);
help loglog or the documentation give additional information.
For a single logarithmic scale use
semilogx(x,y);
semilogy(x,y);
I haven't used MATLAB in a while and I am stuck on a small detail. I would really appreciate it if someone could help me out!
So I am trying to plot a transfer function using a specific function called freqs but I can't figure out how I can label specific points on the graph.
b = [0 0 10.0455]; % Numerator coefficients
a = [(1/139344) (1/183.75) 1]; % Denominator coefficients
w = logspace(-3,5); % Frequency vector
freqs(b,a,w)
grid on
I want to mark values at points x=600 Hz and 7500 Hz with a marker or to be more specific, points (600,20) and (7500,-71), both of which should lie on the curve. For some reason, freqs doesn't let me do that.
freqs is very limited when you want to rely on it plotting the frequency response for you. Basically, you have no control on how to modify the graph on top of what MATLAB generates for you.
Instead, generate the output response in a vector yourself, then plot the magnitude and phase of the output yourself so that you have full control. If you specify an output when calling freqs, you will get the response of the system.
With this, you can find the magnitude of the output by abs and the phase by angle. BTW, (600,20) and (7500,-71) make absolutely no sense unless you're talking about magnitude in dB.... which I will assume is the case for the moment.
As such, we can reproduce the plot that freqs gives by the following. The key is to use semilogx to get a semi-logarithmic graph on the x-axis. On top of this, declare those points that you want to mark on the magnitude, so (600,20) and (7500,-71):
%// Your code:
b = [0 0 10.0455]; % Numerator coefficients
a = [(1/139344) (1/183.75) 1]; % Denominator coefficients
w = logspace(-3,5); % Frequency vector
%// New code
h = freqs(b,a,w); %// Output of freqs
mag = 20*log10(abs(h)); %// Magnitude in dB
pha = (180/pi)*angle(h); %// Phase in degrees
%// Declare points
wpt = [600, 7500];
mpt = [20, -71];
%// Plot the magnitude as well as markers
figure;
subplot(2,1,1);
semilogx(w, mag, wpt, mpt, 'r.');
xlabel('Frequency');
ylabel('Magnitude (dB)');
grid;
%// Plot phase
subplot(2,1,2);
semilogx(w, pha);
xlabel('Frequency');
ylabel('Phase (Degrees)');
grid;
We get this:
If you check what freqs generates for you, you'll see that we get the same thing, but the magnitude is in gain (V/V) instead of dB. If you want it in V/V, then just plot the magnitude without the 20*log10() call. Using your data, the markers I plotted are not on the graph (wpt and mpt), so adjust the points to whatever you see fit.
There are a couple issues before we attempt to answer your question. First, there is no data-point at 600Hz or 7500Hz. These frequencies fall between data-points when graphed using the freqs command. See the image below, with datatips added interactively. I copy-pasted your code to generate this data.
Second, it does not appear that either (600,20) or (7500,-71) lie on the curves, at least with the data as you entered above.
One solution is to use plot a marker on the desired position, and use a "text" object to add a string describing the point. I put together a script using your data, to generate this figure:
The code is as follows:
b = [0 0 10.0455];
a = [(1/139344) (1/183.75) 1];
w = logspace(-3,5);
freqs(b,a,w)
grid on
figureHandle = gcf;
figureChildren = get ( figureHandle , 'children' ); % The children this returns may vary.
axes1Handle = figureChildren(1);
axes2Handle = figureChildren(2);
axes1Children = get(axes1Handle,'children'); % This should be a "line" object.
axes2Children = get(axes2Handle,'children'); % This should be a "line" object.
axes1XData = get(axes1Children,'xdata');
axes1YData = get(axes1Children,'ydata');
axes2XData = get(axes2Children,'xdata');
axes2YData = get(axes2Children,'ydata');
hold(axes1Handle,'on');
plot(axes1Handle,axes1XData(40),axes1YData(40),'m*');
pointString1 = ['(',num2str(axes1XData(40)),',',num2str(axes1YData(40)),')'];
handleText1 = text(axes1XData(40),axes1YData(40),pointString1,'parent',axes1Handle);
hold(axes2Handle,'on');
plot(axes2Handle,axes2XData(40),axes2YData(40),'m*');
pointString2 = ['(',num2str(axes2XData(40)),',',num2str(axes2YData(40)),')'];
handleText2 = text(axes2XData(40),axes2YData(40),pointString2,'parent',axes2Handle);
I'm trying to find a way to nicely plot my measurement data of digital signals.
So I have my data available as csv and mat file, exported from an Agilent Oscilloscope. The reason I'm not just taking a screen shot of the Oscilloscope screen is that I need to be more flexible (make several plots with one set of data, only showing some of the lines). Also I need to be able to change the plot in a month or two so my only option is creating a plot from the data with a computer.
What I'm trying to achieve is something similar to this picture:
The only thing missing on that pic is a yaxis with 0 and 1 lines.
My first try was to make a similar plot with Matlab. Here's what I got:
What's definitely missing is that the signal names are right next to the actual line and also 0 and 1 ticks on the y-axis.
I'm not even sure if Matlab is the right tool for this and I hope you guys can give me some hints/a solution on how to make my plots :-)
Here's my Matlab code:
clear;
close all;
clc;
MD.RAW = load('Daten/UVLOT1 debounced 0.mat'); % get MeasurementData
MD.N(1) = {'INIT\_DONE'};
MD.N(2) = {'CONF\_DONE'};
MD.N(3) = {'NSDN'};
MD.N(4) = {'NRST'};
MD.N(5) = {'1V2GD'};
MD.N(6) = {'2V5GD'};
MD.N(7) = {'3V3GD'};
MD.N(8) = {'5VGD'};
MD.N(9) = {'NERR'};
MD.N(10) = {'PGD'};
MD.N(11) = {'FGD'};
MD.N(12) = {'IGAGD'};
MD.N(13) = {'GT1'};
MD.N(14) = {'NERRA'};
MD.N(15) = {'GT1D'};
MD.N(16) = {'GB1D'};
% concat vectors into one matrix
MD.D = [MD.RAW.Trace_D0, MD.RAW.Trace_D1(:,2), MD.RAW.Trace_D2(:,2), MD.RAW.Trace_D3(:,2), ...
MD.RAW.Trace_D4(:,2), MD.RAW.Trace_D5(:,2), MD.RAW.Trace_D6(:,2), MD.RAW.Trace_D7(:,2), ...
MD.RAW.Trace_D8(:,2), MD.RAW.Trace_D9(:,2), MD.RAW.Trace_D10(:,2), MD.RAW.Trace_D11(:,2), ...
MD.RAW.Trace_D12(:,2), MD.RAW.Trace_D13(:,2), MD.RAW.Trace_D14(:,2), MD.RAW.Trace_D15(:,2)];
cm = hsv(size(MD.D,2)); % make colormap for plot
figure;
hold on;
% change timebase to ns
MD.D(:,1) = MD.D(:,1) * 1e9;
% plot lines
for i=2:1:size(MD.D,2)
plot(MD.D(:,1), MD.D(:,i)+(i-2)*1.5, 'color', cm(i-1,:));
end
hold off;
legend(MD.N, 'Location', 'EastOutside');
xlabel('Zeit [ns]'); % x axis label
title('Messwerte'); % title
set(gca, 'ytick', []); % hide y axis
Thank you guys for your help!
Dan
EDIT:
Here's a pic what I basically want. I added the signal names via text now the only thing that's missing are the 0, 1 ticks. They are correct for the init done signal. Now I just need them repeated instead of the other numbers on the y axis (sorry, kinda hard to explain :-)
So as written in my comment to the question. For appending Names to each signal I would recommend searching the documentation of how to append text to graph. There you get many different ways how to do it. You can change the position (above, below) and the exact point of data. As an example you could use:
text(x_data, y_data, Var_Name,'VerticalAlignment','top');
Here (x_data, y_data) is the data point where you want to append the text and Var_Name is the name you want to append.
For the second question of how to get a y-data which contains 0 and 1 values for each signal. I would do it by creating your signal the way, that your first signal has values of 0 and 1. The next signal is drawn about 2 higher. Thus it changes from 2 to 3 and so on. That way when you turn on y-axis (grid on) you get values at each integer (obviously you can change that to other values if you prefer less distance between 2 signals). Then you can relabel the y-axis using the documentation of axes (check the last part, because the documentation is quite long) and the set() function:
set(gca, 'YTick',0:1:last_entry, 'YTickLabel',new_y_label(0:1:last_entry))
Here last_entry is 2*No_Signals-1 and new_y_label is an array which is constructed of 0,1,0,1,0,....
For viewing y axis, you can turn the grid('on') option. However, you cannot chage the way the legends appear unless you resize it in the matlab figure. If you really want you can insert separate textboxes below each of the signal plots by using the insert ->Textbox option and then change the property (linestyle) of the textbox to none to get the exact same plot as above.
This is the end result and all my code, in case anybody else wants to use the good old ctrl-v ;-)
Code:
clear;
close all;
clc;
MD.RAW = load('Daten/UVLOT1 debounced 0.mat'); % get MeasurementData
MD.N(1) = {'INIT\_DONE'};
MD.N(2) = {'CONF\_DONE'};
MD.N(3) = {'NSDN'};
MD.N(4) = {'NRST'};
MD.N(5) = {'1V2GD'};
MD.N(6) = {'2V5GD'};
MD.N(7) = {'3V3GD'};
MD.N(8) = {'5VGD'};
MD.N(9) = {'NERR'};
MD.N(10) = {'PGD'};
MD.N(11) = {'FGD'};
MD.N(12) = {'IGAGD'};
MD.N(13) = {'GT1'};
MD.N(14) = {'NERRA'};
MD.N(15) = {'GT1D'};
MD.N(16) = {'GB1D'};
% concat vectors into one matrix
MD.D = [MD.RAW.Trace_D0, MD.RAW.Trace_D1(:,2), MD.RAW.Trace_D2(:,2), MD.RAW.Trace_D3(:,2), ...
MD.RAW.Trace_D4(:,2), MD.RAW.Trace_D5(:,2), MD.RAW.Trace_D6(:,2), MD.RAW.Trace_D7(:,2), ...
MD.RAW.Trace_D8(:,2), MD.RAW.Trace_D9(:,2), MD.RAW.Trace_D10(:,2), MD.RAW.Trace_D11(:,2), ...
MD.RAW.Trace_D12(:,2), MD.RAW.Trace_D13(:,2), MD.RAW.Trace_D14(:,2), MD.RAW.Trace_D15(:,2)];
cm = hsv(size(MD.D,2)); % make colormap for plot
figure;
hold on;
% change timebase to ns
MD.D(:,1) = MD.D(:,1) * 1e9;
% plot lines
for i=2:1:size(MD.D,2)
plot(MD.D(:,1), MD.D(:,i)+(i-2)*2, 'color', cm(i-1,:));
text(MD.D(2,1), (i-2)*2+.5, MD.N(i-1));
end
hold off;
%legend(MD.N, 'Location', 'EastOutside');
xlabel('Zeit [ns]'); % x axis label
title('Messwerte'); % title
% make y axis and grid the way I want it
set(gca, 'ytick', 0:size(MD.D,2)*2-3);
grid off;
set(gca,'ygrid','on');
set(gca, 'YTickLabel', {'0'; '1'});
ylim([-1,(size(MD.D,2)-1)*2]);
I am using the plot command in Octave to plot y versus x values. Is there a way such that the graph also shows the (x,y) value of every coordinate plotted on the graph itself?
I've tried using the help command but I couldn't find any such option.
Also, if it isn't possible, is there a way to do this using any other plotting function?
Have you tried displaying a text box at each coordinate?
Assuming x and y are co-ordinates already stored in MATLAB, you could do something like this:
plot(x, y, 'b.');
for i = 1 : numel(x) %// x and y are the same lengths
text(x(i), y(i), ['(' num2str(x(i)) ',' num2str(y(i)) ')']);
end
The above code will take each point in your graph and place a text box (with no borders) in the format of (x,y) where x and y are the coordinates for all of the points.
Note: You may have to play around with the position of the text boxes, because the above code will place each text box right on top of each pair of co-ordinates. You can play around by adding/subtracting appropriate constants in the first and second parameters of the text function. (i.e. text(x(i) + 1, y(i) - 1, .......); However, if you want something quick and for illustrative purposes, then the above code will be just fine.
Here's a method I've used to do something similar. Generating the strings for the labels is the most awkward part, really (I only had a single number per label, which is a lot simpler)
x = rand(1, 10);
y = rand(1, 10);
scatter(x, y);
% create a text object for each point
t = text(x, y, '');
% generate a cell array of labels - x and y must be row vectors in this case
c = strsplit(sprintf('%.2g,%.2g\n',[x;y]), '\n');
c(end) = []; % the final \n gives us an extra empty cell, remove it
c = c'; % transpose to match the dimensions of t
% assign each label to each text object
set(t, {'String'}, c);
You may want to play around with various properties like 'HorizontalAlignment', 'VerticalAlignment' and 'Margin' to tweak the label positions to your liking.
After a little more thought, here's an alternative, more robust way to generate a suitable cell array of coordinate labels:
c = num2cell([x(:) y(:)], 2);
c = cellfun(#(x) sprintf('%.2g,%.2g',x), c, 'UniformOutput', false);
There is a very useful package labelpoints
in MathWorks File Exchange that does exactly that.
It has a lot of convenient features.