Increase precision of MATLAB variable/data point from a plot - matlab

I'm plotting a control system and I need to prove that the steady state error from MATLAB coincides with my calculations.
My calculations have given me 0.000833333, and on the plot, the data-tip markers have very little precision (no decimal points at the location I selected).
However I can right-click the marker and select Export cursor data to workspace...
This gives me the position location of X=98.0037, Y=98.0028
That gives me an error of 0.0009, so it's not quite the 0.00083333 from calculations. I know this is correct however, I just would like to know how to increase the precision of the variables/data points from plot past 4 decimal places if possible.

There are many different routes to get more precisions from these data tip cursors:
1) Export manually:
As you mentioned in your question, right click on the datatip then select Export cursor data to workspace.... Let's assume you export that in the default variable cursor_info, you then get a structure with 3 fields:
cursor_info =
Target: 492.0040
Position: [7.3593e+05 10.6353]
DataIndex: 7
Target is the handle of the line object to which the cursor was snapped
Position is a 1x2 vector giving the x and y coordinates of the selected data points (it will have a third value if the z coordinates are defined)
DataIndex is the index of the selected data point coordinates in the array that was used to plot the line. So you could also ask for :
>> x(7) %// or "x(cursor_info.DataIndex)" would be the same
ans =
7.3593e+05
>> y(7)
ans =
11.3200
Now by default the display of values in the console is limited to a few digits, but the number in the variable has more precision. 2 ways to display more precision:
First method is to type format long in the console. After that all your numeric output in the console will be with 15 digits:
>> cursor_info.Position
ans =
1.0e+05 *
7.359289702215524 0.000106353183026
Second method is to force the precision you desire by using a format specifier and sprintf or fprintf:
>> fprintf('x=%15.15d y=%g \n',cursor_info.Position)
x=7.359289702215524e+05 y=10.6353
2) Export programatically
Alternatively to the manual export of the cursor_info data, you can call that with code:
dcm = datacursormode(gcf) ;
cursor_info = dcm.getCursorInfo ;
You then get the same cursor_info variable than if you exported it manually. Displaying the values can be done the same way than above.
3) Direct display (at the wanted precision).
You can also achieve a full customization of what the data tip will display, not only the position but also some calculated or converted values. Right click on the data tip and select Edit text update function. This will open an editor window with the current code for the data tip, which basically query the data tip position and create a cell array of text to be displayed. Modify this function to your needs then save it somewhere you can retrieve it.
For an example with the data I was using, I display the x and y coordinates with different precision, then a calculation based on the 2 values, and I also convert the x coordinates to the date representation.
function output_txt = myModifiedDatatip(obj,event_obj)
pos = get(event_obj,'Position');
output_txt = {['X: ',num2str(pos(1),12)],... %// default code, only the output precision is changed
['Y: ',num2str(pos(2),8)]};
%// add all the informations you want to calculate and display directly here:
output_txt{end+1} = ['error (y-x): ',sprintf('%16f', pos(2)-pos(1))]; %// difference between x and x
output_txt{end+1} = ['Date: ', datestr(pos(1))]; %// display the date/time
This example will display a data tip like so:
After that, on any subsequent plot or figure, you can re-apply this data tip format by right clicking on a data tip, select text update function, then point to the data tip function you saved earlier.

Related

Accessing legend entries exceeds matrix dimensions

