두 개의 대화형 플롯을 포함하는 chart 클래스
이 예제에서는 대화형 기능이 포함된 두 개의 좌표축을 사용하여 타임테이블 데이터를 시각화하는 클래스를 정의하는 방법을 보여줍니다. 위쪽 좌표축에는 사용자가 관심 있는 영역을 검토할 수 있도록 x 차원에서의 패닝 및 확대/축소 기능이 활성화되어 있습니다. 아래쪽 좌표축은 전체 시간 범위에 대해 플롯을 표시합니다. 또한, 아래쪽 좌표축은 위쪽 좌표축의 시간 범위를 나타내는 연한 파란색 시간 윈도우도 표시합니다. chart 클래스는 다음 속성, 메서드 및 로컬 함수를 정의합니다.
속성:
Data
- 타임테이블을 저장하는 퍼블릭 종속 속성입니다.TimeLimits
- 위쪽 좌표축의 제한과 아래쪽 좌표축의 시간 윈도우 너비를 설정하는 퍼블릭 속성입니다.SavedData
- 사용자가 차트의 인스턴스를 저장하고 불러오며 데이터를 유지할 수 있도록 하는 보호 속성입니다.TopAxes
및BottomAxes
- axes 객체를 저장하는 프라이빗 속성입니다.TopLine
및BottomLine
- line 객체를 저장하는 프라이빗 속성입니다.TimeWindow
- 아래쪽 좌표축에 표시되는 patch 객체로, 위쪽 좌표축의 시간 범위를 나타냅니다.
메서드:
set.Data
및get.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);