I/O 功夫:将数据导入和导出 Vaex#

如果你想在实时的 Python 内核中尝试这个笔记本,请使用 mybinder:

https://mybinder.org/badge_logo.svg

数据输入#

每个项目都从读取一些数据开始。Vaex 支持多种数据源:

  • 二进制文件格式:

  • 基于文本的文件格式:

  • 内存中的数据表示:

    • pandas 数据框以及 pandas 可以读取的所有内容

    • Apache Arrow 表格

    • numpy 数组

    • Python 字典

    • 单行数据框

  • 云支持:

    • Amazon Web Services S3

    • Google Cloud Storage

    • 其他云存储选项

  • 额外内容:

    • 别名

以下示例展示了在Vaex中获取数据的最佳实践。

打开二进制文件格式#

如果你的数据已经是支持的二进制文件格式之一(HDF5、Apache Arrow、Apache Parquet、FITS),使用Vaex打开它非常简单:

[1]:
import vaex

# Reading a HDF5 file
df_names = vaex.open('../data/io/sample_names_1.hdf5')
df_names
[1]:
# 姓名 年龄城市
0约翰 17爱丁堡
1莎莉 33格罗宁根

打开HDF5文件时,可以指定要读取的组:

df_group = vaex.open('my_file_with_groups.hdf5', group='/path/to/my/table')

有关实际示例,请参见导出二进制文件格式部分。

打开一个箭头或parquet文件同样简单:

[2]:
# Reading an arrow file
df_fruits = vaex.open('../data/io/sample_fruits.arrow')
df_fruits
[2]:
# 水果 数量产地
0芒果 5马来亚
1香蕉 10厄瓜多尔
2橙子 7西班牙

无论磁盘上的文件大小如何,打开此类数据都是即时的:Vaex 只会将数据内存映射,而不是将其读入内存。这是处理大于可用 RAM 的大型数据集的最佳方式。

如果你的数据包含在多个文件中,可以像这样同时打开它们:

[3]:
df_names_all = vaex.open('../data/io/sample_names_*.hdf5')
df_names_all
[3]:
# 姓名 年龄城市
0约翰 17爱丁堡
1莎莉 33格罗宁根
2玛丽亚 23加拉加斯
3莫妮卡 55纽约

或者,可以使用 open_many 方法来传递要打开的文件列表:

[4]:
df_names_all = vaex.open_many(['../data/io/sample_names_1.hdf5',
                               '../data/io/sample_names_2.hdf5'])
df_names_all
[4]:
# 姓名 年龄城市
0约翰 17爱丁堡
1莎莉 33格罗宁根
2玛丽亚 23加拉加斯
3莫妮卡 55纽约

结果将是一个包含来自所有文件的所有数据的单个DataFrame对象。

[5]:
# Reading a parquet file
df_cars = vaex.open('../data/io/sample_cars.parquet')
df_cars
[5]:
# 汽车 颜色 年份
0renault红色 1996
1audi 黑色 2005
2丰田 蓝色 2000

基于文本的文件格式#

数据集仍然通常以基于文本的文件格式存储,如CSV和JSON。Vaex支持多种读取此类数据集的方法。

4.14 中的新功能:

vaex.open 方法也可以用来读取CSV文件。使用此方法,Vaex将懒加载读取CSV文件,即当需要执行计算时,CSV文件中的数据将被流式传输。这是由Apache Arrow在底层支持的。通过这种方式,您可以处理任意大的CSV文件,而无需担心内存问题!

注意:以这种方式打开CSV文件时,Vaex会首先快速扫描文件以确定一些基本元数据,例如行数、列名及其数据类型。此过程的持续时间可能会因行数、列数、磁盘读取速度以及infer_schema_fraction(告诉Vaex读取文件的多少部分以确定元数据)而有所不同。

可以使用convert参数,例如vaex.open('my_file.csv', convert='my_file.hdf5'),轻松将CSV文件转换为HDF5以实现更快的访问,或转换为Parquet文件以考虑存储。

[6]:
df_nba_lazy = vaex.open('../data/io/sample_nba_1.csv')  # Read lazily, not kept in RAM
df_nba_lazy
[6]:
# 城市 队伍 球员
0印第安纳波利斯步行者队雷吉·米勒
1芝加哥 公牛队 迈克尔·乔丹
2波士顿 凯尔特人拉里·伯德

