NASA在Unsplash上拍摄的照片
我最近写了两篇有关使用Dask和Vaex处理大数据的介绍性文章,这两个库都能用于处理大于内存的数据集。在写那两片文章时,我想到一个问题:
这些库真的可以处理比内存更大的数据集吗?还是仅仅是销售口号?
这就促使我对Dask和Vaex进行了实际实验,并尝试处理比内存数据集更大的数据。这些数据集非常大到无法使用Pandas打开它。
**
我所说的大数据是什么?
照片由EV上Unsplash
大数据是一个松散定义的术语, 其定义甚至与Google的点击次数一样多。在本文中,我用这个术语来描述一个庞大的数据集,以至于我们需要专门的软件来处理它。对于Big,我指的是“比一台机器上的主内存更大”。
来自维基百科的定义:
大数据是一个领域,它研究处理分析、系统地从数据中提取信息方法,或以某种方式处理过大或复杂的数据集的方式。这些数据集无法由传统的数据处理应用软件处理。
什么是Dask和Vaex?
照片由JESHOOTS.COM上Unsplash
Dask提供了高级并行性的分析方法,可大幅提高您日常喜欢的工具性能。其中包括numpy,pandas和sklearn。它是开源的,免费提供。它使用现有的Python API和数据结构来,简化了在Dask和类似工具软件之间切换到麻烦。
Vaex是一个高性能Python库,用于Lazy Out-of-Core DataFrame(类似于Pandas),可以用来可视化和探索大型表格数据集。它可以计算每秒超过十亿行的基本统计信息。它支持多种可视化,允许交互式探索大数据。
Dask和Vaex数据框与Pandas数据框不完全兼容,但是两种工具都支持某些最常见的“数据整理”操作。Dask更善于集群计算,而Vaex使在单台计算机上处理大型数据集更加容易。
测试
Louis Reed在Unsplash上拍摄的照片
我已经生成了两个具有100万行和1000列的CSV文件。文件大小为18.18 GB,总共36.36 GB。文件具有0到100之间均匀分布的随机数。
两个带有随机数据的CSV文件
import pandas as pd
import numpy as np
from os import path
n_rows = 1_000_000
n_cols = 1000
for i in range(1, 3):
filename = 'analysis_%d.csv' % i
file_path = path.join('csv_files', filename)
df = pd.DataFrame(np.random.uniform(0, 100, size=(n_rows, n_cols)), columns=['col%d' % i for i in range(n_cols)])
print('Saving', file_path)
df.to_csv(file_path, index=False)
df.head()
本测试在32 GB主内存的MacBook Pro上运行的,这台电脑的功能相当强大。在测试Pandas数据框的限制时,我惊讶地发现,在这样的机器上触发内存错误是一个很大的挑战!
当内存达到极限时,macOS将数据从主内存转储到SSD。pandas Dataframe的上限是计算机上100 GB的可用磁盘空间。
当Mac需要内存时,它将会将当前未使用的内容推送到交换文件中以进行临时存储。当再次需要访问时,它将从交换文件中读取数据并返回到内存中。
我花了一些时间思考如何解决这个问题,这样实验才能公平。我想到的第一个想法是禁用交换,以便每个库仅具有可用的主内存。花了几个小时后,我无法禁用交换功能。
第二个想法比较野蛮。我事先将SSD填满,使得操作系统无法使用交换功能,因为设备上已经没有可用空间。
实验期间,您的磁盘几乎已满
这个做法成功了!Pandas无法读取两个18 GB的文件,Jupyter Kernel崩溃了。如果我要再次重复此实验,我将创建一个内存更少的虚拟机。这样,将更容易显示这些工具的局限性。
Dask或Vaex可以帮助我们处理这些大文件吗?哪一个更快?让我们找出答案。
Vaes决战Dask
Frida Bredesen在Unsplash上拍摄的照片
在设计实验时,我考虑了执行数据分析时的基本操作,例如分组,过滤和可视化数据。我想出了以下操作:
• 计算列的第十个分位数;
• 添加一个新列;
• 按列过滤;
• 按列分组并汇总;
• 可视化列。
以上所有操作都使用单个列执行计算,例如:
# filtering with a single column
df[df.col2 > 10]
因此,我很想尝试一项操作,该操作需要处理所有数据:
• 计算所有列的总和。
这可以通过将计算分解为较小的块来实现。例如。分别读取每一列并计算总和,最后一步计算总和。这些类型的计算问题被称为“令人尴尬的并行” ,因为无需花费任何努力就可以将问题分成单独的任务。
Vaex
照片由照片朗蒂上Unsplash
让我们从Vaex开始。该实验的设计遵循了每种工具的最佳实践:对Vaex使用二进制HDF5格式。因此,我们需要将CSV文件转换为HDF5格式(分层数据格式版本5)。
import glob
import vaex
csv_files = glob.glob('csv_files/*.csv')
for i, csv_file in enumerate(csv_files, 1):
for j, dv in enumerate(vaex.from_csv(csv_file, chunk_size=5_000_000), 1):
print('Exporting %d %s to hdf5 part %d' % (i, csv_file, j))
dv.export_hdf5(f'hdf5_files/analysis_{i:02}_{j:02}.hdf5')
Vaex需要405秒的时间才能将两个CSV文件(36.36 GB)转换为两个HDF5文件,这些文件合计16 GB。从文本格式转换为二进制格式可减小文件大小。
使用Vaex打开HDF5数据集:
dv = vaex.open('hdf5_files/*.hdf5')
Vaex需要1218秒来读取HDF5文件。我预计它会更快,因为Vaex自称可以立即打开二进制格式的文件。
引自Vaex文档:
无论磁盘上的文件大小如何,打开此类数据都是瞬间的:Vaex只会对数据进行内存映射,而不是在内存中读取数据。这是处理大于可用RAM的大型数据集的最佳方式。
Vaex展示头:
dv.head()
Vaex需要1189秒来显示文件头。我不确定为什么显示每列的前5行花了这么长时间。
用Vaex计算第十个分位数:
注意,Vaex具有percentile_approx函数,该函数计算分位数的近似值。
quantile = dv.percentile_approx('col1', 10)
Vaex需要0秒来计算col1列的第十个分位数的近似值。
使用Vaex添加新列:
dv ['col1_binary'] = dv.col1> dv.percentile_approx('col1',10)
Vaex具有虚拟列的概念,该表达式将表达式存储为列。它不占用任何内存,并在需要时动态计算。虚拟列的处理方式与普通列相同。如预期的那样,Vaex只要0秒就完成了上述命令。
用Vaex过滤数据:
Vaex有选择的概念,我没有使用过,因为Dask不支持选择,这会使实验不公平。除了Vaex不会复制数据外,下面的过滤器类似于使用Pandas过滤。
dv = dv [dv.col2> 10]
Vaex需要0秒来执行上面的过滤器。
使用Vaex分组和汇总数据:
下面的命令与pandas稍有不同,因为它结合了分组和聚合。该命令将数据按col1_binary分组,并计算col3的平均值:
group_res = dv.groupby(by=dv.col1_binary, agg={'col3_mean': vaex.agg.mean('col3')})
用Vaex计算平均值
Vaex只要0秒来完成上面的命令。
可视化直方图:
可视化大数据对于传统工具而言是个大问题。让我们尝试使用Vaex制作col3的直方图。
plot = dv.plot1d(dv.col3, what='count(*)', limits=[0, 100])
使用Vaex可视化数据
Vaex需要0秒就完成了上图的绘制,快得令人惊讶。
计算所有列的总和
一次处理单个列时,内存不是问题。让我们尝试使用Vaex计算数据集中所有数字的总和。
sum = np.sum(dv.sum(dv.column_names))
Vaex需要40秒来计算所有列的总和。
Dask
照片由凯利期Sikkema上Unsplash
现在,让我们重复上面的操作,但使用Dask。在运行Dask命令之前,Jupyter内核已重新启动。
我们没有使用Dask的read_csv函数直接读取CSV文件,而是将CSV文件转换为HDF5以示公平。
import dask.dataframe as dd
ds = dd.read_csv('csv_files/*.csv')
ds.to_hdf('hdf5_files_dask/analysis_01_01.hdf5', key='table')
Dask进行转换所需的时间为763秒。我试图读取用Vaex转换的HDF5文件,但没有成功。
Dask的最佳做法:
HDF5是具有高性能需求的Pandas用户的通常选择。我们鼓励Dask DataFrame用户改为使用Parquet存储和加载数据。
使用Dask打开HDF5数据集:
import dask.dataframe as dd
ds = dd.read_csv('csv_files/*.csv')
Dask需要0秒来打开HDF5文件。这是因为我没有显式运行compute命令,该命令实际上会读取文件。
Dask展示头:
ds.head()
Dask需要9秒才能输出文件的前5行。
用Dask计算第十个分位数:
Dask具有分位数功能,可以计算实际分位数,而不是近似值。
quantile = ds.col1.quantile(0.1).compute()
Dask无法计算分位数, 因为Juptyter Kernel崩溃了。
使用Dask定义一个新列:
下面的函数使用分位数功能定义一个新的二进制列。Dask无法计算它,因为它使用了分位数。
ds['col1_binary'] = ds.col1 > ds.col1.quantile(0.1)
使用Dask过滤数据:
ds = ds[(ds.col2 > 10)]
由于Dask使用延迟执行方式,因此上面的命令需要0秒才能执行。
使用Dask分组和汇总数据:
group_res = ds.groupby('col1_binary').col3.mean().compute()
Dask无法将数据分组和汇总。
可视化col3的直方图:
plot = ds.col3.compute().plot.hist(bins=64, ylim=(13900, 14400))
Dask无法可视化数据。
计算所有列的总和:
suma = ds.sum().sum().compute()
Dask无法汇总所有数据。
实用结果
下表显示了Vaex vs Dask实验的执行时间。NA表示该工具无法处理数据,Jupyter Kernel崩溃了。
实验中执行时间的摘要
结论
照片由约书亚金果上Unsplash
Vaex要求将CSV转换为HDF5格式,我对此并不介意,因为您可以去吃午饭,回来后便会转换数据。我也了解在恶劣的条件下(例如在实验中),内存很少时读取数据将花费更长的时间。
我不明白的是为什么Vaex需要1189秒显示文件的头5行!Vaex中的其他操作都经过了最优化,这使我们能够对大于主内存数据集的数据进行交互式数据分析。
我对Dask问题的理解是,它更适合于计算集群而不是单台计算机。Dask建立在Pandas之上,这意味着在Pandas运行缓慢的代码在Dask中运行依然缓慢。
实验的获胜者很明显。Vaex能够处理比笔记本电脑上的主内存文件更大的文件,而Dask则不能。这个实验有局限性,因为我是在单台计算机而不是计算群集上测试性能。
原文链接:https://towardsdatascience.com/are-you-still-using-pandas-to-process-big-data-in-2021-850ab26ad919