加速ListLinePlot

8
我正在尝试使用Mathematica分析一些原始数据。我希望能够使用Manipulate和ListLinePlot动态显示我感兴趣的数据范围,但绘图速度非常慢。如何加快速度?
以下是一些额外的细节。一个外部文本文件存储原始数据:第一列是时间戳,第二、三、四列是数据读数,例如:
1309555993069, -2.369941, 6.129157, 6.823794
1309555993122, -2.260978, 6.170018, 7.014479
1309555993183, -2.070293, 6.129157, 6.823794
1309555993242, -1.988571, 6.238119, 7.123442

一个数据文件最多包含2·106行。例如,要显示第二列,我使用以下代码:

x = Import["path/to/datafile"];
ListLinePlot[x[[All, {1, 2}]]]

这个操作的执行时间太长了。为了显示一个变量数据范围,我尝试使用了Manipulate:

Manipulate[ListLinePlot[Take[x, numrows][[All, {1, 2}]]], {numrows, 1, Length[x]}]

这个指令可以工作,但是当我尝试显示超过几千行的数据时,它会变得非常缓慢。如何加速它呢?

以下是一些额外的细节:

  • MATLAB 在同一台电脑上以几乎瞬间的速度显示相同数量的数据,因此原始数据大小不应该是问题。
  • 我已经尝试关闭图形抗锯齿,但它并没有影响渲染速度。
  • 使用 DataRange 来避免使用 Take 没有帮助。
  • 使用 MaxPlotPoints 会使图形扭曲得太厉害,无法使用。
  • Manipulate 中不使用 Take 没有帮助。
  • 渲染似乎花费了大量时间。运行 Timing[ListLinePlot[Take[x,100000][[All, {1, 2}]]]] 返回 0.33:这意味着单独评估 Take 几乎是瞬间完成的,是绘图渲染导致了一切变慢。
  • 我正在使用 Ubuntu Linux 11.10 上的 Mathematica,使用 fglrx 驱动程序。强制 Mathematica 使用 mesa 驱动程序没有帮助。

有什么提示吗?

4个回答

12

如果您的目标仅仅是快速而正确地可视化您的数据,您可以使用以下技巧,这是我经常使用的方法。

我将数据分成若干块,大致对应于我的屏幕分辨率(通常为1000或更少),因为无论如何也不能显示更多的细节。然后我确定每个块的最小值和最大值,并从最小值到最大值再到最小值的方式画出一条锯齿形线条...结果将看起来与原始数据完全相同。然而,您无法“缩放”,因为这样会看到锯齿状线条(例如在导出为高分辨率PDF时)。这时您需要使用更多的块。

rv = RandomVariate[ExponentialDistribution[2], 100000];

ListLinePlot[rv, PlotRange -> All] (* original, slow *)
ListLinePlot[rv, PlotRange -> All, MaxPlotPoints -> 1000] (* fast but distorted *)

numberOfBlocks = 1000;

ListLinePlot[Riffle @@ Through[{Min /@ # &, Max /@ # &}[
   Partition[rv,Floor[Length[rv]/numberOfBlocks]]]], PlotRange -> All]
你可以添加DataRange->{...}选项来适当标记x轴。希望这可以帮到你!
编辑: 还可以参考Mathematica Stackexchange上的类似问题: https://mathematica.stackexchange.com/q/140/58

很好——我在7月26日回答时没有看到这个。 - Szabolcs

7

我没有在我的机器上进行广泛测试(我有一台Mac,所以不能排除Linux特定的问题)。但是我想到了几个要点。对于我来说,以下操作非常快,但显然比数据集较小时慢。您正在绘制数十万个数据点。

