Skip to content

Commit d888ad0

Browse files
fix issue #435
1 parent 084d98c commit d888ad0

File tree

2 files changed

+124
-4
lines changed

2 files changed

+124
-4
lines changed

plotly/plotlyfig_aux/handlegraphics/updateScatter.m

+120-2
Original file line numberDiff line numberDiff line change
@@ -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
%-------------------------------------------------------------------------%
215217
end
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.%sJitter', axName));
274+
jitWidth = eval(sprintf('plotData.%sJitterWidth', 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

plotly/plotlyfig_aux/helpers/extractScatterMarker.m

+4-2
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
marker = struct();
1818
marker.sizeref = 1;
1919
marker.sizemode = 'area';
20-
marker.size = getmarkerSize(plotData);
20+
marker.size = getMarkerSize(plotData);
2121
marker.line.width = 1.5*plotData.LineWidth;
2222

2323
filledMarkerSet = {'o', 'square', 's', 'diamond', 'd', 'v', '^', ...
@@ -32,6 +32,7 @@
3232
switch plotData.Marker
3333
case '.'
3434
markerSymbol = 'circle';
35+
marker.size = 0.1*marker.size;
3536
case 'o'
3637
markerSymbol = 'circle';
3738
case 'x'
@@ -152,6 +153,7 @@
152153
marker.line.color = lineColor;
153154
else
154155
marker.color = lineColor;
156+
if strcmp(plotData.Marker, '.'), marker.line.color = lineColor; end
155157
end
156158

157159
%-------------------------------------------------------------------------%
@@ -204,7 +206,7 @@
204206
outData = (outData - dataLim(1)) / diff(dataLim);
205207
end
206208

207-
function markerSize = getmarkerSize(plotData)
209+
function markerSize = getMarkerSize(plotData)
208210
markerSize = plotData.SizeData;
209211

210212
if length(markerSize) == 1

0 commit comments

Comments
 (0)