具有约2000万个样本点和千兆字节数据的交互式大型图表

104

我有一个问题(关于我的内存):它无法容纳我想要绘制的数据。我确实有足够的硬盘空间。有没有办法避免我的数据集发生“阴影”现象?

具体来说,我正在处理数字信号处理,并且必须使用高采样率。我的框架(GNU Radio)将值保存为二进制数以节省磁盘空间。我解包它。然后需要绘图,我需要缩放和交互式的绘图。这是个问题。

是否存在优化此操作的可能性或者其他可以处理更大数据集的软件/编程语言(如R等)?实际上,我想在我的图表中添加更多数据。但我对其他软件没有经验。GNUplot尝试过了,类似以下方法也不行。我不懂R。

import matplotlib.pyplot as plt
import matplotlib.cbook as cbook
import struct

"""
plots a cfile

cfile - IEEE single-precision (4-byte) floats, IQ pairs, binary
txt - index,in-phase,quadrature in plaintext

note: directly plotting with numpy results into shadowed functions
"""

# unpacking the cfile dataset
def unpack_set(input_filename, output_filename):
    index = 0   # index of the samples
    output_filename = open(output_filename, 'wb')

    with open(input_filename, "rb") as f:

        byte = f.read(4)    # read 1. column of the vector

        while byte != "":
        # stored Bit Values
            floati = struct.unpack('f', byte)   # write value of 1. column to a variable
            byte = f.read(4)            # read 2. column of the vector
            floatq = struct.unpack('f', byte)   # write value of 2. column to a variable
            byte = f.read(4)            # next row of the vector and read 1. column
            # delimeter format for matplotlib 
            lines = ["%d," % index, format(floati), ",",  format(floatq), "\n"]
            output_filename.writelines(lines)
            index = index + 1
    output_filename.close
    return output_filename.name

# reformats output (precision configuration here)
def format(value):
    return "%.8f" % value            

# start
def main():

    # specify path
    unpacked_file = unpack_set("test01.cfile", "test01.txt")
    # pass file reference to matplotlib
    fname = str(unpacked_file)
    plt.plotfile(fname, cols=(0,1)) # index vs. in-phase

    # optional
    # plt.axes([0, 0.5, 0, 100000]) # for 100k samples
    plt.grid(True)
    plt.title("Signal-Diagram")
    plt.xlabel("Sample")
    plt.ylabel("In-Phase")

    plt.show();

if __name__ == "__main__":
    main()

类似 plt.swap_on_disk() 这样的函数可以将东西缓存到我的 SSD 上 ;)


“直接使用numpy绘图会导致阴影函数”这句话是什么意思? - jfs
2
我不明白你是如何得到“千兆字节”数据的。2000万 x (3 x (4字节)) = 240MB,对吧?而且@EOL是完全正确的——将所有这些完美的二进制数据转换为文本格式是完全浪费时间和I/O资源的,使用numpy直接访问二进制数据即可。 - Jonathan Dursi
奇怪,我没有看到你在任何地方存储floati和floatq?或者以“lines”开头的那一行是不是应该在while循环内部? - K.-Michael Aye
有趣的是,由于编辑时的6个非空格字符规则,我无法进行编辑。有时,只需要4个空格就足以使代码完全无法工作... ;) - K.-Michael Aye
6个回答

103

你的数据不是很大,而且你在绘制它时遇到问题,这表明工具存在问题。Matplotlib 有很多选项,输出也很好,但它会占用大量内存,并基本上假定你的数据很小。但是还有其他选择。

例如,我使用以下方式生成了一个包含2000万个数据点的文件'bigdata.bin':

#!/usr/bin/env python
import numpy
import scipy.io.numpyio

npts=20000000
filename='bigdata.bin'

def main():
    data = (numpy.random.uniform(0,1,(npts,3))).astype(numpy.float32)
    data[:,2] = 0.1*data[:,2]+numpy.exp(-((data[:,1]-0.5)**2.)/(0.25**2))
    fd = open(filename,'wb')
    scipy.io.numpyio.fwrite(fd,data.size,data)
    fd.close()

if __name__ == "__main__":
    main()

这将生成一个大约229MB大小的文件,虽然不是很大;但您已经表达了您想要更大的文件,因此最终会遇到内存限制。

首先让我们专注于非交互式绘图。首先要意识到的是,带有每个点的标志的矢量图将是一场灾难——对于其中的20 M个点,其中大多数都将重叠,尝试渲染小十字架、圆形或其他东西将是一场灾难,会生成巨大的文件并需花费大量时间。我认为这是matplotlib默认情况下出现问题的原因。

