Извлечение контуров из ContourPlot в Mathematica - PullRequest
13 голосов
/ 24 июля 2011

У меня есть функция f (x, y) двух переменных, из которых мне нужно знать местоположение кривых, по которым она пересекает ноль.ContourPlot делает это очень эффективно (то есть: он использует умные многошаговые методы, а не просто детальное сканирование методом грубой силы), а просто дает мне график.Я хотел бы иметь набор значений {x, y} (с некоторым заданным разрешением) или, возможно, некоторую интерполяционную функцию, которая позволяет мне получить доступ к расположению этих контуров.

Подумал об извлечении этого из FullForm ContourPlot, но это похоже на хак.Есть ли лучший способ сделать это?

Ответы [ 2 ]

12 голосов
/ 24 июля 2011

Если вы в конечном итоге извлекаете очки из ContourPlot, это простой способ сделать это:

points = Cases[
  Normal@ContourPlot[Sin[x] Sin[y] == 1/2, {x, -3, 3}, {y, -3, 3}],
  Line[pts_] -> pts,
  Infinity
]

Join @@ points (* if you don't want disjoint components to be separate *)

РЕДАКТИРОВАТЬ

Похоже, что ContourPlot не дает очень точных контуров. Они, конечно, предназначены для построения графиков и достаточно хороши для этого, но точки не лежат точно на контурах:

In[78]:= Take[Join @@ points /. {x_, y_} -> Sin[x] Sin[y] - 1/2, 10]

Out[78]= {0.000163608, 0.0000781187, 0.000522698, 0.000516078, 
0.000282781, 0.000659909, 0.000626086, 0.0000917416, 0.000470424, 
0.0000545409}

Мы можем попытаться придумать наш собственный метод для отслеживания контура, но сделать это в общем случае довольно сложно. Вот концепция, которая работает для плавно меняющихся функций, имеющих гладкие контуры:

  1. Начните с некоторой точки (pt0) и найдите пересечение с контуром вдоль градиента f.

  2. Теперь у нас есть точка на контуре. Двигайтесь по касательной к контуру с фиксированным шагом (resolution), затем повторите с шага 1.

Вот базовая реализация, которая работает только с функциями, которые можно символически дифференцировать:

rot90[{x_, y_}] := {y, -x}

step[f_, pt : {x_, y_}, pt0 : {x0_, y0_}, resolution_] :=
 Module[
  {grad, grad0, t, contourPoint},
  grad = D[f, {pt}];
  grad0 = grad /. Thread[pt -> pt0];
  contourPoint = 
    grad0 t + pt0 /. First@FindRoot[f /. Thread[pt -> grad0 t + pt0], {t, 0}];
  Sow[contourPoint];
  grad = grad /. Thread[pt -> contourPoint];
  contourPoint + rot90[grad] resolution
 ]

result = Reap[
   NestList[step[Sin[x] Sin[y] - 1/2, {x, y}, #, .5] &, {1, 1}, 20]
];

ListPlot[{result[[1]], result[[-1, 1]]}, PlotStyle -> {Red, Black}, 
 Joined -> True, AspectRatio -> Automatic, PlotMarkers -> Automatic]

Illustration of the contour finding method

Красные точки - это «отправные точки», а черные точки - след контура.

РЕДАКТИРОВАТЬ 2

Возможно, проще и лучше использовать аналогичную технику для уточнения пунктов, полученных из ContourPlot. Начните с начальной точки, затем двигайтесь вдоль градиента, пока мы не пересечем контур.

Обратите внимание, что эта реализация также будет работать с функциями, которые не могут быть символически дифференцированы. Просто определите функцию как f[x_?NumericQ, y_?NumericQ] := ..., если это так.

f[x_, y_] := Sin[x] Sin[y] - 1/2

refine[f_, pt0 : {x_, y_}] := 
  Module[{grad, t}, 
    grad = N[{Derivative[1, 0][f][x, y], Derivative[0, 1][f][x, y]}]; 
    pt0 + grad*t /. FindRoot[f @@ (pt0 + grad*t), {t, 0}]
  ]

points = Join @@ Cases[
   Normal@ContourPlot[f[x, y] == 0, {x, -3, 3}, {y, -3, 3}],
   Line[pts_] -> pts,
   Infinity
   ]

refine[f, #] & /@ points
6 голосов
/ 25 июля 2011

Небольшое отклонение для получения точек из ContourPlot (возможно, из-за Дэвида Парка):

pts = Cases[
   ContourPlot[Cos[x] + Cos[y] == 1/2, {x, 0, 4 Pi}, {y, 0, 4 Pi}], 
   x_GraphicsComplex :> First@x, Infinity];

или (в виде списка {x, y} точек)

ptsXY = Cases[
   Cases[ContourPlot[
     Cos[x] + Cos[y] == 1/2, {x, 0, 4 Pi}, {y, 0, 4 Pi}], 
    x_GraphicsComplex :> First@x, Infinity], {x_, y_}, Infinity];

Редактировать

Как обсуждено здесь , статья 1015 * Пола Эбботта в Mathematica Journal (Поиск корней в интервале ) дает следующие два альтернативных метода для получения списка значений {x, y} из ContourPlot, включая (!)

ContourPlot[...][[1, 1]]

Для приведенного выше примера

ptsXY2 = ContourPlot[
    Cos[x] + Cos[y] == 1/2, {x, 0, 4 Pi}, {y, 0, 4 Pi}][[1, 1]];

и

ptsXY3 = Cases[
   Normal@ContourPlot[
     Cos[x] + Cos[y] == 1/2, {x, 0, 4 Pi}, {y, 0, 4 Pi}], 
   Line[{x__}] :> x, Infinity];

, где

ptsXY2 == ptsXY == ptsXY3
...