将较小的数据集直接读入内存可能更为实用。这可以通过以下方式轻松完成:

[7]:
df_nba = vaex.from_csv('../data/io/sample_nba_1.csv', copy_index=False)
df_nba
[7]:
# 城市 队伍 球员
0印第安纳波利斯步行者雷吉·米勒
1芝加哥 公牛队 迈克尔·乔丹
2波士顿 凯尔特人拉里·伯德

或者另一种方式:

[8]:
df_nba = vaex.read_csv('../data/io/sample_nba_1.csv', copy_index=False)
df_nba
[8]:
# 城市 队伍 球员
0印第安纳波利斯步行者雷吉·米勒
1芝加哥 公牛队 迈克尔·乔丹
2波士顿 凯尔特人拉里·伯德

Vaex 在后台使用 Pandas 读取 CSV 文件,因此可以将任何参数传递给 vaex.from_csvvaex.read_csv,就像传递给 pandas.read_csv 一样,并指定例如分隔符、列名和列类型。copy_index 参数指定是否应将 Pandas DataFrame 的索引列作为常规列读取,或者省略以节省内存。除此之外,如果指定了 convert=True 参数,数据将自动转换为 HDF5 文件,从而释放 RAM 并允许您以内存高效、外核方式处理数据。

如果CSV文件太大,无法一次性装入RAM,可以通过以下方式将数据转换为HDF5:

df = vaex.from_csv('./my_data/my_big_file.csv', convert=True, chunk_size=5_000_000)

当执行上述代码时,Vaex 将分块读取 CSV 文件,并将每个块转换为磁盘上的临时 HDF5 文件。然后,所有临时文件将被合并为一个单一的 HDF5 文件,并删除临时文件。可以通过 chunk_size 参数指定要读取的单个块的大小。请注意,此自动转换需要两倍于最终 HDF5 文件大小的可用磁盘空间。

我们经常需要分析的数据分布在多个CSV文件中。可以像这样将它们转换为HDF5文件格式:

[9]:
list_of_files = ['../data/io/sample_nba_1.csv',
                 '../data/io/sample_nba_2.csv',
                 '../data/io/sample_nba_3.csv',]

# Convert each CSV file to HDF5
for file in list_of_files:
    df_tmp = vaex.from_csv(file, convert=True, copy_index=False)

上述代码块依次将每个CSV文件转换为HDF5格式。请注意,只要存储空间足够,无论每个CSV文件的大小如何,转换都将正常工作。

现在处理所有数据变得容易了:只需按照上述方法打开所有相关的HDF5文件:

[10]:
df = vaex.open('../data/io/sample_nba_*.csv.hdf5')
df
[10]:
# 城市 队伍 球员
0印第安纳波利斯步行者雷吉·米勒
1芝加哥 公牛队 迈克尔·乔丹
2波士顿 凯尔特人拉里·伯德
3洛杉矶湖人队科比·布莱恩特
4多伦多 猛龙文斯·卡特
5费城76人队艾伦·艾弗森
6圣安东尼奥 马刺 蒂姆·邓肯

然后,可以进一步将这个合并的DataFrame导出到单个HDF5文件中。这应该会带来一些性能上的小幅提升。

[11]:
df.export('../data/io/sample_nba_combined.hdf5')

通过Pandas读取较大的CSV文件可能会很慢。Apache Arrow提供了读取此类文件的更快方法。Vaex方便地展示了这一功能:

[12]:
df_nba_arrow = vaex.from_csv_arrow('../data/io/sample_nba_1.csv')
df_nba_arrow
[12]:
# 城市 队伍 球员
0印第安纳波利斯步行者雷吉·米勒
1芝加哥 公牛队 迈克尔·乔丹
2波士顿 凯尔特人拉里·伯德

事实上,Apache Arrow 解析 CSV 文件的速度非常快,它可以用于流式传输数据并有效地实现惰性读取。通过将 lazy=True 传递给上述方法,可以处理比可用内存大得多的 CSV 文件。这就是 vaex.open 在底层使用的技术,以提供对 CSV 文件的惰性读取。

[13]:
df_nba_arrow_lazy = vaex.from_csv_arrow('../data/io/sample_nba_1.csv', lazy=True)
df_nba_arrow_lazy
[13]:
# 城市 队伍 球员
0印第安纳波利斯步行者雷吉·米勒
1芝加哥 公牛队 迈克尔·乔丹
2波士顿 凯尔特人拉里·伯德

数据通常也存储在JSON文件中。要在Vaex中读取此类数据,可以执行以下操作:

[14]:
df_isles = vaex.from_json('../data/io/sample_isles.json', orient='table', copy_index=False)
df_isles
[14]:
# 岛屿 面积(平方公里)
0复活节岛 163.6
1斐济 18.333
2Tortuga 178.7

这是一个便捷方法,它简单地包装了pandas.read_json,因此相同的参数和文件读取策略适用。如果数据分布在多个JSON文件中,可以应用类似于多个CSV文件的策略:使用vaex.from_json方法读取每个JSON文件,将其转换为HDF5或Arrow文件格式。然后使用vaex.openvaex.open_many方法将所有转换后的文件作为单个DataFrame打开。

要了解更多关于使用Vaex导出数据的不同选项,请阅读下面的下一节。

云支持#

Vaex 支持从亚马逊的 S3 和谷歌云存储流式传输 HDF5、Apache Arrow、Apache Parquet 和 CSV 文件。以下是一个直接从 S3 流式传输 HDF5 文件的示例:

[15]:
df = vaex.open('s3://vaex/taxi/nyc_taxi_2015_mini.hdf5?anon=true')
df.head_and_tail_print(3)
# 供应商ID 上车时间 下车时间 乘客数量 支付类型 行程距离 上车经度 上车纬度 费率代码 存储和转发标志 下车经度 下车纬度 费用金额 附加费 MTA税 小费金额 通行费金额 总金额
0 VTS 2015-02-27 22:11:38.0000000002015-02-27 22:22:51.0000000005 1 2.26 -74.006645 40.707497 1.0 0.0 -74.0096 40.73462 10.0 0.5 0.5 2.0 0.0 13.3
1 VTS 2015-08-04 00:36:01.0000000002015-08-04 00:47:11.0000000001 1 5.13 -74.00747 40.705235 1.0 0.0 -73.96727 40.755196 16.0 0.5 0.5 3.46 0.0 20.76
2 VTS 2015-01-28 19:56:52.0000000002015-01-28 20:03:27.0000000001 2 1.89 -73.97189 40.76286 1.0 0.0 -73.95513 40.78596 7.5 1.0 0.5 0.0 0.0 9.3
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
299,997CMT 2015-06-18 09:05:52.0000000002015-06-18 09:28:19.0000000001 1 2.7 -73.95231 40.78091 1.0 0.0 -73.97917 40.75542 15.0 0.0 0.5 1.25 0.0 17.05
299,998VTS 2015-04-17 11:13:46.0000000002015-04-17 11:33:19.0000000001 2 1.75 -73.951935 40.77804 1.0 0.0 -73.9692 40.763924 13.0 0.0 0.5 0.0 0.0 13.8
299,999VTS 2015-05-29 07:00:45.0000000002015-05-29 07:17:47.0000000005 2 8.94 -73.95345 40.77932 1.0 0.0 -73.86702 40.77094 26.0 0.0 0.5 0.0 5.54 32.34

如果需要,还可以使用 fs_options 来指定需要传递给外部文件系统的任何参数:

例如:

[16]:
df = vaex.open('s3://vaex/taxi/nyc_taxi_2015_mini.hdf5', fs_options={'anon': True})
df.head(3)
[16]:
# 供应商ID 上车时间 下车时间 乘客数量 支付类型 行程距离 上车经度 上车纬度 费率代码 存储和转发标志 下车经度 下车纬度 费用金额 附加费 MTA税 小费金额 通行费金额 总金额
0VTS 2015-02-27 22:11:38.0000000002015-02-27 22:22:51.000000000 5 1 2.26 -74.0066 40.7075 1 0 -74.0096 40.7346 10 0.5 0.5 2 0 13.3
1VTS 2015-08-04 00:36:01.0000000002015-08-04 00:47:11.000000000 1 1 5.13 -74.0075 40.7052 1 0 -73.9673 40.7552 16 0.5 0.5 3.46 0 20.76
2VTS 2015-01-28 19:56:52.0000000002015-01-28 20:03:27.000000000 1 2 1.89 -73.9719 40.7629 1 0 -73.9551 40.786 7.5 1 0.5 0 0 9.3

当流式传输HDF5文件时,fs_options 也接受“缓存”选项。当 True 时,作为默认设置,Vaex 会懒加载并将数据缓存到本地机器上。“懒加载”意味着 Vaex 只会下载你真正需要的数据部分。

例如:想象一下,我们有一个托管在S3上的文件,它有100列和10亿行。通过print(df)获取DataFrame的预览将仅下载前5行和后5行。如果我们随后仅使用5列进行计算或绘图,则仅下载这些列的数据并缓存到本地机器。

默认情况下,从S3和GCS流式传输的数据分别缓存在$HOME/.vaex/file-cache/s3$HOME/.vaex/file-cache/gs,因此连续访问的速度与本地磁盘访问一样快。

流式传输Apache Arrow和Apache Parquet同样简单。这些文件格式支持缓存,但目前使用Apache Arrow格式在打开文件时会读取所有数据,因此用处不大。为了获得最佳性能,我们始终建议使用与存储桶位于同一区域的计算实例。

以下是一个直接从Google Cloud Storage读取Apache Arrow文件的示例:

df = vaex.open('gs://vaex-data/airlines/us_airline_2019_mini.arrow', fs_options={'anon': True})
df

Apache Parquet 文件通常经过压缩,因此在云环境中通常是更好的选择,因为它们往往能降低存储和传输成本。以下是从 Google Cloud Storage 打开 Parquet 文件的示例。

df = vaex.open('gs://vaex-data/airlines/us_airline_2019_mini.parquet', fs_options={'anon': True})
df

下表总结了Vaex当前在读取、缓存和写入不同文件格式到Amazon S3和Google Cloud Storage方面的能力。

格式

读取

缓存

写入

HDF5

否*

箭头

否*

Parquet

否*

FITS

否*

CSV

???

不* - 目前不可用,但将来可能会实现。请访问 vaex.io 获取更多信息。

其他云存储选项 - Minio 示例#

Minio 是一个与 S3 兼容的对象存储服务器,可以用来替代 AWS 的 S3 服务。假设 Minio 的设置如下:

$ export DATA_ROOT=/data/tmp
$ mkdir $DATA_ROOT/taxi
$ wget https://github.com/vaexio/vaex-datasets/releases/download/1.1/yellow_taxi_2012_zones.parquet --directory-prefix $DATA_ROOT/taxi/
$ docker run -it --rm -p 9000:9000 --name minio1 -v $DATA_ROOT:/data -e "MINIO_ROOT_USER=AKIAIOSFODNN7EXAMPLE" -e "MINIO_ROOT_PASSWORD=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" minio/minio server /data

这将创建一个运行中的Minio服务器,可通过localhost:9000访问,托管一个名为'taxi'的存储桶,其中包含1个parquet文件。我们现在可以使用Vaex连接到它。从Web界面中,我们可以获取一个URL,在这种情况下,其形式为:

http://localhost:9000/taxi/yellow_taxi_2012_zones.parquet?Content-Disposition=attachment%3B%20filename%3D%22yellow_taxi_2012_zones.parquet%22&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAIOSFODNN7EXAMPLE%2F20210707%2F%2Fs3%2Faws4_request&X-Amz-Date=20210707T085053Z&X-Amz-Expires=432000&X-Amz-SignedHeaders=host&X-Amz-Signature=03e0b6718a95be0fd0d679c4fc52bc26f9ce9f7845877866d5caa709e9b0e12c

这不是你应该提供给Vaex(或Apache Arrow,Vaex使用它)的S3 URL。正确的URL格式是s3://bucket/path/to/file.ext。我们还需要通过传递适当的fs_options:来告诉Vaex连接到服务器。

df = vaex.open('s3://taxi/yellow_taxi_2012_zones.parquet', fs_options=dict(
    endpoint_override='localhost:9000',
    access_key='AKIAIOSFODNN7EXAMPLE',
    secret_key='wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY',
    scheme='http')
)

内存数据表示#

可以从各种内存数据表示中构建一个Vaex DataFrame。一个常见的操作是将Pandas转换为Vaex DataFrame。让我们用Pandas读取一个CSV文件,然后将其转换为Vaex DataFrame:

[17]:
import pandas as pd