Gnuplot没有处理这个问题:

gnuplot> set term png
gnuplot> set output 'foo.png'
gnuplot> plot 'bigdata.bin' binary format="%3float32" using 2:3 with dots

gnuplot

即使是Matplotlib,只要小心使用(选择光栅后端,并使用像素标记点),也可以得到很好的效果:

#!/usr/bin/env python
import numpy
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt

datatype=[('index',numpy.float32), ('floati',numpy.float32), 
        ('floatq',numpy.float32)]
filename='bigdata.bin'

def main():
    data = numpy.memmap(filename, datatype, 'r') 
    plt.plot(data['floati'],data['floatq'],'r,')
    plt.grid(True)
    plt.title("Signal-Diagram")
    plt.xlabel("Sample")
    plt.ylabel("In-Phase")
    plt.savefig('foo2.png')

if __name__ == "__main__":
    main()  

matplotlib

如果您需要交互性,您将需要对数据进行分组��进行实时缩放以进行绘制。我不知道有哪些Python工具可以帮助您做到这一点。

另一方面,绘制大数据是一个相当常见的任务,有一些工具可以胜任这项工作。我的个人最爱是Paraview,另一个是VisIt。它们主要用于3D数据,但特别是Paraview也可用于2D,并且具有非常交互性(甚至还有Python脚本接口)。唯一的诀窍是将数据写入Paraview可以轻松读取的文件格式中。


3
很棒的帖子。+1 提到了 VisIt 和 ParaView,它们都是有用且强大的可视化程序,专门处理(非常!)大型数据集。请注意,VisIt 还具有 Python 脚本接口,并且可以绘制 1D 曲线,除了 2D 和 3D 图形之外。就文件格式而言,VTK 是两个程序都支持的相对简单的格式(实际上,ParaView 是基于 VTK 库构建的)。 - Chris
5
请查看Bokeh Datashader,它可以"将任何规模的数据转换成图像":https://github.com/bokeh/datashader - tommy.carstensen
3
感谢提到 Paraview 和 VisIt。它们都成功地涵盖了我在以下网址提到的 2D 应用案例:https://dev59.com/e2025IYBdhLWcg3wsIIU#55967461 - Ciro Santilli OurBigBook.com
我尝试使用paraview绘制一个包含约3百万行的csv文件的列。然而,我不知道如何使用线性图的缩放功能。虽然绘图速度非常快,但似乎这个图并不是真正的交互式的。请问有人可以帮忙吗? - Aisha
我试图在Paraview中绘制一列包含约300万行数据的csv文件。然而,我不太清楚如何使用线条图的缩放功能。虽然绘图速度很快,但似乎绘图并不真正具有交互性。有人能帮助吗? - Aisha

99

在Ubuntu上进行的一项拥有1000万个数据点散点图基准测试的开源交互式绘图软件调查

受到以下用例的启发:https://stats.stackexchange.com/questions/376361/how-to-find-the-sample-points-that-have-statistically-meaningful-large-outlier-r,我对几个绘图程序进行了基准测试,使用完全相同的输入文件。

基本上,我想要:

  • 绘制多维数据的XY散点图,希望以Z作为点的颜色
  • 通过鼠标交互地选择一些看起来有趣的点
  • 查看所选点的所有维度(至少包括X、Y和Z),以尝试理解它们为什么是XY散点图中的异常值

这个问题可以用以下简化的测试数据表示:

python -c 'for i in range(10000000): print(f"{i},{i*2},{i*4}")' > 10m1.csv
echo 5000000,20000000,-1 >> 10m1.csv
10m1.csv(约239 MB)的前几行如下所示:

10m1.csv

0,0,0
1,2,4
2,4,8
3,6,12
4,8,16

而最后一个,第一千万个,是个例外,看起来像这样:
5000000,20000000,-1

所以我们基本上有:

  • 一条斜率为2且有1000万个点的直线
  • 加上一个单独的离群点,在绘图的顶部中心之外

类似于:

Y

^
|
|
|       +       +
|
|             +
|
|           +
|
|         +
|
|       +
|
|     +
|
|   +
|
| +
|
+-------------------> X

这个基准的目标是在图表中找到点(5000000,20000000),然后确定第三列的值,我们的测试中为-1

当我最初写下这个答案时,我使用了生成的10.csv。

python -c 'for i in range(10000000): print(f"{i},{i*2},{i*4}")' > 10m.csv

没有异常值。虽然这测试了性能,但它并未测试选择能力,因此目标是在我有动力时将每个测试迁移到10m1.csv。

