matlabanimationhistogrammatlab-figurematlab-hg2

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


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:

                          The desired result

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 ), along with the histogram itself?


Solution

  • Turns out the solution is quite straight-forward, at least when only a single datatip is needed. Here are the required steps:

    1. Replace the bar plot with a histogram:

      hHist = histogram(data(1,:),edges);
      
    2. Create the datatip "manually" instead of using makedatatip:

      hDataCursorMgr = datacursormode(ancestor(hHist,'figure'));
      hDT = createDatatip(hDataCursorMgr,hHist);
      
    3. Update the position as needed:

      hDT.Cursor.DataIndex = maxInd;
      
    4. 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:

                                  The desired result 2


    Some notes \ observations: