8
$\begingroup$

I have plotted a ListContourPlot and have not specified the number of contours that are plotted, just the range. Is there a way to count how many contours have been automatically plotted by Mathematica?

EDIT

If I have contours representing different values, is there a way to select the contour relative to a specific value?

$\endgroup$
  • $\begingroup$ I and J.M. interpreted the question differently. Are you trying to count the number of contours in his sense or in the sense that I counted them? $\endgroup$ – C. E. Apr 5 '17 at 22:53
  • $\begingroup$ @C.E. Let us assume I have a contour plot with 10 contours and their value ranges from 1 to 10. How can I select the contour which represents the value of 7 (without selecting the 7th contour)? $\endgroup$ – GEF Apr 5 '17 at 23:00
  • 1
    $\begingroup$ What do you mean "select"? $\endgroup$ – C. E. Apr 5 '17 at 23:21
  • $\begingroup$ @C.E. I mean extracting all the points belonging to the contour representing the value of 7. $\endgroup$ – GEF Apr 5 '17 at 23:25
  • $\begingroup$ You can use the same method that march uses in his answer. You apply Normal to the plot and then extract the lines using Cases. Select all lines such that if you take on of the coordinates in the line and apply your function then you get the number that you desire. $\endgroup$ – C. E. Apr 5 '17 at 23:29
4
$\begingroup$

FindDivisions