pandas_df = pd.read_csv('../data/io/sample_nba_1.csv')
pandas_df
[17]:
城市 团队 球员
0 印第安纳波利斯 步行者队 雷吉·米勒
1 芝加哥 公牛队 迈克尔·乔丹
2 波士顿 凯尔特人 拉里·伯德
[18]:
df = vaex.from_pandas(df=pandas_df, copy_index=True)
df
[18]:
# 城市 球队 球员 指数
0印第安纳波利斯步行者队雷吉·米勒 0
1芝加哥 公牛队 迈克尔·乔丹 1
2波士顿 凯尔特人拉里·伯德 2

copy_index 参数指定是否应将 Pandas DataFrame 的索引列导入到 Vaex DataFrame 中。将 Pandas 转换为 Vaex DataFrame 特别有用,因为 Pandas 可以从多种文件格式中读取数据。例如,我们可以使用 Pandas 从数据库中读取数据,然后像这样将其传递给 Vaex:

import vaex
import pandas as pd
import sqlalchemy

connection_string = 'postgresql://readonly:' + 'my_password' + '@server.company.com:1234/database_name'
engine = sqlalchemy.create_engine(connection_string)

pandas_df = pd.read_sql_query('SELECT * FROM MYTABLE', con=engine)
df = vaex.from_pandas(pandas_df, copy_index=False)

另一个例子是使用pandas读取SAS文件:

[19]:
pandas_df = pd.read_sas('../data/io/sample_airline.sas7bdat')
df = vaex.from_pandas(pandas_df, copy_index=False)
df
[19]:
# 年份 Y W R L K
0 1948.01.21399998664855960.243000000715255740.1454000025987625 1.41499996185302730.6119999885559082
1 1949.01.35399997234344480.259999990463256840.218099996447563171.38399994373321530.5590000152587891
2 1950.01.569000005722046 0.277999997138977050.3156999945640564 1.38800001144409180.5730000138282776
3 1951.01.94799995422363280.296999990940094 0.393999993801116941.54999995231628420.5640000104904175
4 1952.02.265000104904175 0.3100000023841858 0.355899989604949951.80200004577636720.5740000009536743
... ... ... ... ... ... ...
271975.018.72100067138672 1.246999979019165 0.230100005865097055.72200012207031259.062000274658203
281976.019.25 1.375 0.3452000021934509 5.76200008392334 8.26200008392334
291977.020.64699935913086 1.5440000295639038 0.450800001621246345.876999855041504 7.473999977111816
301978.022.72599983215332 1.7029999494552612 0.5877000093460083 6.107999801635742 7.104000091552734
311979.023.6189994812011721.7790000438690186 0.534600019454956 6.85200023651123056.874000072479248

可以以类似的方式将箭头表读取为Vaex DataFrame。让我们首先使用pyarrow将CSV文件读取为箭头表。

[20]:
import pyarrow.csv

arrow_table = pyarrow.csv.read_csv('../data/io/sample_nba_1.csv')
arrow_table
[20]:
pyarrow.Table
city: string
team: string
player: string
----
city: [["Indianopolis","Chicago","Boston"]]
team: [["Pacers","Bulls","Celtics"]]
player: [["Reggie Miller","Michael Jordan","Larry Bird"]]

一旦我们有了箭头表,将其转换为DataFrame就很简单了:

[21]:
df = vaex.from_arrow_table(arrow_table)
df
[21]:
# 城市 队伍 球员
0印第安纳波利斯步行者雷吉·米勒
1芝加哥 公牛队 迈克尔·乔丹
2波士顿 凯尔特人拉里·伯德

从numpy数组构建Vaex DataFrame也很常见。可以这样做:

[22]:
import numpy as np

x = np.arange(2)
y = np.array([10, 20])
z = np.array(['dog', 'cat'])


df_numpy = vaex.from_arrays(x=x, y=y, z=z)
df_numpy
[22]:
# x yz
0 0 10
1 1 20

从Python字典构建DataFrame也非常直接:

[23]:
# Construct a DataFrame from Python dictionary
data_dict = dict(x=[2, 3], y=[30, 40], z=['cow', 'horse'])

df_dict = vaex.from_dict(data_dict)
df_dict
[23]:
# x yz
0 2 30
1 3 40

有时,可能需要创建一个单行的DataFrame。Vaex提供了一个便捷的方法,该方法接受单个元素(标量)并创建DataFrame:

[24]:
df_single_row = vaex.from_scalars(x=4, y=50, z='mouse')
df_single_row
[24]:
# x yz
0 4 50鼠标

最后,我们可以选择连接不同的DataFrames,而不会像这样产生任何内存开销:

