Mathematica:如何获取由绘图命令绘制的数据点?

13
使用Plot命令绘制函数时,我想要获得所绘制的数据点集合。 例如,在以下简单示例中,我应该如何获取Plot使用的点列表{t,f}?
f = Sin[t]
Plot[f, {t, 0, 10}]

我尝试使用一种向列表中添加值的方法,该方法在Jerry B. Keiper的《Mathematica数值计算》第4页(Numerical1.ps)中展示,链接地址为http://library.wolfram.com/infocenter/Conferences/4687/

f = Sin[t]
flist={}
Plot[f, {t, 0, 10}, AppendTo[flist,{t,f[t]}]]

无论我尝试什么方法,都会生成错误信息。
非常感谢您的任何建议。

错误信息非常易懂。在Plot函数中使用AppendTo作为第三个参数是不支持的语法。我刚刚查看了你提到的19年前的论文(时间过得真快,如果你玩得开心),它使用了类似于我下面使用的复合语法。 - Sjoerd C. de Vries
这个问题是重复的吗?我知道之前有人问过,而且我记得在StackOverflow上看到过,但现在找不到了。 - Mr.Wizard
@Mr. Wizard。也许可以在这里找到答案:https://dev59.com/zVPTa4cB1Zd3GeqPj38U? - 681234
@TomD 我不相信那是我所记得的。可能是一条MathGroup消息。 - Mr.Wizard
7个回答

18
f = Sin[t];
plot = Plot[f, {t, 0, 10}]

提取点的一种方法如下:

points = Cases[
   Cases[InputForm[plot], Line[___], 
    Infinity], {_?NumericQ, _?NumericQ}, Infinity];

ListPlot 可以让我们来“看看”数据

ListPlot[points]

给出以下内容: enter image description here 编辑: Brett Champion 指出 InputForm 是多余的。
ListPlot@Cases[
  Cases[plot, Line[___], Infinity], {_?NumericQ, _?NumericQ}, 
  Infinity]

会起作用。

也可以将绘图粘贴到文档中,有时候这很有用。比如说,我创建了一个外部数据的ListPlot,然后不小心把数据文件弄丢了(只能访问生成的图形),我可以通过选择图形单元格括号、复制和粘贴来重新生成数据:

ListPlot@Transpose[{Range[10], 4 Range[10]}]

points = Cases[
  Cases[** Paste_Grphic _Here **, Point[___], 
   Infinity], {_?NumericQ, _?NumericQ}, Infinity] 

编辑2.

我还应该交叉参考并承认Yaroslav Bulatov的这个非常好的答案。

编辑3

Brett Champion不仅指出FullForm是多余的,而且在生成GraphicsComplex的情况下,应用Normal将把复合体转换为基元。这可以非常有用。

例如:

lp = ListPlot[Transpose[{Range[10], Range[10]}], 
  Filling -> Bottom]; Cases[
 Cases[Normal@lp, Point[___], 
  Infinity], {_?NumericQ, _?NumericQ}, Infinity] 

给出正确答案:

{{1.,1.},{2.,2.},{3.,3.},{4.,4.},{5.,5.},{6.,6.},{7.,7.},{8.,8.},{9.,9.},{10.,10.}}

感谢Brett Champion。

最后,这个答案中提供的通用方法更加简洁易懂,我在这里找到了它。

将原问题转换为ListPlot的形式,可以按照以下方式实现:

ListPlot@Cases[g, x_Line :> First@x, Infinity]

编辑4

更简单

ListPlot@Cases[plot, Line[{x__}] -> x, Infinity]

或者

ListPlot@Cases[** Paste_Grphic _Here **, Line[{x__}] -> x, Infinity]

或者

ListPlot@plot[[1, 1, 3, 2, 1]]

这个表达式的值为True

plot[[1, 1, 3, 2, 1]] == Cases[plot, Line[{x__}] -> x, Infinity]

