How to programatically update histogram contents and datatip location? (MATLAB hg2) - matlab

I am trying to make an animation where several datasets are being cycled through in a histogram plot, and a datatip follows the highest bar in every frame, as demonstrated below:
                          
Here's a code which achieves the desired result using a bar graph:
%% // Initialization
close all force; clear variables; clc;
%% // Generate some data:
indMax = 20; data = randi(indMax,[5,45]);
%% // Generate the 1st values to plot:
edges = 0.5:1:indMax+0.5;
counts = histcounts(data(1,:),edges);
[~,maxInd] = max(counts);
%% // Create the plot and the datatip:
figure(100); hBar = bar(1:indMax,counts);
hDT = makedatatip(hBar,maxInd); hDT = handle(hDT);
grid on; hold on; grid minor; xlim([0,indMax+1]); ylim([0,10]);
%% // Update the figure and the datatip:
for indFrame = 2:size(data,1)
counts = histcounts(data(indFrame,:),edges);
[~,maxInd] = max(counts);
hBar.YData = counts; %// Update bar heights
hDT.Cursor.DataIndex = maxInd; %// Update datatip location
%// Alternatively to the above line: hDT.Position = [newX newY newZ];
java.lang.Thread.sleep(1000);
drawnow;
end
Note that the datatip is created using a modified version of the makedatatip submission from FEX, as per the comment on the submission page (this is true for the 27/06/2012 version of makedatatip):
a couple of changes need to be made to the code:
***********CHANGE 1*********
line 122 needs to be: pos = [X(index(n)) Y(index(n)) 0];
***********CHANGE 2*********
lines 135-141 should be commented OUT
And also Change 3: line 84 to Z = [];
Since makedatatip attempts to acces the 'XData' and 'YData' properties of the input handle, which are absent in histogram plots, it refuses to work. So my question is:
How can datatips be created and updated programmatically in histogram plots (using matlab-hg2), along with the histogram itself?

Turns out the solution is quite straight-forward, at least when only a single datatip is needed. Here are the required steps:
Replace the bar plot with a histogram:
hHist = histogram(data(1,:),edges);
Create the datatip "manually" instead of using makedatatip:
hDataCursorMgr = datacursormode(ancestor(hHist,'figure'));
hDT = createDatatip(hDataCursorMgr,hHist);
Update the position as needed:
hDT.Cursor.DataIndex = maxInd;
To update the histogram's bar heights, it is not possible to update the 'Values' property directly (since it's read-only), so one must update the 'Data' property (and let MATLAB recompute the bar heights on its own):
hHist.Data = data(indFrame,:);
And everything put together:
%% // Initialization
close all force; clear variables; clc;
%% // Generate some data:
indMax = 20; data = randi(indMax,[5,45]);
%% // Generate the 1st values to plot:
edges = 0.5:1:indMax+0.5;
counts = histcounts(data(1,:),edges);
[~,maxInd] = max(counts);
%% // Create the plot and the datatip:
figure(100); hHist = histogram(data(1,:),edges);
hDataCursorMgr = datacursormode(ancestor(hHist,'figure'));
hDT = createDatatip(hDataCursorMgr,hHist); hDT.Cursor.DataIndex = maxInd;
grid on; hold on; grid minor; xlim([0,indMax+1]); ylim([0,10]);
%% // Update the plot and the datatip:
for indFrame = 2:size(data,1)
[~,maxInd] = max(histcounts(data(indFrame,:),edges));
hHist.Data = data(indFrame,:);
hDT.Cursor.DataIndex = maxInd;
java.lang.Thread.sleep(1000);
drawnow;
end
Which results in:
                              
