I/O 功夫:将数据导入和导出 Vaex#
如果你想在实时的 Python 内核中尝试这个笔记本,请使用 mybinder:
数据输入#
每个项目都从读取一些数据开始。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]:
| # | 汽车 | 颜色 | 年份 |
|---|---|---|---|
| 0 | renault | 红色 | 1996 |
| 1 | audi | 黑色 | 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_csv 或 vaex.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 |
| 2 | Tortuga | 178.7 |
这是一个便捷方法,它简单地包装了pandas.read_json,因此相同的参数和文件读取策略适用。如果数据分布在多个JSON文件中,可以应用类似于多个CSV文件的策略:使用vaex.from_json方法读取每个JSON文件,将其转换为HDF5或Arrow文件格式。然后使用vaex.open或vaex.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.000000000 | 2015-02-27 22:22:51.000000000 | 5 | 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.000000000 | 2015-08-04 00:47:11.000000000 | 1 | 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.000000000 | 2015-01-28 20:03:27.000000000 | 1 | 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,997 | CMT | 2015-06-18 09:05:52.000000000 | 2015-06-18 09:28:19.000000000 | 1 | 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,998 | VTS | 2015-04-17 11:13:46.000000000 | 2015-04-17 11:33:19.000000000 | 1 | 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,999 | VTS | 2015-05-29 07:00:45.000000000 | 2015-05-29 07:17:47.000000000 | 5 | 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 来指定需要传递给外部文件系统的任何参数:
当使用亚马逊的S3时:
pyarrow.fs.S3FileSystem - 如果Arrow支持。
s3fs.core.S3FileSystem - 用于通配符匹配和回退。
使用Google Cloud Storage时:
例如:
[16]:
df = vaex.open('s3://vaex/taxi/nyc_taxi_2015_mini.hdf5', fs_options={'anon': True})
df.head(3)
[16]:
| # | 供应商ID | 上车时间 | 下车时间 | 乘客数量 | 支付类型 | 行程距离 | 上车经度 | 上车纬度 | 费率代码 | 存储和转发标志 | 下车经度 | 下车纬度 | 费用金额 | 附加费 | MTA税 | 小费金额 | 通行费金额 | 总金额 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | VTS | 2015-02-27 22:11:38.000000000 | 2015-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 |
| 1 | VTS | 2015-08-04 00:36:01.000000000 | 2015-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 |
| 2 | VTS | 2015-01-28 19:56:52.000000000 | 2015-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.0 | 1.2139999866485596 | 0.24300000071525574 | 0.1454000025987625 | 1.4149999618530273 | 0.6119999885559082 |
| 1 | 1949.0 | 1.3539999723434448 | 0.25999999046325684 | 0.21809999644756317 | 1.3839999437332153 | 0.5590000152587891 |
| 2 | 1950.0 | 1.569000005722046 | 0.27799999713897705 | 0.3156999945640564 | 1.3880000114440918 | 0.5730000138282776 |
| 3 | 1951.0 | 1.9479999542236328 | 0.296999990940094 | 0.39399999380111694 | 1.5499999523162842 | 0.5640000104904175 |
| 4 | 1952.0 | 2.265000104904175 | 0.3100000023841858 | 0.35589998960494995 | 1.8020000457763672 | 0.5740000009536743 |
| ... | ... | ... | ... | ... | ... | ... |
| 27 | 1975.0 | 18.72100067138672 | 1.246999979019165 | 0.23010000586509705 | 5.7220001220703125 | 9.062000274658203 |
| 28 | 1976.0 | 19.25 | 1.375 | 0.3452000021934509 | 5.76200008392334 | 8.26200008392334 |
| 29 | 1977.0 | 20.64699935913086 | 1.5440000295639038 | 0.45080000162124634 | 5.876999855041504 | 7.473999977111816 |
| 30 | 1978.0 | 22.72599983215332 | 1.7029999494552612 | 0.5877000093460083 | 6.107999801635742 | 7.104000091552734 |
| 31 | 1979.0 | 23.618999481201172 | 1.7790000438690186 | 0.534600019454956 | 6.8520002365112305 | 6.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 | y | z |
|---|---|---|---|
| 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 | y | z |
|---|---|---|---|
| 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 | y | z |
|---|---|---|---|
| 0 | 4 | 50 | 鼠标 |
最后,我们可以选择连接不同的DataFrames,而不会像这样产生任何内存开销:
[25]:
df = vaex.concat([df_numpy, df_dict, df_single_row])
df
[25]:
| # | x | y | z |
|---|---|---|---|
| 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.000000000 | 2015-02-27 22:22:51.000000000 | 5 | 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.000000000 | 2015-08-04 00:47:11.000000000 | 1 | 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.000000000 | 2015-01-28 20:03:27.000000000 | 1 | 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,997 | CMT | 2015-06-18 09:05:52.000000000 | 2015-06-18 09:28:19.000000000 | 1 | 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,998 | VTS | 2015-04-17 11:13:46.000000000 | 2015-04-17 11:33:19.000000000 | 1 | 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,999 | VTS | 2015-05-29 07:00:45.000000000 | 2015-05-29 07:17:47.000000000 | 5 | 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]:
|
表达式表示#
单个 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]:
|