[25]:
df = vaex.concat([df_numpy, df_dict, df_single_row])
df
[25]:
# x yz
0 0 10
1 1 20
2 2 30
3 3 40
4 4 50鼠标

附加内容#

Vaex 允许您为您最常用的数据集的位置创建别名。它们可以是本地的或在云端的:

[26]:
vaex.aliases['nba'] = '../data/io/sample_nba_1.csv'
vaex.aliases['nyc_taxi_aws'] = 's3://vaex/taxi/nyc_taxi_2015_mini.hdf5?anon=true'
[27]:
df = vaex.open('nba')
df
[27]:
# 城市 队伍 球员
0印第安纳波利斯步行者雷吉·米勒
1芝加哥 公牛队 迈克尔·乔丹
2波士顿 凯尔特人拉里·伯德
[28]:
df = vaex.open('nyc_taxi_aws')
df.head_and_tail_print(3)
# 供应商ID 上车时间 下车时间 乘客数量 支付类型 行程距离 上车经度 上车纬度 费率代码 存储和转发标志 下车经度 下车纬度 费用金额 附加费 MTA税 小费金额 通行费金额 总金额
0 VTS 2015-02-27 22:11:38.0000000002015-02-27 22:22:51.0000000005 1 2.26 -74.006645 40.707497 1.0 0.0 -74.0096 40.73462 10.0 0.5 0.5 2.0 0.0 13.3
1 VTS 2015-08-04 00:36:01.0000000002015-08-04 00:47:11.0000000001 1 5.13 -74.00747 40.705235 1.0 0.0 -73.96727 40.755196 16.0 0.5 0.5 3.46 0.0 20.76
2 VTS 2015-01-28 19:56:52.0000000002015-01-28 20:03:27.0000000001 2 1.89 -73.97189 40.76286 1.0 0.0 -73.95513 40.78596 7.5 1.0 0.5 0.0 0.0 9.3
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
299,997CMT 2015-06-18 09:05:52.0000000002015-06-18 09:28:19.0000000001 1 2.7 -73.95231 40.78091 1.0 0.0 -73.97917 40.75542 15.0 0.0 0.5 1.25 0.0 17.05
299,998VTS 2015-04-17 11:13:46.0000000002015-04-17 11:33:19.0000000001 2 1.75 -73.951935 40.77804 1.0 0.0 -73.9692 40.763924 13.0 0.0 0.5 0.0 0.0 13.8
299,999VTS 2015-05-29 07:00:45.0000000002015-05-29 07:17:47.0000000005 2 8.94 -73.95345 40.77932 1.0 0.0 -73.86702 40.77094 26.0 0.0 0.5 0.0 5.54 32.34

数据导出#

可以将Vaex数据框导出为多种文件或内存数据表示形式:

导出二进制文件格式#

在使用Vaex时,最有效的数据存储方式是使用二进制文件格式。Vaex可以将DataFrame导出为HDF5、Apache Arrow、Apache Parquet和FITS格式:

[29]:
df.export_hdf5('../data/io/output_data.hdf5')
df.export_arrow('../data/io/output_data.arrow')
df.export_parquet('../data/io/output_data.parquet')

或者,可以简单地使用:

[30]:
df.export('../data/io/output_data.hdf5')
df.export('../data/io/output_data.arrow')
df.export('../data/io/output_data.parquet')

Vaex 将根据文件名的指定扩展名来确定文件的格式。如果扩展名不被识别,将会引发异常。

当导出到HDF5时,您可以指定一个特定的组。现有的HDF5文件也可以通过另一个数据集进行追加,但它需要在另一个组中。例如:

[31]:
df1 = vaex.from_arrays(x=[1, 2, 3], y=[-0.5, 0, 0.5])
df2 = vaex.from_arrays(s1=['Apple', 'Orange', 'Peach'], s2=['potato', 'carrot', 'cucumber'])

df1.export_hdf5('../data/io/output_hdf5_file_with_multiple_groups.hdf5', mode='w', group='/numbers')
df2.export_hdf5('../data/io/output_hdf5_file_with_multiple_groups.hdf5', mode='a', group='/food')

打开二进制格式部分所述,可以通过将group参数传递给vaex.open方法来打开这个新创建的文件:

