@@ -22,6 +22,7 @@ function updateScatter(obj,plotIndex)
2222 obj.data{plotIndex }.type = ' scatter' ;
2323 obj.data{plotIndex }.xaxis = sprintf(' x%d ' , xSource );
2424 obj.data{plotIndex }.yaxis = sprintf(' y%d ' , ySource );
25+ updateCategoricalAxis(obj , plotIndex );
2526 else
2627 obj.data{plotIndex }.type = ' scatter3d' ;
2728 obj.data{plotIndex }.scene = sprintf(' scene%d ' , xSource );
@@ -36,8 +37,9 @@ function updateScatter(obj,plotIndex)
3637 % -------------------------------------------------------------------------%
3738
3839 % -set trace data-%
39- obj.data{plotIndex }.x = plotData .XData ;
40- obj.data{plotIndex }.y = plotData .YData ;
40+ [xData , yData ] = getTraceData2D(plotData );
41+ obj.data{plotIndex }.x = xData ;
42+ obj.data{plotIndex }.y = yData ;
4143
4244 if isScatter3D
4345 obj.data{plotIndex }.z = plotData .ZData ;
@@ -214,3 +216,119 @@ function updateScene(obj, dataIndex)
214216 % -------------------------------------------------------------------------%
215217end
216218
219+ function updateCategoricalAxis(obj , plotIndex )
220+
221+ % -INITIALIZATIONS-%
222+ axIndex = obj .getAxisIndex(obj .State .Plot(plotIndex ).AssociatedAxis);
223+ [xSource , ySource ] = findSourceAxis(obj ,axIndex );
224+ plotData = get(obj .State .Plot(plotIndex ).Handle);
225+
226+ xData = plotData .XData ;
227+ yData = plotData .YData ;
228+
229+ if iscategorical(xData )
230+ ax = eval(sprintf(' obj.layout.xaxis%d ' , xSource ));
231+ nTicks = length(ax .ticktext );
232+
233+ ax.autorange = false ;
234+ ax.range = 0.5 + [0 nTicks ];
235+ ax.type = ' linear' ;
236+ ax.tickvals = 1 : nTicks ;
237+
238+ eval(sprintf(' obj.layout.xaxis%d = ax;' , xSource ));
239+ end
240+
241+ if iscategorical(yData )
242+ ax = eval(sprintf(' obj.layout.yaxis%d ' , ySource ));
243+ nTicks = length(ax .ticktext );
244+
245+ ax.autorange = false ;
246+ ax.range = 0.5 + [0 nTicks ];
247+ ax.type = ' linear' ;
248+ ax.tickvals = 1 : nTicks ;
249+
250+ eval(sprintf(' obj.layout.yaxis%d = ax;' , ySource ));
251+ end
252+ end
253+
254+ function [xData , yData ] = getTraceData2D(plotData )
255+
256+ % -initializations-%
257+ isSwarmchart = isfield(plotData , ' XJitter' );
258+ xData = categ2NumData(plotData .XData );
259+ yData = categ2NumData(plotData .YData );
260+
261+ % -get 2D trace data-%
262+ if isSwarmchart
263+ if ~strcmp(plotData .XJitter , ' none' )
264+ xData = setJitData(xData , yData , plotData , ' X' );
265+
266+ elseif ~strcmp(plotData .XJitter , ' none' )
267+ yData = setJitData(yData , xData , plotData , ' Y' );
268+ end
269+ end
270+ end
271+
272+ function jitData = setJitData(jitData , refData , plotData , axName )
273+ jitType = eval(sprintf(' plotData.%s Jitter' , axName ));
274+ jitWidth = eval(sprintf(' plotData.%s JitterWidth' , axName ));
275+ jitUnique = sort(unique(jitData ), ' ascend' );
276+ jitWeight = getJitWeight(jitData , refData );
277+ isJitDensity = strcmp(jitType , ' density' );
278+
279+ for n = 1 : length(jitUnique )
280+ jitInd = find(jitData == jitUnique(n ));
281+
282+ if length(jitInd ) > 1
283+ jitDataN = getJitData(refData(jitInd ), jitWidth , jitType );
284+ if isJitDensity , jitDataN = jitWeight(n )*jitDataN ; end
285+ jitData(jitInd ) = jitData(jitInd ) + jitDataN ;
286+ end
287+ end
288+ end
289+
290+ function jitWeight = getJitWeight(jitData , refData )
291+ jitUnique = sort(unique(jitData ), ' ascend' );
292+
293+ for n = 1 : length(jitUnique )
294+ jitInd = find(jitData == jitUnique(n ));
295+
296+ if length(jitInd ) > 1
297+ refDataN = refData(jitInd );
298+ stdData(n ) = std( refDataN(~isnan(refDataN )) );
299+ end
300+ end
301+
302+ jitWeight = ( stdData / min(stdData ) ).^(-1 );
303+ end
304+
305+ function jitData = getJitData(refData , jitWeight , jitType )
306+ jitData = rand(size(refData )) - 0.5 ;
307+
308+ if strcmp(jitType , ' density' )
309+ refPoints = linspace(min(refData ), max(refData ), 2 * length(refData ));
310+ [densityData , refPoints ] = ksdensity(refData , refPoints );
311+ densityData = jitWeight * rescale(densityData , 0 , 1 );
312+
313+ for n = 1 : length(refData )
314+ [~ , refInd ] = min(abs(refPoints - refData(n )));
315+ jitData(n ) = jitData(n ) * densityData(refInd );
316+ end
317+
318+ elseif strcmp(jitType , ' rand' )
319+ jitData = jitWeight * jitData ;
320+
321+ elseif strcmp(jitType , ' rand' )
322+ jitData = jitWeight * rescale(randn(size(refData )), - 0.5 , 0.5 );
323+
324+ end
325+ end
326+
327+ function numData = categ2NumData(categData )
328+ numData = categData ;
329+
330+ if iscategorical(categData )
331+ [~ , ~ , numData ] = unique(numData );
332+ numData = numData ' ;
333+ end
334+ end
0 commit comments