我还制作了一个包含10个数据点和异常值的示例,以便评估某些无法处理1000万数据点计数的工具的可用性:

i=0;
while [ "$i" -lt 10 ]; do
  echo "$i,$((2 * i)),$((4 * i))"; i=$((i + 1));
done > 11.csv
echo 5,20,-1 >> 11.csv

为了增加趣味性,我还准备了一个更大的10亿点数据集,以防任何一个程序能够处理1000万个点!CSV文件有点混乱,所以我转向了HDF5格式。
#!/usr/bin/env python3

import h5py
import numpy

size = 1000000000

with h5py.File('1b.hdf5', 'w') as f:
    x = numpy.arange(size + 1)
    x[size] =  size / 2
    f.create_dataset('x', data=x, dtype='int64')
    y = numpy.arange(size + 1) * 2
    y[size] =  3 * size / 2
    f.create_dataset('y', data=y, dtype='int64')
    z = numpy.arange(size + 1) * 4
    z[size] = -1
    f.create_dataset('z', data=z, dtype='int64')

这将生成一个类似于10m1.csv的约23 GiB文件,其中包含:

  • 类似于10m.csv的一条直线上的10亿个点
  • 图形中心顶部的一个异常点

我还要创建一个10m1.csv的SQLite版本,因为从实际应用角度来看,这可能是最合理的格式之一,它将允许进行良好理解的SQL查询、明确的索引控制和二进制数值数据。

f=10m.sqlite
rm -f "$f"
n=10000000
time sqlite3 "$f" 'create table t(x integer, y integer, z integer)'
time sqlite3 "$f" "insert into t select value as id, value as x, value * 2 as y, value * 2 as z from generate_series(0, $((n - 1)))"
time sqlite3 "$f" "INSERT INTO t VALUES (?, ?, ?)', ($((n/2)), $((3*n/2)), -1))"
time sqlite3 "$f" 'create index txy on t(x, y)'

我还用n = 10亿运行了那段代码,生成了一个1b.sqlite文件。到目前为止,generate_series是我找到的最快的插入方法:使用Python将大量数据批量插入SQLite
我按照(x, y)进行索引,因为这可能会加快查找工具在给定x-y矩形中获取所有点的查询速度。生成的10m1.sqlite文件约为367 MB,比CSV文件大,这是由于索引造成的。
除非在某个子部分中另有说明,测试是在Ubuntu 18.10上进行的,使用ThinkPad P51笔记本电脑,配备Intel Core i7-7820HQ CPU(4核/8线程),2x Samsung M471A2K43BB1-CRC RAM(2x 16GiB),NVIDIA Quadro M1200 4GB GDDR5 GPU。 结果摘要 根据我的非常特定的测试用例和我作为许多被评估软件的首次用户的经验,这是我观察到的情况:
它能处理1000万个点吗:
工具 处理10百万点吗? 功能丰富吗? 用户界面好用吗?
Vaex 是的,甚至可以处理10亿! 是的。 是的,Jupyter小部件
VisIt 是的,但不支持1亿 是的,2D和3D,专注于交互。
Paraview 与上述相同,可能稍微少一些2D功能。 非常好
Mayavi 是的 仅支持3D,良好的交互和脚本支持,但功能更有限。 可以
gnuplot 勉强支持非交互模式。 功能丰富,但在交互模式下受限。 可以
matplotlib 与上述相同。 可以
Bokeh 否,最多支持100万 是的,易于脚本编写。 非常好,Jupyter小部件
PyViz
seaborn
sqlitebrowser 可以可视化SQL查询结果 一般
Vaex 2.0.2

https://github.com/vaexio/vaex

按照以下链接安装并使hello world正常工作:如何在Vaex中进行交互式2D散点图缩放/点选择?

我测试了vaex,它可以处理高达10亿个数据点,真是太棒了!

它首先是“Python脚本为主”,这对于可重复性非常好,并且可以轻松地与其他Python工具进行交互。

Jupyter设置有一些复杂,但一旦我使用virtualenv运行起来,就感觉很棒。

要在Jupyter中加载我们的CSV文件,请运行以下命令:

import vaex
df = vaex.from_csv('10m.csv', names=['x', 'y', 'z'],)
df.plot_widget(df.x, df.y, backend='bqplot')

我们可以立即看到:

enter image description here

现在,我们可以用鼠标进行缩放、平移和选择点,更新速度非常快,全部完成不到10秒。这里我已经放大了一些个别的点,并且选中了其中几个(图像上有一个淡色矩形框)。

enter image description here

