Ben Vincent
Ben Vincent

Reputation: 381

multiple matlab contour plots with one level

I have a number of 2d probability mass functions from 2 categories. I am trying to plot the contours to visualise them (for example at their half height, but doesn't really matter).

I don't want to use contourf to plot directly because I want to control the fill colour and opacity. So I am using contourc to generate xy coordinates, and am then using fill with these xy coordinates.

The problem is that the xy coordinates from the contourc function have strange numbers in them which cause the following strange vertices to be plotted.

enter image description here

At first I thought it was the odd contourmatrix format, but I don't think it is this as I am only asking for one value from contourc. For example...

contourmatrix = contourc(x, y, Z, [val, val]);
h = fill(contourmatrix(1,:), contourmatrix(2,:), 'r');

Does anyone know why the contourmatrix has these odd values in them when I am only asking for one contour?

UPDATE:

My problem seems might be a failure mode of contourc when the input 2D matrix is not 'smooth'. My source data is a large set of (x,y) points. Then I create a 2D matrix with some hist2d function. But when this is noisy the problem is exaggerated... enter image description here

But when I use a 2d kernel density function to result in a much smoother 2D function, the problem is lessened... enter image description here

The full process is a) I have a set of (x,y) points which form samples from a distribution b) I convert this into a 2D pmf c) create a contourmatrix using contourc d) plot using fill

Upvotes: 0

Views: 728

Answers (1)

Hoki
Hoki

Reputation: 11812

Your graphic glitches are because of the way you use the data from the ContourMatrix. Even if you specify only one isolevel, this can result in several distinct filled area. So the ContourMatrix may contain data for several shapes.


simple example:

isolevel = 2 ;
[X,Y,Z] = peaks ;
[C,h] = contourf(X,Y,Z,[isolevel,isolevel]);

Produces:

contourf output


Note that even if you specified only one isolevel to be drawn, this will result in 2 patches (2 shapes). Each has its own definition but they are both embedded in the ContourMatrix, so you have to parse it if you want to extract each shape coordinates individually.

To prove the point, if I simply throw the full contour matrix to the patch function (the fill function will create patch objects anyway so I prefer to use the low level function when practical). I get the same glitch lines as you do:

xc = X(1,:) ;
yc = Y(:,1) ;
c = contourc(xc,yc,Z,[isolevel,isolevel]);
hold on
hp = patch(c(1,1:end),c(2,1:end),'r','LineWidth',2) ;

produces the same kind of glitches that you have:

not parsed


Now if you properly extract each shape coordinates without including the definition column, you get the proper shapes. The example below is one way to extract and draw each shape for inspiration but they are many ways to do it differently. You can certainly compact the code a lot but here I detailed the operations for clarity.

The key is to read and understand how the ContourMatrix is build.

parsed = false ;
iShape = 1 ;
while ~parsed
    %// get coordinates for each isolevel profile
    level   = c(1,1) ; %// current isolevel
    nPoints = c(2,1) ; %// number of coordinate points for this shape

    idx = 2:nPoints+1 ; %// prepare the column indices of this shape coordinates
    xp = c(1,idx) ;     %// retrieve shape x-values
    yp = c(2,idx) ;     %// retrieve shape y-values
    hp(iShape) = patch(xp,yp,'y','FaceAlpha',0.5) ; %// generate path object and save handle for future shape control.

    if size(c,2) > (nPoints+1)
        %// There is another shape to draw
        c(:,1:nPoints+1) = [] ; %// remove processed points from the contour matrix
        iShape = iShape+1 ;     %// increment shape counter
    else
       %// we are done => exit while loop
       parsed  = true ;
    end
end
grid on

This will produce:

enter image description here

Upvotes: 2

Related Questions