I am trying to plot some data with Matlab R2015a and as the data sets are changing for different plots I want to create the legend (semi-)automatically. I do this with a list of strings (called list) and then
leg = legend(list);
legtxt=findobj(leg,'type','text');
set(legtxt(1),'color','r');
set(legtxt(2),'color','b');
a.s.o. according to the entries.
However, no matter what index I give in the 'set(legtxt(i))' part, Matlab always tells me 'Index exceeds matrix dimensions.' Same, if I create the the legend manually by not using 'legend(list)' but explicitly typing the legend entries. Does anyone know why this happens or how to solve it? Thanks!
You need to use additional outputs from the call to legend. Specifically, check out the 2nd output called icons in the docs.
As for the error, the call to findobj(...) yields an empty vector, so matrix dimensions are indeed exceeded. Indeed (from the docs):
Starting in R2014b, the legend function returns a legend object. In
previous releases it returns an axes object.
So maybe that's why you can't use findobj to fetch legend text...
Anyhow here is how to solve your problem. In this example I create 3 plots and change the color of the text of the 1st and 2nd entry inside the legend:
x = 1:10;
y1 = sin(x);
y2 = cos(x);
y3 = x;
plot(x,y1,'y*',x,y2,'g--',x,y3,'k')
list = {'y1';'y2';'y3'};
%// You want to play with icons and possibly plots.
[leg,icons,plots,str] = legend(list)
set(icons(1),'color','r','FontSize',12)
set(icons(2),'color','b','FontSize',12)
Output:
Of course you can use the plots output to change any property you want of the plots to make them fit with their legend entry.

Select input pixel of image by mouseclick in Matlab

I am trying to implement a tracking program using the Mean-Shift algorithm in Matlab. The idea is that, given the first frame of one video, the user can click on top of any object he wants and the program will track it all through the video sequence. I have already implemented and working the tracking part, but I am having problems giving the user the possibility to click on top of the image to select the initial pixel for the tracking algorithm.
I have thought about input function, but I don't know how to make it work. How can I display an image and click on top of a pixel and get its coordinates [x,y] to initialize the program?
You could use ginput (Graphical input from mouse or cursor)
Read any image and show them using imread and imshow
h = imread('hestain.png'); % any input image
imshow(h);
Get the coordinates using ginput, where the input argument corresponds to the number of user clicks recorded.
[x,y] = ginput(1); % getting coordinates from user
To obtain the pixel value, we need to pass the coordinates as indices of the image. To do this, the output arguments from the ginput which are double by default, must be converted to unsigned integers.
Also, x and y represents horizontal and vertical by default. But matlab syntax takes first dimension as rows (number of horizontal lines calculated vertically). Hence y value is passed as first dimension. Similarly x value as second dimension.
pixelValue = h(uint8(y),uint8(x)); % using coordinates as indices

horizontally shift starting point for stem() in MatLab

I have a sequence with data and an offset. I'm asked to plot a stem() graph of the data, starting at the offset. I have figured out the data part (the easy part) and how to change the window to include the offset, but when I plot the graph, it shows the offset with a value of zero and zeros until 1 where the sequence.data will start and plot points.
methods
function s = sequence(data, offset)
s.data = data;
s.offset = offset;
end
function stem(x)
% STEM Display a Matlab sequence, x, using a stem plot.
stem(x.offset,x.data);
axis([x.offset x.offset+length(x.data) 'auto' 'auto']);
end
I need to figure out how to "move" my x.data to my x.offset and start stem plotting there.
I don't understand why you simply can't add an x offset while keeping the y data the same?
For instance, given your example in your comments above:
x = 0:4;
y = 1:5;
This is what the original graph looks like, as well as shifting the graph to the left by 3 (-3):
stem(x,y,'b');
hold on;
stem(x-3,y,'r');
This is what I get:
The blue data is the original, while the red is the shifted instance to the left by 3. As you can see, the y data is the same, but the x points move to the left by 3. What your code is actually doing is that it does not shift the actual data. You are only changing the display range of your stem plot. As such, you should really be doing this:
methods
function s = sequence(data, offset)
s.data = data;
s.offset = offset;
end
function stem(x)
% STEM Display a Matlab sequence, x, using a stem plot.
%// First define sequence from [0,N-1]
vals = 0:numel(x.data)-1;
%// Now use the above and manually shift the x coordinate
stem(vals+x.offset,x.data);
end
I'm going to assume that your data on the x-axis starts counting at 0, and so we will declare a sequence from 0 up to N-1 where N is the total number of elements that you have. Once we declare this sequence, when it's time to draw the stem plot, we simply add an offset to this sequence and use this as the x data. The y data should stay the same.
However, I would argue that creating a custom class for implementing this addition to stem is less readable than what I originally did above. If this is a requirement for whatever you're developing, then certainly go ahead and do it this way, but I don't really think it's necessary.