在使用鼠标进行选择后,这与使用df.select()方法具有完全相同的效果。因此,我们可以通过在Jupyter中运行以下代码来提取所选点:
df.to_pandas_df(selection=True)

输出数据格式为:
        x       y        z   index
0 4525460 9050920 18101840 4525460
1 4525461 9050922 18101844 4525461
2 4525462 9050924 18101848 4525462
3 4525463 9050926 18101852 4525463
4 4525464 9050928 18101856 4525464
5 4525465 9050930 18101860 4525465
6 4525466 9050932 18101864 4525466

自从10M点正常工作后,我决定尝试1B点...结果也很顺利!
import vaex
df = vaex.open('1b.hdf5')
df.plot_widget(df.x, df.y, backend='bqplot')

为了观察在原始图中看不见的异常值,我们可以参考如何在vaex交互式Jupyter bqplot plot_widget中更改点的样式以使单个点更大和可见?并使用以下方法:
df.plot_widget(df.x, df.y, f='log', shape=128, backend='bqplot')

生成的结果是:

enter image description here

选择了这个点之后:

enter image description here

我们获取了异常值的完整数据。
   x          y           z
0  500000000  1500000000  -1

这是创作者们使用更有趣的数据集和更多功能的演示:https://www.youtube.com/watch?v=2Tt0i823-ec&t=770 不幸的是,它没有内置的sqlite支持:https://github.com/vaexio/vaex/issues/864 在Ubuntu 19.04中进行了测试。 VisIt 2.13.3 网站:https://wci.llnl.gov/simulation/computer-codes/visit 许可证:BSD
劳伦斯利弗莫尔国家实验室开发,该实验室是一个国家核安全局的实验室,所以你可以想象如果我能让它工作,1000万个点对它来说不算什么。(书籍《超人:西摩·克雷的故事》(查尔斯·J·默里著,1997年)很好地展示了这些计算能力饥渴的实验室在建造第一颗氢弹时的情况,因为你不能随意进行核试验,即使你这样做了,你也无法真正测量你想要的结果,因为它爆炸得太快、太热:计算机模型是必需的。他们决定,一群物理学家的妻子带着计算器是不够的,就像早期洛斯阿拉莫斯裂变弹那样。当以色列购买了他们的一台计算机时,每个人都立刻认为这是为了制造核武器。)

安装:没有Debian软件包,只需从网站下载Linux二进制文件即可运行,无需安装。另请参阅:https://askubuntu.com/questions/966901/installing-visit

基于VTK,这是许多高性能图形软件使用的后端库。用C语言编写。

在与用户界面玩了3个小时后,我终于搞定了,并且它确实解决了我在此处详细描述的用例:https://stats.stackexchange.com/questions/376361/how-to-find-the-sample-points-that-have-statistically-meaningful-large-outlier-r

以下是它在本帖的测试数据上的外观:

enter image description here

一些选择和缩放功能。

enter image description here

这是选取窗口的位置。

enter image description here

就性能而言,VisIt表现得非常出色:每个图形操作要么只需要很短的时间,要么立即完成。当我需要等待时,它会显示一个“处理中”的消息,并显示剩余工作的百分比,GUI不会冻结。

由于10m个数据点运行得很好,我也尝试了1亿个数据点(一个2.7G的CSV文件),但不幸的是它崩溃/进入了奇怪的状态,我在htop中看到4个VisIt线程占用了我全部的16GiB RAM并且可能因为内存分配失败而死机。

初始入门过程有点痛苦:

  • 如果你不是核弹工程师,许多默认设置都感觉糟透了。例如:
    • 默认点大小为1像素(会与我的显示器上的灰尘混淆)
    • 坐标轴从0.0到1.0: 如何在Visit绘图程序上显示实际的坐标轴数值而不是0.0到1.0的分数?
    • 多窗口设置时,在选取数据点时会出现讨厌的多个弹出窗口
    • 显示您的用户名和绘图日期(通过"控制"->"注释"->"用户信息"中移除)
    • 自动定位的默认设置很糟糕:图例与坐标轴冲突,找不到标题自动化设置,所以我不得不手动添加一个标签并重新调整所有内容的位置
  • 这个软件拥有许多功能,所以很难找到你想要的功能
  • 这份手册非常有帮助,但它是一份386页的PDF巨兽,令人不安地标注着"2005年10月版本1.5"。我想知道他们是否用这个来开发Trinity 而实际上这是一份漂亮的Sphinx HTML手册,是我最初回答这个问题后创建的
  • 没有Ubuntu软件包,但预编译二进制文件可以直接使用。