3
如果您无法控制绘图的源头,InputForm是不必要的,但可以用Normal替换它。例如,如果添加了Filling选项,则绘图将包含GraphicsComplex,而点将只是全局点集的整数引用。请注意保持原文意思不变,并让翻译更具通俗易懂性。 - Brett Champion
@Brett Champion 谢谢!我从未意识到 InputForm 是多余的。我已经进行了编辑。通常在提取数据之前,我会先“看一眼”(通常会有一个策略建议)。您能否发布一个示例,其中 GraphicsComplex 将给出整数引用?例如,f = Sin[t]; plotfill = Plot[f, {t, 0, 10}, Filling -> Axis]; ListPlot@Cases[ Cases[plot, GraphicsComplex[___], Infinity], {_?NumericQ, _?NumericQ}, Infinity] 将起作用。感谢您的评论。我经常使用这种方法。 - 681234
2
ListPlot[{1, 2}, Filling -> Bottom] 包含以下 GraphicsComplex: GraphicsComplex[{{1., 1.}, {2., 2.}, {1., 0.}, {2., 0.}, {1., 1.}, {2., 2.}}, {{{}, {}, {}, {}, {Directive[{Opacity[0.2], Hue[0.67, 0.6, 0.6]}], Line[{3, 1}], Line[{4, 2}]}}, {{}, {Hue[0.67, 0.6, 0.6], Point[{5, 6}]}, {}}}]。其中的 Line[{4,2}] 表示我们从第四个点 ({2., 0.}) 到第二个点 ({2., 2.}) 绘制一条线。 - Brett Champion
@Brett Champion 再次感谢!很抱歉一开始我没有理解您关于Normal的观点,以及(从GraphicsComplex的帮助中了解到)它如何用于将GraphicsComplex分割成基元。Cases[Cases[Normal@ListPlot[{1, 2}, Filling -> Bottom], Point[___], Infinity], {_?NumericQ, _?NumericQ}, Infinity]将从您给出的示例中提取(两个)数据点。省略它或替换为FullForm都不起作用。这是一个好消息,再次感谢! - 681234

14

一种方法是使用EvaluationMonitor选项与ReapSow结合使用,例如:

In[4]:= 
(points = Reap[Plot[Sin[x],{x,0,4Pi},EvaluationMonitor:>Sow[{x,Sin[x]}]]][[2,1]])//Short

Out[4]//Short= {{2.56457*10^-7,2.56457*10^-7},<<699>>,{12.5621,-<<21>>}}

4
对于任何无法使用EvaluationMonitor的情况,您可以使用复合语句,例如:{plot, {points}} = Reap[Plot[Sow[x]; Sin[x], {x, 0, 4 Pi}]];。请注意,在这种特殊情况下,应该丢弃前两个点,因为它们不是数值评估循环的一部分。 - Sjoerd C. de Vries
1
@Sjoerd 我同意。事实上,我在这篇文章中使用了这种方法:https://dev59.com/3m445IYBdhLWcg3w3Nsf#4668972。但是,由于我认为 OP 已经看到了那个答案,并且由于 EvaluationMonitor 可以避免这一步骤,在可用时可能更加 "清洁",所以我在这里使用了它。 - Leonid Shifrin
EvaluationMonitor:>Sow[{x,Sin[x]}]需要在每个点上对目标函数进行额外的评估。请参见我的答案,了解更高效的解决方案(类似于@Sjoerd的但不同)。 - Alexey Popkov
你可以使用 Plot[y=Sin[x]...EvaluationMonitor:>Sow[{x,y}]] - agentp

10

除了Leonid的回答和我的跟进评论中提到的方法外,为了实时跟踪缓慢函数绘图的进度以查看正在发生什么,您可以执行以下操作(使用最近问题的示例):

(* CPU intensive function *)
LogNormalStableCDF[{alpha_, beta_, gamma_, sigma_, delta_}, x_] :=
 Block[{u},
  NExpectation[
   CDF[StableDistribution[alpha, beta, gamma, sigma], (x - delta)/u], 
   u \[Distributed] LogNormalDistribution[Log[gamma], sigma]]]