The list of automatically selected contours can be obtained using FindDivisions[{#, #2}, #3, Method -> {}] as the setting for Contours.

SeedRandom[123]
data = RandomReal[1, {10, 10}];
lcp1 = ListContourPlot[data, InterpolationOrder -> 3, ImageSize -> 300];

The default list of contours in lcp1 can also be obtained using FindDivisions in the option setting for Contours. (See this answer to a related question.)

lcp2 = ListContourPlot[data, InterpolationOrder -> 3, 
   Contours -> ((contoursFD = FindDivisions[{#, #2}, #3, Method -> {}]) &)];

The two graphics objects lcp1 and lcp2 are identical:

lcp1 == lcp2

True

And the list contoursFD captured in lcp2 is identical with the default list of contours in lcp1.

N @ contoursFD

{0., 0.2, 0.4, 0.6, 0.8, 1.}

These contours match the ones produced by @J.M.'s method using Tooltips:

contoursTT = Reverse[Cases[lcp1, Tooltip[a_ /; Not[FreeQ[a, _Line]], t_] :> t, ∞]]

{0, 0.2, 0.4, 0.6, 0.8, 1}

Row[{Show[lcp1, PlotLabel -> Style["contours : " <> ToString@contoursTT, 16]], 
  Show[lcp2, PlotLabel -> Style["contours : " <> ToString@N[contoursFD], 16]]}]

enter image description here

Update: FindDivisions also works to find the contour levels for the setting Contours -> {Automatic, atmostn} using atmostn as the second argument of FindDivisions:

atmostn = 3;
lcp3a = ListContourPlot[data, InterpolationOrder -> 3, Contours -> {Automatic, atmostn}];
contoursTT = Reverse[Cases[lcp3a, Tooltip[a_ /; Not[FreeQ[a, _Line]], t_] :> t, ∞]];
lcp3b = ListContourPlot[data, InterpolationOrder -> 3, 
   Contours -> ((contoursFD = FindDivisions[{#, #2}, atmostn, Method -> {}]) &)];
contoursFD == contoursTT

True

Style[Row[{Show[lcp3a, PlotLabel ->  
      Pane[Style["contours : " <> ToString@N[contoursTT], 12], 
       ImageSize -> {200, Automatic}]],
   Show[lcp3b, PlotLabel -> 
     Pane[Style["contours : " <> ToString@N[contoursFD], 12], 
      ImageSize -> {200, Automatic}]]}], 
 ImageSizeMultipliers -> {1, 1}]

enter image description here

For atmostn = 25 we get

contoursFD == contoursTT

True

enter image description here

Update 2: For the question added in the update:

assume I have a contour plot with 10 contours and their value ranges from 1 to 10. How can I select the contour which represents the value of 7 (without selecting the 7th contour)?

a contour plot with 10 contours and their value ranges from 1 to 10:

lcp10 = ListContourPlot[3 + data, InterpolationOrder -> 3, 
  Contours -> Range[10], ContourShading -> None, 
  ContourStyle -> (ColorData[63, "ColorList"][[;; 10]])]

enter image description here

How can I select the contour which represents the value of 7?

lcpC7 = ListContourPlot[3 + data, InterpolationOrder -> 3, 
  Contours -> {7}, ContourStyle -> Directive[Red, Thick], 
  ContourShading -> None]

enter image description here

lines = Cases[Normal@lcpC7, _Line, Infinity];
lines // Short

{Line[{{100., 3.55836}, {99.9841, 3.5646}, <<24>> ,{97.1146, 4.}}], <<29>>, <<1>>}

Show[lcp10, Epilog -> {Blue, Thick, lines}]

enter image description here

| improve this answer | |
$\endgroup$
6
$\begingroup$

If you're dealing with multiple contours corresponding to multiple heights, a hacky way is to count the number of Tooltip[] objects being used internally. For instance,

conts = ContourPlot[Cos[x] + Cos[y], {x, 0, 4 π}, {y, 0, 4 π}, ContourShading -> None]

contours

Then,

Count[conts, Tooltip[l : {__} /; MemberQ[l, Line[__]], v_?NumericQ], ∞]
   7

Here, counting Line[] objects would result in a gross overestimate. This is confirmed when one uses the explicit setting Contours -> 7 in ContourPlot[].

| improve this answer | |
$\endgroup$
5
$\begingroup$

Original interpretation of the question

cp = ContourPlot[Cos[x] + Cos[y] == 1/2, {x, 0, 4 π}, {y, 0, 4 π}]

Mathematica graphics

Count[cp, _Line, ∞]

9

To understand how this works you need to be familiar with how graphics is represented in Mathematica. Examining FullForm[cp] is a good start in case you don't know this yet.

Here is my take on the extra question using the same kind of approach:

cp = ContourPlot[{Abs[Sin[x] Sin[y]] == 0.5, 
   Abs[Cos[x] Cos[y]] == 0.5}, {x, -3, 3}, {y, -3, 3}]

Mathematica graphics

Cases[cp, {Directive[col_, __], lines__Line} :> {col, Length[{lines}]}, ∞]

Mathematica graphics

But I think you are better off plotting the levels one by one and then using the approach here above.

cps = ContourPlot[#, {x, -3, 3}, {y, -3, 3}] & /@ {Abs[
     Sin[x] Sin[y]] == 0.5, Abs[Cos[x] Cos[y]] == 0.5}

Mathematica graphics

Count[#, _Line, ∞] & /@ cps

{4, 9}

Alternative interpretation of the question

J.M. interpreted the question a little differently than I. My version of his code would not count tooltip labels, but would count colors instead. The reason is that it's slightly easier.

cp = ContourPlot[
  Cos[x] + Cos[y], {x, 0, 4 π}, {y, 0, 4 π},
  ContourShading -> None,
  ContourStyle -> (ColorData[97][#] & /@ Range[100])
  ];

Count[cp, _RGBColor, ∞]

7

Note that I did not need to know the number of levels in order to insert the colors, I only had to make sure to generate more colors than I had levels.

| improve this answer | |
$\endgroup$
3
$\begingroup$

As mentioned by @JM, ContourPlot by default includes a tooltip for each contour value. The first element of each tooltip includes the Line objects making up the contour, using GraphicsComplex coordinates. So, if you want to have an association tying each contour value with the Line objects making up that contour, you can do the following:

assoc = Association @ Cases[
    Normal @ ContourPlot[Cos[x] + Cos[y], {x, 0, 4 π}, {y, 0, 4 π}],
    Tooltip[x_, y_] :> y -> Cases[x,_Line,Infinity],
    Infinity
];

The total number of contours is:

Length @ assoc

7

and the Line objects associated with the contour 1.5 is:

assoc[1.5] //Short[#,10]&

{Line[{{0.,1.04642},{0.0360218,1.04582},{0.0790736,1.04292},{0.1122,1.03923},{0.137919,1.03552},{0.213951,1.02025},{0.224399,1.01775},{0.249554,1.0098},{0.315379,0.988578},{0.336599,0.980251},<<19>>,{0.980251,0.336599},{0.988578,0.315379},{1.0098,0.249554},{1.01775,0.224399},{1.02025,0.213951},{1.03552,0.137919},{1.03923,0.1122},{1.04292,0.0790736},{1.04582,0.0360218},{1.04642,0.}}],<<7>>,Line[{{<<19>>,<<18>>},<<163>>}]}

| improve this answer | |
$\endgroup$
1
$\begingroup$

Expanding on C.E.'s answer to address the extra question, let's do the following. Taking the function

f[x_, y_] = Cos[x] + Cos[y];

as an example consider the contour plot:

cp = ContourPlot[f[x, y],
        {x, 0, 4 Pi}, {y, 0, 4 Pi},
        Contours -> {0.1, 0.5, 1}, ContourShading -> False]

enter image description here

If we do

assoc = GroupBy[Cases[Normal@cp, Line[a_] :> a, Infinity], f @@ #[[1]] &];

then we get an Association object that associates the value of the contour (as computed by f @@ #[[1]] &) to the list of contours with that value. Unfortunately, because of numerical error in the contour values, there are more than just the three (0.1, 0.5, and 1):

Keys@assoc // FullForm
(* List[1.`, 1.0000000000000002`, 0.9999999999999996`, 1.0000438962964133`, 0.4993229505918183`, 0.4993229505918182`, 0.4993229505918185`, 0.4993229505918175`, 0.5006924094447289`, 0.09990244696130435`, 0.09990244696130413`, 0.09990244696130446`, 0.10067337224158247`] *)

Maybe that's okay, because maybe we want to know the values on the contours to machine precision, but probably not. To get around this, we can use Round in our selector function, like so:

assoc = GroupBy[Cases[Normal@cp, Line[a_] :> a, Infinity], Round[f @@ #[[1]], 0.01] &];

The choice of 0.01 is made after examining what we get. Then:

Keys@assoc
(* {1., 0.5, 0.1} *)

To get the contours for a particular value, do, for instance,

assoc[1.]

To see how it works:

ListLinePlot@assoc[1.]

enter image description here

| improve this answer | |
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.