加速Matplotlib?

17

我曾在这里读到,matplotlib擅长处理大型数据集。我正在编写一个数据处理应用程序,并将matplotlib的图表嵌入到wx中。但是,我发现matplotlib在处理大量数据方面非常糟糕,无论是速度还是内存方面都表现不佳。除了对输入进行下采样之外,是否有人知道加速(减少内存占用)matplotlib的方法?

为了说明matplotlib内存使用有多么糟糕,请看这段代码:

import pylab
import numpy
a = numpy.arange(int(1e7)) # only 10,000,000 32-bit integers (~40 Mb in memory)
# watch your system memory now...
pylab.plot(a) # this uses over 230 ADDITIONAL Mb of memory

8
我一直在进行下采样。为什么你需要在图表上尝试呈现1000万个点? - Paul
1
Matplotlib是缓慢的。这是已知的事实。对于Qt,我使用guiqwt包,也许对于wx来说有类似的东西。 - tillsten
2
@Paul 我只是想让用户可以通过图形方式轻松地探索数据。也就是说,当他们缩放时,我不想根据他们的缩放边界重新采样,无论他们如何缩放/平移,他们都能看到实际数据。 - David Morton
3
我的测试结果并未显示出点比线更快或占用更少的内存。 :( - David Morton
与哪个软件包相比,"Terrible" 在处理大量数据方面表现很差?还有什么绘图软件包可以自动下采样? - Pete
显示剩余5条评论
3个回答

7

降采样是一个很好的解决方案--在matplotlib中绘制1000万个点会消耗大量的内存和时间。如果你知道可以接受多少内存,那么你可以基于这个数量进行降采样。例如,假设100万个点需要额外23MB的内存,并且你认为这个空间和时间上都是可以接受的,因此你应该进行降采样,使其始终低于100万个点:

if(len(a) > 1M):
   a = scipy.signal.decimate(a, int(len(a)/1M)+1)
pylab.plot(a)

或者类似于上面的代码片段(上面的代码可能会对您的喜好进行过度降采样)。

2
一个简单的抽样是不够的,根据我所知,Matplotlib 内部也是这样处理的。我不想只进行抽样,因为每个抽样间隔里的极端值会丢失。如果在某个区间内有一个突然的尖峰,除非你对区间很幸运,否则你根本看不到它。我写了一些代码,更加智能地进行了处理,选取每个抽样区间的极值而不是区间中心(或边缘)的值。尽管如此,我还是接受了您的答案,因为原则上这就是我的做法。 - David Morton
6
David - 如果你以更聪明的方式解决了这个问题,你介意分享一下吗?你可以将自己的答案标记为“已解决”,并可能会获得一些赞成票... - danodonovan

2

我对极值也很感兴趣,因此在绘制大量数据之前,我会按照以下方式进行处理:

import numpy as np

s = np.random.normal(size=(1e7,))
decimation_factor = 10 
s = np.max(s.reshape(-1,decimation_factor),axis=1)

# To check the final size
s.shape

当然,np.max只是一个极端计算函数的示例。
附注: 使用numpy的“跨度技巧”,应该可以在重新形状时避免复制数据。

2
我对保留采样地块记录中的一侧很感兴趣,因此想到了以下方法: (downsample 是我的第一次尝试)
def downsample(x, y, target_length=1000, preserve_ends=0):
    assert len(x.shape) == 1
    assert len(y.shape) == 1
    data = np.vstack((x, y))
    if preserve_ends > 0:
        l, data, r = np.split(data, (preserve_ends, -preserve_ends), axis=1)
    interval = int(data.shape[1] / target_length) + 1
    data = data[:, ::interval]
    if preserve_ends > 0:
        data = np.concatenate([l, data, r], axis=1)
    return data[0, :], data[1, :]

def geom_ind(stop, num=50):
    geo_num = num
    ind = np.geomspace(1, stop, dtype=int, num=geo_num)
    while len(set(ind)) < num - 1:
        geo_num += 1
        ind = np.geomspace(1, stop, dtype=int, num=geo_num)
    return np.sort(list(set(ind) | {0}))

def log_downsample(x, y, target_length=1000, flip=False):
    assert len(x.shape) == 1
    assert len(y.shape) == 1
    data = np.vstack((x, y))
    if flip:
        data = np.fliplr(data)
    data = data[:, geom_ind(data.shape[1], num=target_length)]
    if flip:
        data = np.fliplr(data)
    return data[0, :], data[1, :]

"最初的回答" 翻译成英文是 "Original Answer"。这句话的意思是:这使我能够更好地保留情节的一面。
newx, newy = downsample(x, y, target_length=1000, preserve_ends=50)
newlogx, newlogy = log_downsample(x, y, target_length=1000)
f = plt.figure()
plt.gca().set_yscale("log")
plt.step(x, y, label="original")
plt.step(newx, newy, label="downsample")
plt.step(newlogx, newlogy, label="log_downsample")
plt.legend()

test


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