data = Accumulate@RandomVariate[NormalDistribution[], 200000];
Manipulate[ListLinePlot[Take[data, n]], {n, 1, Length[data]}]
  1. Manipulate 中,您可以使用 Take 来任意变化显示的数据量。尝试仅在每 100 个点左右递增 numrows,以减少渲染。
  2. 尝试使用 ContinuousAction->False 选项(请参阅文档)(我看到 @Szabolcs 在我打字时也有同样的想法)。
  3. 我本来想建议使用 MaxPlotPoints,但是请尝试使用 PerformanceGoal ->"Speed" 选项(请参阅文档)。

使用合理的MaxPlotPoints值和使用PerformanceGoal解决了问题。显示效果仍然比MATLAB差,但比以前好得多,谢谢! - Giuseppe Cardone
@giuseppe,你使用了多少MaxPlotPoints的值?我觉得你图像的像素数(ImageSize)或屏幕上的像素数是一个不错的选择。在1000时,Verbeia的数据绘图非常快。它出现在我的屏幕上,甚至比我手指离开回车键还要快。质量非常好。 - Sjoerd C. de Vries
@Sjoerd,这就是我在另一条评论中所说的,但实际上,如果数据波动非常强烈,那么无论像素宽度如何,减少MaxPlotPoints都可能会对外观产生影响。 另一方面,对于如此强烈的波动,人们在小图上看不到太多东西。 - Szabolcs
@Sjoerd,我想他只是指Matlab可以更快地呈现完整(而不是缩小)的数据。实际上,除了抗锯齿之外,渲染中可能没有任何显著的“质量”差异。至少我熟悉的Matlab版本不会进行抗锯齿处理。 - Szabolcs
1
@giuseppe,你总是可以使用 << Version5`Graphics` 恢复到版本6之前的图形行为,并且使用 ListPlot[data, PlotJoined->True],它可以为一百万个数据点提供非常快的渲染。但是这种方法有许多其他的缺陷和与版本6后的特性不兼容之处,所以很难让它变得有用。 - Szabolcs
显示剩余4条评论

5
我还注意到有时Mathematica渲染图形需要太长时间。实际上,必须是从Mathematica Graphics表达式到其他表示的某些翻译步骤需要时间,因为一旦渲染完成,调整大小(因此重新渲染)图形会更快。在许多示例中,版本6之前的图形渲染速度更快(但也缺少6+的许多功能)。
以下是您可以采取的一些措施:
  1. 使用ListLinePlotMaxPlotPoints选项在绘制之前减少数据量。如果下采样,则外观可能不会有任何区别。 Method选项应选择下采样算法,但我找不到任何文档(有人知道吗?)

  2. Manipulate中使用ContinuousAction -> False,停止它在拖动滑块时实时重新计算所有内容。


+1 只是因为提到了 MaxPlotPoints,这是我之前不知道的。 - acl
加一分,因为你比我先完成了ContinuousAction :) - Verbeia
非常抱歉我的问题没有表达清楚。ContinuousAction -> False 肯定会有所帮助,但即使对于 ListLinePlot[Take[x, 100000][[All, {1, 2}]]] 这样的绘图过程也非常缓慢,而且 MaxPlotPoints 会使绘图失真,无法使用(奇怪的是,即使我使用它,绘图时间也不会明显减少)。 - Giuseppe Cardone
1
你确定不能使用 MaxPlotPoints 吗?图形只有那么多像素宽(肯定远远小于10^6!),使用超过最大像素宽度的点毫无意义。 - Szabolcs
你说得对,我使用的MaxPlotPoints值太高了。与MATLAB相比,渲染质量较低且速度慢得多,但总体而言比以前好。我接受Verbeia的答案,因为他建议使用PerformanceGoal,这似乎有所帮助。感谢你的回答! - Giuseppe Cardone

5

这里有另一个想法,就是使用Ramer-Douglas-Peucker算法在绘图前减少数据点的数量。这样做很可能会更好地保留数据的形状。我不知道您是否仍需要此功能,因此我不会提供实现。


1
我使用Peucker算法编写了一个答案,并添加了一些有趣的动画。https://dev59.com/kJbfa4cB1Zd3GeqPtm5U#36937976 - Mark Setchell

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