Some notes \ observations:
Datatips can only be added to supported data types, which currently only consist of double values (i.e. plotting something other than double doesn't let you add datatips to it, apparently). This is true for MATLAB 2015a. See another discussion about it here.
If datatips should contain some LaTeX-formatted strings, this Q&A describes what needs to be done.
The gif animations I used were created using this.
To center the animations in the posts, I used a combination of "alt+0160" and "alt+255".

Related

How to plot a bar graph with different colors and groups in matlab

I want to plot a bar graph which summarizes algorithms performance. It has three main parameters
Publication year (the x axis)
Data type (bar color)
Algorithm score ( bar height)
Here is an example data:
dtypes = {'type1','type2','type3'}; %All of the possible data types
methods_name = {'method1','method2','method3'};
methods_accuracy = [89.2, 95.54, 85];
methods_year = [2016, 2017, 2016] ;
methods_dtype = {'type1', 'type2', 'type2'};
Here I wish to get 3 bars, 2 of which in the year 2016 with different colors and one in 2017 with a color matching to one from 2016.
For some reason, I can't seem to do what I want using the bar function. It seems simple but I think I am missing something regarding how this function works.
using
bar(methods_year, methods_accuracy)
gives an error :
XData cannot contain duplicate values.
Drawing multiple bars per category seems to be commonly done by using a matrix. Based on your comment this doesn't seem possible. It also seems that the bar() function cannot accept a vector of colors. The following code should be more general and allow you to specify the bar colors based on your 'types'.
I have modified code available at on the MATLAB Answers forum (https://ch.mathworks.com/matlabcentral/answers/310039-how-to-change-the-color-of-individual-bars-in-a-bar-chart) to account for your type of data and processing the data into the correct format.
% code modified from https://ch.mathworks.com/matlabcentral/answers/310039-how-to-change-the-color-of-individual-bars-in-a-bar-chart
% Plots a bar chart and gives a different color to each bar.
% Also plots the value of the bar above the bar.
clc; % Clear the command window.
close all; % Close all figures (except those of imtool.)
clear; % Erase all existing variables.
workspace; % Make sure the workspace panel is showing.
fontSize = 10;
format compact
%%%%%%%%%%%%%%%%%%%%% your data here %%%%%%%%%%%%%%%%%%%%%%%%
dtypes = {'type1','type2','type3'}; %All of the possible data types
methods_name = {'method1','method2','method3'};
methods_accuracy = [89.2, 95.54, 85];
methods_year = [2016, 2017, 2016] ;
methods_dtype = {'type1', 'type2', 'type2'};
% sorting all vectors into values
[sort_yrs,inds] = sort(methods_year);
sort_methods = methods_name(inds);
sort_dtype = methods_dtype(inds);
sort_accuracy = methods_accuracy(inds);
colors = zeros([length(sort_methods),3]);
colors_types = [[1,0,0];[0,1,0];[0,0,1]];
for i = 1:1:length(dtypes)
ind1 = strfind(sort_dtype, dtypes{i});
ind2 = find(not(cellfun('isempty', ind1)));
for j = 1:1:length(ind2)
colors(ind2(j),:) = colors_types(i,:);
end
end
%%%%%%%%%%% end of processing data %%%%%%%%%%%%%%
% plotting all bars in a loop
for b = 1 : length(sort_accuracy)
% plot a single bar
handlebar(b) = bar(b, sort_accuracy(b), 'BarWidth', 0.9);
% assigning color to bar
set(handlebar(b), 'FaceColor', colors(b,:));
hold on;
end
% title and axis labels, if desired
title('A title Here', 'FontSize', fontSize);
xlabel('x', 'FontSize', fontSize);
ylabel('y', 'FontSize', fontSize);
% finding locations for tick labels for years, if desired
temp = histc(sort_yrs,unique(sort_yrs));
% locations to use if at the middle of the year
xTickLocs = cumsum(temp) - 0.5*temp;
% locations to use if at the beginning of the year
%xTickLocs = cumsum(temp) - 0.5*temp(1);
xticks(xTickLocs);
xticklabels(unique(sort_yrs));
% addind labels
set(gca, 'XTickLabels', xticklabels);

How can I change 'PlotStyle' property of a given boxplot figure?

Given a .fig file of a Matlab boxplot (i.e. underlying data not available), is it possible to change the PlotStyle attribute (from 'traditional' to 'compact')?
This question is kind of tricky because not like other graphic objects in Matlab, boxplot is a group of lines. As so, all the properties that are set while you create it are inaccessible (and in fact does not exist) after plotting.
One option to deal with that is to create a 'dummy' boxplot, and then alter it to your data. Because boxplot has no simple properties of XData and YData, at least not as we use to them, it takes some work to do that.
Here is a short code to demonstrate that:
% this is just to make a figure for example:
X = normrnd(10,1,100,1);
boxplot(X) % this is the 'Traditional' figure that you load
% you start here, after you load your figure:
bx = findobj('tag','boxplot');
% get the properties of the axes:
axlimx = bx.Parent.XLim;
axlimy = bx.Parent.YLim;
% get all needed properties for plotting compact box plot
txt = bx.Parent.XAxis.TickLabels;
med = get(findobj(bx,'tag','Median'),'YData'); % Median
out = get(findobj(bx,'tag','Outliers'),'YData'); % Outliers
box = get(findobj(bx,'tag','Box'),'YData'); % the Box
whis = cell2mat(get([findobj(bx,'tag','Lower Whisker')...
findobj(bx,'tag','Upper Whisker')],'YData')); % Whisker
minmax = #(R) [min(R(:)) max(R(:))]; % helper function
close all
% Now we closed the original figure, and create a new one for manipulation:
boxplot(normrnd(10,1,100,1),'PlotStyle','Compact');
bxc = findobj('tag','boxplot');
% set the properties of the axes:
bxc.Parent.XLim = axlimx;
bxc.Parent.YLim = axlimy;
% set all properties of the compact box plot:
bxc.Children(1).String = txt;
set(bxc.Children(2),'YData',out) % Outliers
set(bxc.Children(3:4),'YData',med(1)) % MedianInner & MedianOuter
set(bxc.Children(5),'YData',minmax(box)) % the Box
set(bxc.Children(6),'YData',minmax(whis)) % Whisker
Another way to alter the boxplot to 'compact' style, is to change the graphics directly. In this case, we don't create a new dummy figure but work on the loaded figure.
Here is a code for that approach:
% this is just to make a figure for example:
X = normrnd(10,1,100,1);
boxplot(X) % this is the 'Traditional' figure that you load
% you start here, after you load your figure:
bx = findobj('tag','boxplot');
minmax = #(R) [min(R(:)) max(R(:))]; % helper function
% get the whisker limits:
whis = cell2mat(get([findobj(bx,'tag','Lower Whisker')...
findobj(bx,'tag','Upper Whisker')],'YData')); % Whisker
set(findobj(bx,'tag','Upper Whisker'),{'YData','Color','LineStyle'},...
{minmax(whis),'b','-'})
% set the median:
set(findobj(bx,'tag','Median'),{'XData','YData','LineStyle','Marker',...
'Color','MarkerSize','MarkerFaceColor'},...
{1,min(get(findobj(bx,'tag','Median'),'YData')),'none','o','b',6,'auto'});
% set the box:
set(findobj(bx,'tag','Box'),{'XData','YData','LineWidth'},...
{[1 1],minmax(get(findobj(bx,'tag','Box'),'YData')),4});
im_med = copyobj(findobj(bx,'tag','Median'),bx);
im_med.Marker = '.';
% set the outliers:
out = get(findobj(bx,'tag','Outliers'),'YData'); % Outliers
set(findobj(bx,'tag','Outliers'),{'XData','LineStyle','Marker','MarkerEdgeColor',...
'MarkerSize','MarkerFaceColor'},{0.9+0.2*rand(size(out)),'none','o','b',4,'none'});
% rotate x-axis labels:
bx.Parent.XAxis.TickLabelRotation = 90;
% delete all the rest:
delete(findobj(bx,'tag','Lower Whisker'))
delete(findobj(bx,'tag','Lower Adjacent Value'))
delete(findobj(bx,'tag','Upper Adjacent Value'))

Matlab - refresh 3D volume (slice) with GUI

I would like to refresh my figure (3D, obtained with the matlab slice command) using a slider (GUI) to update the slice position.
Here is my code to create the figure (it works):
% Create figure
Fig_3dview = figure;
Fig_3dview.Name= 'Microstructure viewer';
% hold on;
% The data to be displayed are within "Volume", a n*m*k matrix
Domain_size = size(Volume)
% Initial position of the slices
% Basically, the 6 faces of the volume
xslice = [1,Domain_size(1)];
yslice = [1,Domain_size(2)];
zslice = [1,Domain_size(3)];
% Display all the slices
imslice = slice(Volume,xslice,yslice,zslice,'parent',Fig_3dview);
% Remove voxel edge
set(imslice,'edgecolor','none')
% Current axis label
ax_3d_slice = gca;
% Set axis equal and tight
axis equal tight
hold off;
The code for the GUI, to control the slice position (it doesn't work):
% Create GUI figure
Fig_3dview_GUI = figure;
Fig_3dview_GUI.Name= 'Microstructure viewer 3D GUI';
% Create slider
h_slice1 = uicontrol('Style','slider','BackgroundColor','w',...
'Unit','pixels','Position',[10,200,250,50],'Parent',Fig_3dview_GUI,...
'Callback',{#slider_slice1_Callback});
% Define function associated with h_slice1
function slider_slice1_Callback(source,eventdata)
% Determine the selected data set.
pos_normalized = source.Value;
position_slice_1= round(pos_normalized*Domain_size(1)); % Get closest integer value
position_slice_1=max(1,position_slice_1); % 0 must be avoided
% Update figure
And here I don't know how to update the figure Fig_3dview with the new position for the slice normal to the first direction.
I would like the new figure looks like:
xslice = [position_slice_1,Domain_size(1)];
imslice = slice(Volume,xslice,yslice,zslice,'parent',Fig_3dview);
I do not want to close the figure and regenerate it, I want to refresh it, so that I get a smooth visualization.
% Update figure
end
Thanks for any advice.
François

How To Label Colormaps in MATLAB?

I have the following image derived from imagesc(some matrix whose entries correspond to those colors). The Cyan and the Yellow both mean different things. I would like to either:
Add a legend where I can fill in what each color means
Segregate parts of the X-axis to where I can type "cyan" on the x region below the cyan part, and "yellow" on the x region below the yellow part.
Either or would be fine, and which ever one is easier would be appropriate for me.
CYAN YELLOW
Do you want something like this? It's very basic haha.
clc
clear
close all
%// Dummy array
A = repmat([0 0 0 1 1 1],6,1);
imagesc(A)
hold on
%// Dummy data to add legend
scatter(0,0,1,'b','filled')
scatter(0,0,1,'r','filled')
axis off
colorbar
%// Get axis coordinates (x first and then y)
ax = axis;
%// Add text. You can easily adjust the x-offset depending on how many colors you have.
text(ax(2)/4+ax(1),ax(4)+.2,'Blue','Color','b','FontSize',20,'HorizontalAlignment','Center')
text(3*ax(2)/4+.2,ax(4)+.2,'Red','Color','r','FontSize',20,'HorizontalAlignment','Center')
%// Add legend
legend({'Blue';'Red'})
Output:
Here's another option, which happens to be matlab-hg2 friendly:
%% // Initialization
clear variables; close all force; clc;
%% // Generate some data
fakeData = magic(3)-0.5;
fakeData_horz = fakeData(:)'; %//'
fakeNames = cellstr(strcat('color',num2str((1:9)'))); %//'
fakeNameMapping = fakeNames(randperm(numel(fakeData)));
%% // Create figure
hFig = figure('Position',[680,488,758,610],'Resize','off');
%% // Top left example
cLims = [0 numel(fakeData)+1];
hSp = subplot(2,2,1);
imagesc(fakeData); axis image; set(hSp,'XTick',[],'YTick',[]);
colorbar; caxis(cLims);
[XX,YY] = meshgrid(1:size(fakeData,1),1:size(fakeData,2));
text(XX(:),YY(:),fakeNameMapping,'HorizontalAlignment','center');
%% // Bottom example
hSp = subplot(2,2,3:4);
cLims = [0 numel(fakeData)+1]; %Not required here since unchanged
imagesc(fakeData_horz); axis image; set(hSp,'XTick',[],'YTick',[]);
colorbar; caxis(cLims);
drawnow; %// This command will allow the annotations to be positioned properly
for ind1=1:numel(fakeData_horz)
newPos = [hSp.Position(1)+hSp.Position(3)/numel(fakeData_horz) * (ind1-1),...
hSp.Position(2)*1.6,... %1.6 is chosen for the demo
hSp.Position(3)/numel(fakeData_horz),...
0.05]; % 0.05 is chosen for the demo; play around with it
h= annotation('textbox',newPos,'String',fakeNameMapping{ind1},...
'LineStyle','none','HorizontalAlignment','center');
end
%% // Top right example
hSp = subplot(2,2,2);
cLims = [0 numel(fakeData)]; %// cLims is a bit different here!
imagesc(fakeData); axis image; set(hSp,'XTick',[],'YTick',[]);
caxis(hSp,cLims); colormap(hSp,parula(numel(fakeData)));
cb = colorbar; %// This time we need a handle to the colorbar
cb.Ticks = (hSp.CLim(1):hSp.CLim(2))+0.5; %// Set the tick positions
cb.TickLabels = fakeNames; %// Set the tick strings
Which results in:
Note: unless using a more intelligent text positioning computation, the figure's size should not be changed after it was plotted (in the 2nd example), because then the text no longer remains where it should be.
Edit: added another option where only the colorbar is labeled.

Matlab plot of several digital signals

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]);