Plot circles with alpha values in MATLAB
I'm creating a scatter plot of some data, as you do, and I have a number of repeated data points which I'd like to plot as circles with some alpha value so that piling on of extra points at the same location is obvious.
As far as I can te开发者_StackOverflow社区ll you cannot set the alpha properties of the little circles that you generate with plot(x, y, 'o')
, so I've resorted to drawing thousands of little circles using patch()
myself:
x = repmat([1:10], [1 10]);
y = round(10*rand(100, 1))/10;
xlim([0 11])
ylim([0 1])
p = ag_plot_little_circles(x', y, 10, [1 0 .4], 0.2);
function p = ag_plot_little_circles(x, y, circle, col, alpha)
%AG_PLOT_LITTLE_CIRCLES Plot circular circles of relative size circle
% Returns handles to all patches plotted
% aspect is width / height
fPos = get(gcf, 'Position');
% need width, height in data values
xl = xlim();
yl = ylim();
w = circle*(xl(2)-xl(1))/fPos(3);
h = circle*(yl(2)-yl(1))/fPos(4);
theta = 0:pi/5:2*pi;
mx = w*sin(theta);
my = h*cos(theta);
num = 0;
for k = 1:max(size(x))
for f = 1:size(y,2)
num = num+1;
p(num) = patch(x(k)+mx, y(k,f)+my, col, 'FaceColor', col, 'FaceAlpha', alpha, 'EdgeColor', 'none');
end
end
end
As you can see, this is not optimal as I need to know and set the size of the plot (xlim
and ylim
) before I plot it so that the circles end up being circular. If I reshape the plot then they end up as ovals. I also end up with millions of objects, which is a pain when making legends.
Is there an easier way?
I have found no way to have a line marker with alpha in MATLAB.
If you look at line properties (the (mid) low level function behind plot), you will see that you can define markers, and their sole properties are their color (MarkerEdgeColor
or MarkerFaceColor
), which does not take any alpha (there is no MarkerFaceAlpha
property).
So how you do it with patches seems the way to go.
The only thing I can propose to avoid having zillions of objects would be to group them in a hggroup, which will make them appear as a single object in the legend.
I tried to do a general function using patch
. It's slow and a bit buggy, but it works OK for simple cases. MATLAB needs a real-line alpha though.
x
and y
are points to plot, w
the width of the line, col
the color and a
the alpha value.
Example:
x = linspace(-2,2,100);
plotWithAlpha(x,sin(4*x),0.04,'r',0.2)
function [] = plotWithAlpha(x,y,w,col,a)
%function [] = plotWithAlpha(x,y,w,col,a)
if(size(x,1) ~= 1)
x = x.';
end
if(size(x,1) ~= 1)
y = y.';
end
sz = length(x);
% Calculate derrivatives of the curve
X = csaps(1:sz(1),x,1);
Y = csaps(1:sz(1),y,1);
mx = fnval(fnder(X,1),1:sz(1)).';
my = fnval(fnder(Y,1),1:sz(1)).';
T = [mx my]; %tangent
% Normalize tangents
T = bsxfun(@rdivide,T,sqrt(sum(T.^2,2)));
N = zeros(size(T));
N(:,2) = 1;
N(:,1) = -T(:,2)./T(:,1);
N = bsxfun(@rdivide,N,sqrt(sum(N.^2,2)));
N = N.';
hold on
for i = 2:length(x)
X = [x(i-1)+w*N(1,i-1) x(i)+w*N(1,i) x(i-1)-w*N(1,i-1) x(i)-w*N(1,i)];
Y = [y(i-1)+w*N(2,i-1) y(i)+w*N(2,i) y(i-1)-w*N(2,i-1) y(i)-w*N(2,i)];
% Order the points
D = pdist([X' Y']);
D = squareform(D); D(D==0) = Inf;
inds = 1;
D(:,1) = Inf;
[val ind] = min(D(1,:));
inds(2) = ind;
D(:,ind) = Inf;
[val ind] = min(D(ind,:));
inds(3) = ind;
D(:,ind) = Inf;
inds(4) = setxor(inds,1:4);
X = X(inds);
Y = Y(inds);
patch(X,Y,col,'edgeColor','none','FaceAlpha',a)
end
hold off
I have the same problem, and my scatter plot has 100,000's of points. The idea of answer #2 is great, but it was impractical for me to open my plot in Illustrator because I had too many points. So I adapted answer #2 into a batch process:
print the figure to a pdf file in matlab:
print -dpdf foo.pdf
convert the pdf file to svg using pdf2svg from debian package pdf2svg:
pdf2svg foo.pdf foo.svg
make all blue objects transparent:
sed '/rgb(0%,0%,100%)/s/fill-opacity:1/fill-opacity:.2' < foo.svg > foo2.svg
convert foo2.svg to pdf using rsvg-convert from debian package librsvg2-bin:
rsvg-convert -f pdf -o foo2.pdf foo2.svg
Found the patchline
function on the Matlab Exchange that can take any patch
arguments including 'FaceAlpha'
:
http://www.mathworks.com/matlabcentral/fileexchange/36953-patchline/content/patchline.m
As far I can tell, there is no way to directly set alpha for markers in MATLAB. Patch methods fail to work well when alpha is set below about 0.03 or when there are far more than 10,000 markers.
The workaround to generate publication quality graphics when you have a dense cloud of markers is to give the markers a unique color. Then print the figure to EPS. Then open in Illustrator, select all objects of that color and set the transparency and color of the objects in Illustrator. It's not the one-step solution one would hope for, but it does give much, much better looking results than using internal MATLAB functions and is quick, provided you have Illustrator or an equivalent vector graphics package.
精彩评论