How to make a plot of a circle with dashed coloured border in MATLAB?

So i have this code to obtain radial gravity on Earth in function of the latitude:
G=6.6e-11;
M=5.976e24;
N=1000;
r=6371000;
w=2*pi/(24*3600);
for i=1:1:360
Theta=i*pi/180;
x(i)=i;
Vi(i)=-G*M/(r*r);
Phii(i)=r*w*w*sin(Theta)*sin(Theta);
gr(i)=Vi(i)+Phii(i);
end
plot(x,gr)
And it runs well. I want to make a graph of a circle made of a border of points (representing angle (i)) that change colour according to the value of gr(I want to set ranges of values of gr so that if the value obtained falls in a specific category, the point will have a specific colour).
I'm really new to MATLAB. Is there any possible way to make this?
Thanks in advance.
Here is the basic algorithm that I would do:
Determine how many colours you want to represent in your plot.
Create a colour map that has this many points for what you want to compute.
Determine a linearly increasing vector that varies from the minimum value of gr to the maximum value of gr with as many points as you have determined in Step #2
For each point in gr:
a. Determine which point yields the closest distance of this point to the vector in Step #3
b. Use this to index which colour you want.
c. Convert your angle into Cartesian co-ordinates, then plot this point with the colour found in Step 4b.
Let's tackle each point in detail.
Step #1 - Determine how many colours you want
This is pretty simple. Just determine how many colours you want. For now, let's assume that you want 20 colours, so:
num_colours = 20;
Step #2 - Create a colour map
What we can do is create a 20 x 3 matrix where each row determines a RGB tuple that denotes the amount of red, green and blue that each colour will occupy. MATLAB has built-in colour maps that will help you facilitate this. Here are all of the available colour maps that MATLAB has:
Each colour map has a special variable where you can provide it an integer number, and it'll return this 2D matrix of as many rows as the number you have provided. Each row gives you an RGB triplet which denotes the proportion of red, green and blue respectively. This matrix varies from the beginning of the colour map (top row) to the end (bottom row). All you have to do is use any name seen in the figure I've shown you above to create a colour map of that type. For example, if you wanted to get a bones colour map of 15 points, simply do:
colour_map = bones(15);
If you wanted to get a jet colour map of 25 points, simply do:
colour_map = jet(25);
.... you get the idea right? I like hsv so let's use the HSV colour map. You can use any colour map you want, but let's just stick with HSV for the sake of this example.
As such:
colour_map = hsv(num_colours);
Step #3 - Get that linearly increasing vector
You want certain colours to map into certain ranges, which is why this step is important. Given a value in gr, we want to figure out which colour we want to choose, and all you have to do is determine which value in gr is the closest to a value in this vector in Step #3. Therefore, you can use linspace to do this for you:
bin_vector = linspace(min(gr), max(gr), num_colours);
This will create a num_colours 1D array where the beginning of this array starts at the minimum value of gr and varies up to the maximum value of gr and each value is equally spaced such that we generate a num_colours array.
Step #4 - Bring it all home
Now, for each point in gr, we need to figure out which point is the closest to that vector in Step #3, we then use this to figure out the colour we want, then we need to convert our angle into Cartesian co-ordinates, then plot this point.
For illustration purposes, I'm going to assume your radius is 1. You can figure out how to get the x and y co-ordinates by simply doing cos(theta) and sin(theta), where theta is the angle you are examining. Since your gr array has 360 slots, I'm going to assume a resolution of 1 degree per slot. Therefore, you can easily do this in a for loop. Make sure you use hold on because we are going to call plot multiple times, and we don't want to overwrite the plot each time we call plot. You want all of the points to stay in the plot.
Without further ado:
figure; %// Create blank figure
hold on; %// Remember all points
%// For each point in our array...
for idx = 1 : 360
%// Find the closest slot between gr and our vector in Step #3
[~,min_idx] = min(abs(gr(idx) - bin_vector));
%// Grab this colour
clr = colour_map(min_idx,:);
%// Plot the point with this colour
plot(cosd(idx), sind(idx), '.', 'Color', clr, 'MarkerSize', 10);
end
Take notice that cosd and sind take in degrees as the input argument while cos and sin take in radians. Also, take note that I also changed the size of the point so that it's bigger. With the above logic, and your array in gr, this is what I get:
If you want the radius to get larger, all you have to do is multiply each cosd and sind term with your radius. Therefore, you can do something like this:
radius = 2;
for idx = 1 : 360
... %// Insert colour code here
...
...
%// Now plot
plot(radius*cosd(idx), radius*sind(idx), '.', 'Color', clr, 'MarkerSize', 10);
end
Just leave the code the same, but for the plot command, just multiply each x and y value by the radius.
Minor note in efficiency
The way you're calculating your gr array is using an inefficient for loop. There are some situations (like mine above) where you need to use a for loop, but for simple computations there is no need. It's better if you vectorize its creation. Therefore, you can get rid of the for loop to calculate your gr array like so:
x = 1 : 360;
Theta = x*pi/180;
Phii = r*w*w*sin(Theta).*sin(Theta);
Vi = -G*M/(r*r);
gr = Vi + Phii;
x is simply a vector going from 1 to 360, and that's done in the first line. Also, Vi is just an array which contains a single value and if you know how operations work between a scalar and an array, you can just do an addition with this single value and it'll add every value in your array by this much. As such, there's no need to create an array for Vi. Also, take a look at how I calculated Phii. I'm using element-by-element operations as Theta is now an array. You want to create an array Phii that takes corresponding values of Theta, and applies that formula to each value in Theta to produce Phii.
Hope this helps. Good luck!

