Main Content

두 개의 대화형 플롯을 포함하는 chart 클래스

이 예제에서는 대화형 기능이 포함된 두 개의 좌표축을 사용하여 타임테이블 데이터를 시각화하는 클래스를 정의하는 방법을 보여줍니다. 위쪽 좌표축에는 사용자가 관심 있는 영역을 검토할 수 있도록 x 차원에서의 패닝 및 확대/축소 기능이 활성화되어 있습니다. 아래쪽 좌표축은 전체 시간 범위에 대해 플롯을 표시합니다. 또한, 아래쪽 좌표축은 위쪽 좌표축의 시간 범위를 나타내는 연한 파란색 시간 윈도우도 표시합니다. chart 클래스는 다음 속성, 메서드 및 로컬 함수를 정의합니다.

속성:

  • Data - 타임테이블을 저장하는 퍼블릭 종속 속성입니다.

  • TimeLimits - 위쪽 좌표축의 제한과 아래쪽 좌표축의 시간 윈도우 너비를 설정하는 퍼블릭 속성입니다.

  • SavedData - 사용자가 차트의 인스턴스를 저장하고 불러오며 데이터를 유지할 수 있도록 하는 보호 속성입니다.

  • TopAxesBottomAxes - axes 객체를 저장하는 프라이빗 속성입니다.

  • TopLineBottomLine - line 객체를 저장하는 프라이빗 속성입니다.

  • TimeWindow - 아래쪽 좌표축에 표시되는 patch 객체로, 위쪽 좌표축의 시간 범위를 나타냅니다.

메서드:

  • set.Dataget.Data - 사용자가 차트의 인스턴스를 저장하고 불러오며 데이터를 유지할 수 있도록 합니다

  • setup - 차트가 생성될 때 한 번 실행됩니다. 레이아웃과 좌표축, line 객체 및 patch 객체를 구성합니다.

  • update - setup 메서드 다음에 실행되며 또한 사용자가 차트에 대한 하나 이상의 속성을 변경하면 실행됩니다.

  • panZoom - 사용자가 위쪽 좌표축 내에서 패닝 또는 확대/축소할 때 차트의 시간 제한을 업데이트합니다. 그 결과로 시간 윈도우가 새 제한을 반영하도록 업데이트됩니다.

  • click - 사용자가 아래쪽 좌표축을 클릭할 때 시간 제한을 다시 계산합니다.

로컬 함수:

  • updateDataTipTemplate - update 메서드 내에서 호출됩니다. 이 함수는 데이터팁에 타임테이블의 변수에 대응하는 행을 만듭니다.

  • mustHaveOneNumericVariable - Data 속성의 유효성을 검사합니다. 이 함수는 사용자가 지정한 타임테이블에 하나 이상의 숫자형 변수가 있는지 확인합니다.

클래스를 정의하려면 다음 코드를 편집기에 복사하고 쓰기 가능한 폴더에 TimeTableChart.m이라는 이름으로 저장하십시오.