(* real time tracking of plot process *)
res = {};
ListLinePlot[res // Sort, Mesh -> All] // Dynamic

Plot[(AppendTo[res, {x, #}]; #) &@
  LogNormalStableCDF[{1.5, 1, 1, 0.5, 1}, x], {x, -4, 6}, 
 PlotRange -> All, PlotPoints -> 10, MaxRecursion -> 4]

输入图像描述

输入图像描述

输入图像描述

等等。


在我看来,这是一种非常好的监视绘图进度的方式。赞! - Dr. belisarius

6
这里有一种非常高效的方法来获取所有数据点:
{plot, {points}} = Reap @ Plot[Last@Sow@{x, Sin[x]}, {x, 0, 4 Pi}]

2
或者,我们可以通过更复杂的 Reap 语法来使 Plot 内部的部分和赋值更加简洁:{plot, points} = Reap[ Plot[Sow[Sin@x, x], {x, 0, 4 Pi}], _, {#, #2[[1]]} &] - Mr.Wizard
@Mr. 这是一个非常有启发性的Reap第三个参数使用示例,谢谢! - Alexey Popkov

4

根据Sjoerd C. de Vries的回答,我现在编写了以下代码,用于自动化绘图预览(在Mathematica 8上进行了测试):

pairs[x_, y_List]:={x, #}& /@ y
pairs[x_, y_]:={x, y}
condtranspose[x:{{_List ..}..}]:=Transpose @ x
condtranspose[x_]:=x
Protect[SaveData]
MonitorPlot[f_, range_, options: OptionsPattern[]]:=
  Module[{data={}, plot},
    Module[{tmp=#},
      If[FilterRules[{options},SaveData]!={},
        ReleaseHold[Hold[SaveData=condtranspose[data]]/.FilterRules[{options},SaveData]];tmp]]&@
    Monitor[Plot[(data=Union[data, {pairs[range[[1]], #]}]; #)& @ f, range,
                 Evaluate[FilterRules[{options}, Options[Plot]]]],
      plot=ListLinePlot[condtranspose[data], Mesh->All,
      FilterRules[{options}, Options[ListLinePlot]]];
      Show[plot, Module[{yrange=Options[plot, PlotRange][[1,2,2]]},
        Graphics[Line[{{range[[1]], yrange[[1]]}, {range[[1]], yrange[[2]]}}]]]]]]
SetAttributes[MonitorPlot, HoldAll]

除了展示情节的进展,它还标记了当前计算的x位置。

主要问题是对于多个图形,Mathematica应用相同的绘图样式来绘制最终图中的所有曲线(有趣的是,在临时图中它不会这样做)。

要将生成的数据保存到变量dest中,请使用选项SaveData:>dest


1
非常好!+1。使用示例:MonitorPlot[Pause[.08];Sin[x],{x,0,Pi},PlotRange->{{0,Pi},Full}] - Alexey Popkov
1
相关:"Mathematica中Plot[]的工作原理。" - Alexey Popkov

3

可能是另一种方式,具体实现取决于不同的情况:

ListPlot@Flatten[
            Plot[Tan@t, {t, 0, 10}] /. Graphics[{{___, {_, y__}}}, ___] -> {y} /. Line -> List
         , 2]

enter image description here


我想重写你对https://dev59.com/NXA85IYBdhLWcg3wCfBZ的回答,可以吗?还是我应该发表一个单独的答案? - Mr.Wizard
@Mr. 代码高尔夫问题的常规程序是,如果您的修改只是一个小更新并使用相同的方法,则编辑现有答案;如果您提出了不同的方法,则添加新答案。请根据此自由决定。 - Dr. belisarius

3

只需查看绘图结构(对于不同类型的绘图,结构会略有不同),并使用类似以下内容的代码:

plt = Plot[Sin[x], {x, 0, 1}];
lstpoint = plt[[1, 1, 3, 2, 1]];

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接