我将这些问题归因于:

  • 它存在了很长时间,使用了一些过时的图形用户界面(GUI)思想
  • 你不能只是点击绘图元素来更改它们(例如坐标轴、标题等),而且功能很多,所以有点难找到你要找的那个

我也喜欢一些 LLNL 基础设施渗透到那个代码库中。例如,看看 docs/OfficeHours.txt 和该目录中的其他文件!对于 Brad 来说,他是“周一早上的人”真是遗憾!哦,还有答录机的密码是“Kill Ed”,别忘了。

Paraview 5.9.0

网站:https://www.paraview.org/

许可证:BSD

在 Ubuntu 20.10 上测试通过。

安装:

sudo apt install paraview

或者从网站上下载预编译版本获取最新版本。这就是我为了这篇评论所做的,因为apt只有5.7.0版本。我下载了ParaView-5.9.0-MPI-Linux-Python3.8-64bit.tar.gz
Kitware洛斯阿拉莫斯国家实验室开发,后来又加入了桑迪亚国家实验室(还有其他两个NNSA实验室),所以我们再次期望它能轻松处理数据。它也是基于VTK并用C++编写的,这更加令人期待。
然而,我感到失望:由于某种原因,1000万个点使得GUI非常缓慢和无响应,导致无法使用。每当我点击某些东西,比如隐藏线条,都要花费几十秒的时间。我认为在某个时候它出现了故障,并完全停止响应。

我对一个受控的、广告宣传充分的“我正在工作,请稍等片刻”的时刻感到满意,但是GUI在此期间冻结?这是不可接受的。

htop显示Paraview使用了8个线程和3GB的内存,所以CPU和内存都没有达到最大限制。

就GUI而言,Paraview非常漂亮和现代化,比VisIt好得多,只要不出现卡顿问题。

由于10m1.csv导致程序崩溃,我测试了11.csv来看看除了性能之外是否能解决我的问题,答案是肯定的:

  • paraview 11.csv
  • 从弹出窗口中选择CSV读取器
  • 在左侧应用属性
  • 在Pipeline Browser上右键单击CSV文件
  • 添加过滤器 > 按字母顺序 > 绘制数据。为什么绘图是一个过滤器?对于初次使用者来说不太直观,相关链接:paraview: 从CSV文件绘制数据我确定一旦你理解了过滤器的更多普遍化能做什么的概念,这些事情就会变得有意义,但还是有点费解。
  • 属性 > 应用
  • 取消选择"使用索引作为x轴"
  • X数组名称:Field 0
  • 系列参数移除Field 0和Field 2
  • 选择Field 1并且:
    • 线条样式:无
    • 标记样式:十字
    • 标记大小:根据需要增加或减小
  • 在图表上方点击"矩形选择(s)"图标
  • 选择异常值(点被突出显示)
  • 在图表过滤器中添加另一个过滤器:"提取选择"
  • 应用
最后终于!!!我得到了一个只包含选定异常值的表格,并显示"Field 2"的值为-1。

enter image description here

嗯,确实不是一帆风顺的过程,但我最终成功了。

另一个缺点是与VisIt相比,Paraview在功能上显得有些不足,例如:

Mayavi 4.6.2

网站:https://github.com/enthought/mayavi

开发者:Enthought

安装:

sudo apt-get install libvtk6-dev
python3 -m pip install -u mayavi PyQt5

Mayavi似乎非常专注于3D,我找不到如何在其中进行2D绘图,所以对于我的用例来说不太适合。
为了检查性能,我根据https://docs.enthought.com/mayavi/mayavi/auto/example_scatter_plot.html中的示例进行了调整,使用了1000万个点,运行得很好,没有出现延迟。
import numpy as np
from tvtk.api import tvtk
from mayavi.scripts import mayavi2

n = 10000000
pd = tvtk.PolyData()
pd.points = np.linspace((1,1,1),(n,n,n),n)
pd.verts = np.arange(n).reshape((-1, 1))
pd.point_data.scalars = np.arange(n)

@mayavi2.standalone
def main():
   from mayavi.sources.vtk_data_source import VTKDataSource
   from mayavi.modules.outline import Outline
   from mayavi.modules.surface import Surface
   mayavi.new_scene()
   d = VTKDataSource()
   d.data = pd
   mayavi.add_source(d)
   mayavi.add_module(Outline())
   s = Surface()
   mayavi.add_module(s)
   s.actor.property.trait_set(representation='p', point_size=1)
main()

输出:

enter image description here

我无法放大到足够的程度来看到个别点,近似3D平面太远了。也许有办法吗?