classdef TimeTableChart < matlab.graphics.chartcontainer.ChartContainer
    properties (Dependent)
        Data timetable {mustHaveOneNumericVariable} = ...
            timetable(datetime.empty(0,1),zeros(0,1))
    end
    
    properties
        TimeLimits (1,2) datetime = [NaT NaT]
    end
    
    properties (Access = protected)
        SavedData timetable = timetable(datetime.empty(0,1),zeros(0,1))
    end
    
    properties (Access = private, Transient, NonCopyable)
        TopAxes matlab.graphics.axis.Axes
        TopLine matlab.graphics.chart.primitive.Line
        BottomAxes matlab.graphics.axis.Axes
        BottomLine matlab.graphics.chart.primitive.Line
        TimeWindow matlab.graphics.primitive.Patch
    end
    
    methods
        function set.Data(obj, tbl)
            % Reset the time limits if the row times have changed.
            oldTimes = obj.SavedData.Properties.RowTimes;
            newTimes = tbl.Properties.RowTimes;
            if ~isequal(oldTimes, newTimes)
                obj.TimeLimits = [NaT NaT];
            end
            
            % Store the new table.
            obj.SavedData = tbl;
        end
        
        function tbl = get.Data(obj)
            tbl = obj.SavedData;
        end
    end
    
    methods (Access = protected)
        function setup(obj)
            % Create two axes. The top axes is 3x taller than bottom axes.
            tcl = getLayout(obj);
            tcl.GridSize = [4 1];
            obj.TopAxes = nexttile(tcl, 1, [3 1]);
            obj.BottomAxes = nexttile(tcl, 4);
            
            % Add a shared toolbar on the layout, which removes the
            % toolbar from the individual axes.
            axtoolbar(tcl, 'default');
            
            % Create one line to show the zoomed-in data.
            obj.TopLine = plot(obj.TopAxes, NaT, NaN);
            
            % Create one line to show an overview of the data, and disable
            % HitTest so the ButtonDownFcn on the bottom axes works.
            obj.BottomLine = plot(obj.BottomAxes, NaT, NaN, ...
                'HitTest', 'off');
            
            % Create a patch to show the current time limits.
            obj.TimeWindow = patch(obj.BottomAxes, ...
                'Faces', 1:4, ...
                'Vertices', NaN(4,2), ...
                'FaceColor', obj.TopLine.Color, ...
                'FaceAlpha', 0.3, ...
                'EdgeColor', 'none', ...
                'HitTest', 'off');
            
            % Constrain axes panning/zooming to only the X-dimension.
            obj.TopAxes.Interactions = [ ...
                dataTipInteraction;
                panInteraction('Dimensions','x');
                rulerPanInteraction('Dimensions','x');
                zoomInteraction('Dimensions','x')];
            
            % Disable pan/zoom on the bottom axes.
            obj.BottomAxes.Interactions = [];
            
            % Add a listener to XLim to respond to zoom events.
            addlistener(obj.TopAxes, 'XLim', 'PostSet', @(~, ~) panZoom(obj));
            
            % Add a callback for clicks on the bottom axes.
            obj.BottomAxes.ButtonDownFcn = @(~, ~) click(obj);
        end
        
        function update(obj)
            % Extract the time data from the table.
            tbl = obj.Data;
            t = tbl.Properties.RowTimes;
            
            % Extract the numeric variables from the table.
            S = vartype('numeric');
            numericTbl = tbl(:,S);
            
            % Update the data on both lines.
            set([obj.BottomLine obj.TopLine], 'XData', t, 'YData', numericTbl{:,1});
            
            % Create a dataTipTextRow for each variable in the timetable.
            updateDataTipTemplate(obj.TopLine, tbl)
            
            % Update the top axes limits.
            obj.TopAxes.YLimMode = 'auto';
            if obj.TimeLimits(1) < obj.TimeLimits(2)
                obj.TopAxes.XLim = obj.TimeLimits;
            else
                % Current time limits are invalid, so set XLimMode to auto and
                % let the axes calculate limits based on available data.
                obj.TopAxes.XLimMode = 'auto';
                obj.TimeLimits = obj.TopAxes.XLim;
            end
            
            % Update time window to reflect the new time limits.
            xLimits = ruler2num(obj.TimeLimits, obj.BottomAxes.XAxis);
            yLimits = obj.BottomAxes.YLim;
            obj.TimeWindow.Vertices = [xLimits([1 1 2 2]); yLimits([1 2 2 1])]';
        end
        
        function panZoom(obj)
            % When XLim on the top axes changes, update the time limits.
            obj.TimeLimits = obj.TopAxes.XLim;
        end
        
        function click(obj)
            % When clicking on the bottom axes, recenter the time limits.
            
            % Find the center of the click using CurrentPoint.
            center = obj.BottomAxes.CurrentPoint(1,1);
            
            % Convert from numeric units into datetime using num2ruler.
            center = num2ruler(center, obj.BottomAxes.XAxis);
            
            % Find the width of the current time limits.
            width = diff(obj.TimeLimits);
            
            % Recenter the current time limits.
            obj.TimeLimits = center + [-1 1]*width/2;
        end
    end
end

function updateDataTipTemplate(obj, tbl)

% Create a dataTipTextRow for each variable in the timetable.
timeVariable = tbl.Properties.DimensionNames{1};
rows = dataTipTextRow(timeVariable, tbl.(timeVariable));
for n = 1:numel(tbl.Properties.VariableNames)
    rows(n+1,1) = dataTipTextRow(...
        tbl.Properties.VariableNames{n}, tbl{:,n});
end
obj.DataTipTemplate.DataTipRows = rows;

end

function mustHaveOneNumericVariable(tbl)

% Validation function for Data property.
S = vartype('numeric');
if width(tbl(:,S)) < 1
    error('TimeTableChart:InvalidTable', ...
        'Table must have at least one numeric variable.')
end

end

클래스 파일을 저장한 후, 차트의 인스턴스를 만듭니다. 여기서는 차트를 사용하여 1년 치의 자전거 통행량 데이터에서 몇 주간의 데이터를 검토합니다.

bikeTbl = readtimetable('BicycleCounts.csv');
bikeTbl = bikeTbl(169:8954,:);
tlimits = [datetime(2015,8,6) datetime(2015,8,27)];
TimeTableChart('Data',bikeTbl,'TimeLimits',tlimits);

참고 항목

함수

클래스

속성

관련 항목