[32]:
df_food = vaex.open('../data/io/output_hdf5_file_with_multiple_groups.hdf5', group='food')
df_food
[32]:
# s1 s2
0苹果 土豆
1橙子胡萝卜
2桃子黄瓜
[33]:
df_nums = vaex.open('../data/io/output_hdf5_file_with_multiple_groups.hdf5', group='numbers')
df_nums
[33]:
# x y
0 1-0.5
1 2 0
2 3 0.5

当导出为Apache Arrow和Apache Parquet文件格式时,数据以块的形式写入,从而能够一次性导出不适合在RAM中的数据。可以通过chunk_size参数指定自定义块大小,其默认值为1048576。例如:

[34]:
df.export('../data/io/output_data.parquet', chunk_size=10_000)

Vaex 支持在将数据导出为 Apache Arrow 和 Apache Parquet 文件格式时直接写入 Amazon 的 S3 和 Google 云存储桶。与打开文件时类似,可以指定 fs_options 字典以传递参数给底层文件系统,例如认证凭据。以下是两个示例:

# Export to Google Cloud Storage
df.export_arrow(to='gs://my-gs-bucket/my_data.arrow', fs_options={'token': my_token})

# Export to Amazon's S3
df.export_parquet(to='s3://my-s3-bucket/my_data.parquet', fs_options={'access_key': my_key, 'secret_key': my_secret_key})

基于文本的文件格式#

有时,将数据导出为基于文本的文件格式(如CSV)可能很有用。在这种情况下,可以简单地执行以下操作:

[35]:
df.export_csv('../data/io/output_data.csv')  # `chunk_size` has a default value of 1_000_000

df.export_csv 方法在背后使用了 pandas_df.to_csv,因此可以将任何参数传递给 df.export_csv,就像传递给 pandas_df.to_csv 一样。数据是分块导出的,这些块的大小可以通过 df.export_csv 中的 chunk_size 参数指定。通过这种方式,可以保存那些太大而无法放入 RAM 的数据到磁盘。

如果需要将较大的DataFrame导出为CSV,Apache Arrow后端提供了更好的性能:

[36]:
df.export_csv_arrow('../data/io/output_data.csv')

并行导出到多个文件#

使用export_many方法,可以将DataFrame并行导出为多个相同类型的文件。与将单个大型Arrow或Parquet文件连续写入相比,这在将非常大的DataFrame导出到云端时可能会更高效。该方法还接受fs_options字典,并且在导出到云存储时特别方便。

[37]:
df.export_many('../data/io/output_chunk-{i:02}.parquet', chunk_size=100_000)
[38]:
!ls ./data/io/output_chunk*.parquet
./data/io/output_chunk-00.parquet  ./data/io/output_chunk-02.parquet
./data/io/output_chunk-01.parquet

内存数据表示#

Python拥有一个丰富的生态系统,由各种用于数据操作的库组成,这些库提供不同的功能。因此,能够将数据从一个库传递到另一个库通常非常有用。Vaex能够通过多种内存表示形式将其数据传递给其他库。

DataFrame 表示#

Vaex DataFrame 可以像这样转换为 pandas DataFrame:

[39]:
df = vaex.open('../data/io/sample_simple.hdf5')
pandas_df = df.to_pandas_df()
pandas_df  # looks the same doesn't it?
[39]:
x y z
0 0 10
1 1 20
2 2 30
3 3 40
4 4 50 鼠标

对于太大而无法放入内存的DataFrames,可以指定chunk_size参数,在这种情况下,to_pandas_df方法会返回一个生成器,生成一个pandas DataFrame,其行数由chunk_size参数指定:

[40]:
gen = df.to_pandas_df(chunk_size=3)

for i1, i2, chunk in gen:
    print(i1, i2)
    print(chunk)
    print()
0 3
   x   y    z
0  0  10  dog
1  1  20  cat
2  2  30  cow

3 5
   x   y      z
0  3  40  horse
1  4  50  mouse

生成器还会生成该块第一个和最后一个元素的行号,因此我们可以准确地知道我们在父DataFrame中的位置。以下DataFrame方法也支持具有相同行为的chunk_size参数。

将Vaex DataFrame转换为箭头表的过程类似:

[41]:
arrow_table = df.to_arrow_table()
arrow_table
[41]:
pyarrow.Table
x: int64
y: int64
z: string
----
x: [[0,1,2,3,4]]
y: [[10,20,30,40,50]]
z: [["dog","cat","cow","horse","mouse"]]

可以简单地将DataFrame转换为数组列表。默认情况下,数据以numpy或arrow数组列表的形式暴露:

[42]:
arrays = df.to_arrays()
arrays
[42]:
[array([0, 1, 2, 3, 4]),
 array([10, 20, 30, 40, 50]),
 <pyarrow.lib.StringArray object at 0x7ff265f91c40>
 [
   "dog",
   "cat",
   "cow",
   "horse",
   "mouse"
 ]]

通过指定array_type参数,可以选择数据是由numpy数组、xarrays还是Python列表表示。

[43]:
arrays = df.to_arrays(array_type='xarray')
arrays  # list of xarrays
[43]:
[<xarray.DataArray (dim_0: 5)>
 array([0, 1, 2, 3, 4])
 Dimensions without coordinates: dim_0,
 <xarray.DataArray (dim_0: 5)>
 array([10, 20, 30, 40, 50])
 Dimensions without coordinates: dim_0,
 <xarray.DataArray (dim_0: 5)>
 array(['dog', 'cat', 'cow', 'horse', 'mouse'], dtype=object)
 Dimensions without coordinates: dim_0]
[44]:
arrays = df.to_arrays(array_type='list')
arrays  # list of lists
[44]:
[[0, 1, 2, 3, 4],
 [10, 20, 30, 40, 50],
 ['dog', 'cat', 'cow', 'horse', 'mouse']]

保持接近纯Python,可以将Vaex DataFrame导出为字典。同样适用于这里的array_type关键字参数:

[45]:
d_dict = df.to_dict(array_type='numpy')
d_dict
[45]:
{'x': array([0, 1, 2, 3, 4]),
 'y': array([10, 20, 30, 40, 50]),
 'z': array(['dog', 'cat', 'cow', 'horse', 'mouse'], dtype=object)}

或者,也可以将DataFrame转换为元组列表,其中元组的第一个元素是列名,而第二个元素是数据的数组表示。

[46]:
# Get a single item list
items = df.to_items(array_type='list')
items
[46]:
[('x', [0, 1, 2, 3, 4]),
 ('y', [10, 20, 30, 40, 50]),
 ('z', ['dog', 'cat', 'cow', 'horse', 'mouse'])]

与各种类型的API交互时,通常会传递一个“记录”列表,其中记录是描述DataFrame单行的字典:

[47]:
records = df.to_records()
records
[47]:
[{'x': 0, 'y': 10, 'z': 'dog'},
 {'x': 1, 'y': 20, 'z': 'cat'},
 {'x': 2, 'y': 30, 'z': 'cow'},
 {'x': 3, 'y': 40, 'z': 'horse'},
 {'x': 4, 'y': 50, 'z': 'mouse'}]

如前所述,对于上述所有示例,可以使用chunk_size参数,该参数创建一个生成器,以指定格式生成DataFrame的一部分。在.to_dict方法的情况下:

[48]:
gen = df.to_dict(array_type='list', chunk_size=2)

for i1, i2, chunk in gen:
    print(i1, i2, chunk)
0 2 {'x': [0, 1], 'y': [10, 20], 'z': ['dog', 'cat']}
2 4 {'x': [2, 3], 'y': [30, 40], 'z': ['cow', 'horse']}
4 5 {'x': [4], 'y': [50], 'z': ['mouse']}

最后但同样重要的是,Vaex DataFrame 可以懒加载为 Dask 数组:

[49]:
dask_arrays = df[['x', 'y']].to_dask_array()   # String support coming soon
dask_arrays
[49]:
数组
字节 80 B 80 B
形状 (5, 2) (5, 2)
计数 2 任务 1 块
类型 int64 numpy.ndarray
2 5

表达式表示#

单个 Vaex 表达式也可以转换为各种内存表示形式:

[50]:
# pandas Series
x_series = df.x.to_pandas_series()
x_series
[50]:
0    0
1    1
2    2
3    3
4    4
dtype: int64
[51]:
# numpy array
x_numpy = df.x.to_numpy()
x_numpy
[51]:
array([0, 1, 2, 3, 4])
[52]:
# Python list
x_list = df.x.tolist()
x_list
[52]:
[0, 1, 2, 3, 4]
[53]:
# Dask array
x_dask_array = df.x.to_dask_array()
x_dask_array
[53]:
数组
字节 40 B 40 B
形状 (5,) (5,)
计数 2 任务 1 块
类型 int64 numpy.ndarray
5 1