关于Mayavi的一个很酷的事情是开发人员在允许你从Python脚本中方便地启动和设置GUI方面投入了很多努力,就像Matplotlib和gnuplot一样。似乎在Paraview中也可能实现这一点,但至少文档不如其他两者好。

总体而言,Mayavi似乎没有VisIt/Paraview那样丰富的功能。例如,我无法直接从GUI加载CSV:如何从Mayavi GUI加载CSV文件?

Gnuplot 5.2.2

网站:http://www.gnuplot.info/

当我需要快速简单操作时,gnuplot确实很方便,而且我总是尝试它。

安装:

sudo apt-get install gnuplot

对于非交互式使用,它可以相当好地处理1000万个点:
#!/usr/bin/env gnuplot
set terminal png size 1024,1024
set output "gnuplot.png"
set key off
set datafile separator ","
plot "10m1.csv" using 1:2:3:3 with labels point

完成于7秒钟:

enter image description here

但是如果我试着与之互动
#!/usr/bin/env gnuplot
set terminal wxt size 1024,1024
set key off
set datafile separator ","
plot "10m.csv" using 1:2:3 palette

并且:

gnuplot -persist main.gnuplot

然后初始渲染和缩放感觉太慢了。我甚至看不到矩形选择线!
另外请注意,对于我的使用情况,我需要使用超文本标签,如:
plot "10m.csv" using 1:2:3 with labels hypertext

但是标签功能存在性能问题,包括非交互式渲染。不过我已经报告了这个问题,Ethan在一天内解决了它:https://groups.google.com/forum/#!topic/comp.graphics.apps.gnuplot/qpL8aJIi9ZE 然而,我必须说有一个合理的解决方法可以选择异常值:只需为所有点添加带有行ID的标签!如果附近有很多点,你可能无法阅读标签。但对于你关心的异常值,你可能会成功!例如,如果我在原始数据中添加一个异常值:
cp 10m.csv 10m1.csv
printf '2500000,10000000,40000000\n' >> 10m1.csv

修改绘图命令为:
#!/usr/bin/env gnuplot
set terminal png size 1024,1024
set output "gnuplot.png"
set key off
set datafile separator ","
plot "10.csv" using 1:2:3:3 palette with labels

这个修复显著减慢了绘图速度(在上述修复之后40分钟!!!),但产生了一个合理的输出:

enter image description here

所以通过一些数据过滤,我们最终会达到目标。
Matplotlib 1.5.1,numpy 1.11.1,Python 3.6.7
网站:https://matplotlib.org/ 当我的gnuplot脚本变得太疯狂时,我通常尝试使用Matplotlib。
仅仅numpy.loadtxt就花费了大约10秒钟,所以我知道这不会顺利进行。
#!/usr/bin/env python3

import numpy
import matplotlib.pyplot as plt

x, y, z = numpy.loadtxt('10m.csv', delimiter=',', unpack=True)
plt.figure(figsize=(8, 8), dpi=128)
plt.scatter(x, y, c=z)
# Non-interactive.
#plt.savefig('matplotlib.png')
# Interactive.
plt.show()

首先,非交互式尝试产生了良好的输出,但耗时3分钟55秒...

然后,交互式尝试在初始渲染和缩放上花费了很长时间。无法使用:

enter image description here

请注意这个截图上的问题,应该立即缩放并消失的缩放选择却在屏幕上停留了很长时间,等待缩放计算完成!
为了让交互版本正常工作,我不得不注释掉plt.figure(figsize=(8, 8), dpi=128),否则会出现以下错误:
RuntimeError: In set_size: Could not set the fontsize

Bokeh 1.3.1

https://github.com/bokeh/bokeh

Ubuntu 19.04 安装:
python3 -m pip install bokeh

然后启动Jupyter:
jupyter notebook

现在,如果我绘制1百万个点,一切都运行得非常完美,界面非常棒且快速,包括缩放和悬停信息。
from bokeh.io import output_notebook, show
from bokeh.models import HoverTool
from bokeh.transform import linear_cmap
from bokeh.plotting import figure
from bokeh.models import ColumnDataSource
import numpy as np

output_notebook()
N = 1000000
source = ColumnDataSource(data=dict(
    x=np.random.random(size=N) * N,
    y=np.random.random(size=N) * N,
    z=np.random.random(size=N)
))
hover = HoverTool(tooltips=[("z", "@z")])
p = figure()
p.add_tools(hover)
p.circle(
    'x',
    'y',
    source=source,
    color=linear_cmap('z', 'Viridis256', 0, 1.0),
    size=5
)
show(p)

初始视图:

enter image description here

在一个缩放后:

enter image description here

如果我提升到10米,它就会出问题,htop显示Chromium有8个线程占用了我所有的内存,并处于不可中断的IO状态。

这是关于引用以下要点的问题:如何引用选择的Bokeh数据点

PyViz

https://pyviz.org/

待办事项:评估。

集成Bokeh + datashader + 其他工具。

视频演示10亿数据点:https://www.youtube.com/watch?v=k27MJJLJNT4 "PyViz: 使用30行Python代码可视化10亿数据点的仪表盘" 由 "Anaconda, Inc." 发布于2018-04-17。

seaborn

https://seaborn.pydata.org/

待办事项:评估。

已经有一个关于如何使用seaborn可视化至少5000万行的问答(QA)了。

sqlitebrowser 3.12.2

https://github.com/sqlitebrowser/sqlitebrowser

我试了一下,看看它能否处理10m1.sqlite,但不幸的是它不能。真可惜!
不过,它可以直接绘制查询结果,这还是挺酷的。
以下是它的外观:

enter image description here

在这张图片中,我将10m1.sqlite加载到工具中,然后开始浏览数据。
但它只绘制了用于浏览的数据。
您可以点击绘图下方右侧的按钮“加载所有数据并重新绘制图表”,但这会打开一个进度条,每3秒增加1%,所以前景不太乐观,我放弃了。
在Ubuntu 23.04上进行了测试。 SQL直方图查询 我想知道为什么我很难找到一个使用此作为后端的交互式UI工具。在索引数据库上使用SQL直方图感觉是最合理的方法。例如,使用10个步骤并忽略空箱:
div=10
x=0
y=0
x2=10000000
y2=20000000

dx=$(((x2 - x) / div))
dy=$(((y2 - y) / div))
time sqlite3 10m1.sqlite --cmd '.mode csv' <<EOF
select
  floor(x/$dx)*$dx as x,
  floor(y/$dy)*$dy as y,
  count(*) as cnt
from t
where
  x >= $x and x < $x2 and
  y >= $y and y < $y2
group by 1, 2
order by 1, 2
EOF

我们到达:
0,0,1000000
1000000,2000000,1000000
2000000,4000000,1000000
3000000,6000000,1000000
4000000,8000000,1000000
5000000,10000000,1000000
5000000,20000000,1
6000000,12000000,1000000
7000000,14000000,1000000
8000000,16000000,1000000
9000000,18000000,1000000

查询需要6秒钟,所以它可以处理大约10百万个点,还算可以,但无法扩展到10亿个点。
既然我们只有一个点,那么我们可以在该范围内进行完整的列表。
x=5000000
y=20000000
x2=6000000
y2=40000000
time sqlite3 10m1.sqlite --cmd '.mode csv' <<EOF
select *
from t
where
  x >= $x and x < $x2 and
  y >= $y and y < $y2
order by x, y
EOF

立即得出最终所需的:

5000000,20000000,-1

所以,这个GUI可以有一个点的最大限制,其中:

  • 如果超过了限制,使用热力图
  • 否则,在该区间内查询完整的单个点,并在图中绘制单个点

如何将SQL扩展到10亿行:R-tree索引

为了扩展到10亿行,我们需要r-tree/空间索引,它们允许我们高效地对多列进行不等式操作。SQLite拥有这些功能,但使用起来有点麻烦:

尽管存在这些限制,我还是进行了一个亿级点测试,使用了重复的x/y列和以下创建时间:30分钟,文件大小:5.9 GB

然后进行了一个有上限的计数扫描:

max=100
div=10
x=0
y=0
x2=100000000
y2=200000000
dx=$(((x2 - x) / div))
dy=$(((y2 - y) / div))

cx=0
while [ $cx -lt $x2 ]; do
  cy=0
  while [ $cy -lt $y2 ]; do
    printf "$cx,$cy,"
    sqlite3 100mr.sqlite --cmd '.mode csv' <<EOF
select count(x) from (
  select x from t
  where
    x >= $cx and x < $((cx + dx)) and
    y >= $cy and y < $((cy + dy))
  limit $max
)
EOF
    cy=$((cy+dy))
  done
  cx=$((cx+dx))
done

仅用0.2秒完成,令人惊叹。如果不考虑极长的生成时间,它可能会扩展到10亿。

不幸的是,PostgreSQL索引创建速度也没有更快:如何将使用SQLite R树的简单空间索引移植到Postgres? 不过至少它支持点而不仅限于矩形。

在Ubuntu 23.04上进行了测试。


17

一个更为新的项目在处理大数据集时具有巨大潜力: Bokeh,它是以此为目标创建的。