Matlab - Get (x,y) value of a plot by clicking on it

Matlab has the ginput for getting the (x,y) value of where you have clicked on the graph. However, say that I have plotted a graph with plot(t, sin(2*pi*t)). I want to be able to have the cursor move on the actual plot, and by clicking it, I get the (x,y) coordinate of my plot (in this case, the time and the value of sine).
Is this possible?
When the MATLAB figure is open, you can use the data cursor tool to help you traverse through the plot.
Here's a quick example:
t = 0 : 0.01 : 10;
plot(t, sin(t));
Here's what your plot looks like, and I have highlighted the option of where you can click for the data cursor tool.
Once you click on the data cursor option, click anywhere on the plot and it'll give you a data cursor tip. At that point you selected, it shows the x and y co-ordinate. In your case, x denotes time and y denotes amplitude.
Once you have a data cursor tip there, click on it (the black square) and drag it along the curve. You will see that the data cursor tip updates and moves along the curve. Judging from your question, I believe this is what you're looking for.
Take note that the data cursor tip only works for the points that you have plotted. It does not interpolate in between the points. As such, if you want finer resolution, choose a smaller step size between the beginning and end points.
Also, if you want to know the exact index of where your point is, plot just the amplitude values themselves:
plot(sin(t));
The x axis will plot the index number of each point instead of the time values. You can then use the data cursor tip to locate which index you want, then do that indexing that you have specified in your comment above. In other words:
This is telling us that at index 304, it gives us a y value of 0.1114. Then you could do t(304) to find the actual time value where the amplitude was at 0.1114.
h = plot(t, sin(2*pi*t)); %// get a handle to the graph
[x, ~] = ginput(1); %// y from ginput not wanted; it will be read from the graph
y = interp1(get(h,'XData'), get(h,'YData'), x); %// interpolate the graph for y