实际上,仅将在绘图尺度下相关的数据发送到显示后端。这种方法比Matplotlib的方法要快得多。


4
您可以访问http://go.continuum.io/visualizing-billions-data-points/了解Datashader(和Bokeh!)的相关内容。 - scls

14

你可以优化读取文件的速度:你可以直接将其读入NumPy数组中,以便利用NumPy的高速性能。你有几个选择。如果内存是问题,你可以使用memmap,它将大部分文件保留在磁盘上(而不是在内存中):

# Each data point is a sequence of three 32-bit floats:
data = np.memmap(filename, mode='r', dtype=[('index', 'float32'), ('floati','float32'), ('floatq', 'float32')])
如果RAM不是问题,您可以使用 fromfile 将整个数组放入RAM中:

请注意原文中的HTML标签不要更改。

data = np.fromfile(filename, dtype=[('index', 'float32'), ('floati','float32'), ('floatq', 'float32')])

然后可以使用Matplotlib的常规plot(*data)函数进行绘图,可能需要通过另一种解决方案提出的"放大"方法来实现。


1
如果您想创建一个结构化的numpy数组并以只读方式打开文件,则可以使用以下代码:np.memmap(filename, mode='r', dtype=[('floati','f'), ('floatq', 'f')]) - jfs
从一百万个双精度浮点数开始,我在任何我尝试的后端中都会出现Agg溢出,即使使用path.simplify=True也是如此。因此,我不相信使用Matplotlib可以“轻而易举”地完成这个任务。 - K.-Michael Aye
有趣。正如Jonathan Dursi的回答所提到的,使用Matplotlib可以实现2000万个点,但有一些限制(如光栅输出等)。 - Eric O. Lebigot

8
我建议使用稍微复杂一些但应该会起作用的方法:按不同的分辨率和范围构建您的图形。
例如,想象一下谷歌地球。如果您将缩放最大级别以覆盖整个地球,则分辨率最低。当您缩放时,图片会更改为更详细的图片,但仅限于您缩放的区域。
因此,基本上对于您的绘图(是2D还是3D?我假设它是2D),我建议您构建一个大的图形,它涵盖整个[0,n]范围,具有较低的分辨率,两个小图形涵盖[0,n / 2]和[n / 2 +1,n],其分辨率是大图形的两倍,4个小图形覆盖[0,n / 4] ...... [3 * n / 4 +1,n],其分辨率是前面两个的两倍,以此类推。 不确定我的解释是否很清楚。此外,我不知道任何现有的绘图程序是否处理这种多分辨率图形。

在那种情况下,我可以简单地降低采样率...但我需要在一个交互式图中有那么多的值。 - wishi
4
考虑到其中许多值会重叠,你真的需要在一个图中呈现数百万个数据吗?对我来说,自适应分辨率似乎是合理的选择。 - user703016
没有机会...具体来说,我正在记录封装在非常短的信号突发中的实时数据。这意味着要么全部采样,要么什么都没有。你不能在计划分析的东西上进行适应。 ;) - wishi
12
只是显示的问题,不涉及数据分析。你的结果不会受到影响,只是显示会有所不同。 - user703016
我有一个类似的问题,但是我有几年的数千兆字节数据(40kHz采样率的传感器数据)。现在我想要一个交互式图表,它将绘制像x小时或整天的平均值之类的东西,当我放大时,它应该动态计算新的平均值,直到我缩放到不需要平均计算的程度。问题是:是否已经有类似这样的实现,还是我必须自己编程? - white91wolf
回复我的评论,我的意思是我知道我至少要编写一些代码,但不是全部。我现在正在尝试/测试PyViz和一些集成工具。 - white91wolf

2
我在想,加速查询点的方法是否可行?(我一直对R*树很感兴趣。)
也许在这种情况下使用R*树是一个好选择。(当缩放比例较小时,树中较高的节点可以包含更粗糙、缩放比例较大的渲染信息,离叶子节点更远的节点则包含单个样本)
甚至可以将树(或其他所选结构)的内存映射到内存中以保持性能和降低RAM使用率。(这样你就可以将内存管理任务卸载给内核)
希望我的话有意义……有点啰嗦了,因为已经很晚了!

我对R一无所知。我的数据集类似于{索引,浮点i,浮点q}的csv格式,重复了2000万次。我不确定如何在R中构建您提到的数据结构。 - wishi
3
我认为这可能是一个复杂的项目。我在谈论“R星”树。维基百科链接:http://en.wikipedia.org/wiki/R*_tree HTH - nielsbot

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