应用-为Pandas选择一个好的文件格式

目录

为Pandas选择一个好的文件格式

在你用Pandas处理你的数据之前,你需要加载它 (从磁盘或远程存储)。Pandas支持的数据格式有很多,从 CSV 到 JSON,到 Parquet,还有很多其他的。

你应该用哪个?

  • 您不希望加载数据的速度很慢,或者使用大量内存: 这纯粹是开销。理想情况下,您需要一种快速、高效、小型且受到广泛支持的文件格式。
  • 您还需要确保加载的数据具有所有正确的类型: 数值类型、日期时间等等。有些数据格式在这方面比其他格式做得更好。

虽然没有一个真正适合每个人的答案,但本文将试图帮助您缩小范围,做出明智的决定。

“最好” 是针对具体情况的

不同的情况意味着不同的需求。

与外部组织共享数据

如果您需要与其他组织,甚至您组织中的其他团队共享数据,那么您需要限制自己使用您知道他们能够处理的数据格式。这是非常具体的情况,所以很难给出一个普遍的答案。

处理传入数据

如果有人给你一个文件,他们控制格式。如果您将只处理它一次,那么更改文件格式可能不值得这么麻烦。

流式记录与全文件处理

如果您正在通过网络传输数据,并且希望在数据到达时逐行处理它,那么这意味着一种非常不同的数据格式: 您需要一些简单的基于行的解析。
CSV 实际上非常擅长这个 ,尽管我们将看到它在其他方面是一种令人讨厌的格式。

如果你使用Pandas,你就不太可能做这种处理。

我们正在考虑的情况:内部数据集

为了帮助限制讨论的范围,我们假设您在一个内部进程中使用写入磁盘的大型数据集,然后在稍后的一个或多个其他内部进程中读取数据。

我们可以考虑数据格式的多个标准:

  • **类型:**有些数据是数字的,有些数据是由字符串组成的,其他数据可能是基于时间的;能够区分这些不同的类型是很有用的。
  • 高效的磁盘格式: 最小化磁盘上的空间使用量。
  • 高效读写: 写入磁盘和从磁盘加载应该是快速的。加载时内存使用应该很低。

额外要求:互操作性

您可能不会永远使用Pandas,或者您可能希望在某些情况下使用其他库。因此,作为未来灵活性的替代品,我们还将添加一个要求,即您应该能够使用 Polars 轻松处理您的数据,Polars 是一种新兴的Pandas替代品。这就排除了 Polars 目前还不支持的许多数据格式,以及过于专门针对Pandas的格式,比如 pickle a Pandas DataFrame

检查候选数据格式

根据上面的标准,我们将考虑三种格式: CSV、 JSON 和 Parquet。

候选人 1: CSV

  • 类型: CSV 没有固有的类型概念;有时字符串会加引号,但即使这样也不能保证。已经有一些为 CSV 设计方案语言的努力,例如“CSV Schema”。它很难搜索,而且我没有看到任何重要的库实现了这个或任何其他 Python 标准;

  • 高效的磁盘格式: 存储年的列作为一个 16 位整数可以很好地工作,需要两个字节来记录,但在 CSV 中,它总是用 4 个字节加上不同数量的开销 (逗号分隔符) 来写出。其结果是数据表示效率低下,可以通过压缩进行一定程度的改进。

  • 高效的读和写: CSV 是面向行的,这使得流解析更加容易,但也意味着你不能只加载一个特定的列。因为所有内容都表示为文本,所以许多数据格式 (从数字到日期) 在加载过程中都需要进行解析。

候选人 2: JSON

JSON 数据可以以多种不同的方式结构化,有些表面上是面向列的,例如:

{
    "name": ["Cambridge St.", "Hampshire St.", "Broadway"],
    "installation_year": [2017, 2023, 2024],
}

理论上,您的解析器可以利用这一点,并且只解析您感兴趣的列,但是并非所有 JSON 解析器都是这样。实际上,Pandas根本不会公开这种选择。

当然,数据可以通过许多其他方式进行格式化,比如:

[
    {"name": "Cambridge St.", "installation_year": 2017},
    {"name": "Hampshire St.", "installation_year": 2023},
    {"name": "Broadway", "installation_year": 2024},
]

或者像这样:

[
    ["name", "installation_year"],
    ["Cambridge St.", 2017],
    ["Hampshire St.", 2023],
    ["Broadway", 2024],
]

或者您可以使用 JSON-document-per-line 格式,其中有多个文档正在被解析,每个文档都有自己的一行:

{"name": "Cambridge St.", "installation_year": 2017}
{"name": "Hampshire St.", "installation_year": 2023}
{"name": "Broadway", "installation_year": 2024}

或者也许是更接近 CSV 的一种变体:

["name", "installation_year"]
["Cambridge St.", 2017]
["Hampshire St.", 2023]
["Broadway", 2024]

接下来是我们的标准:

  • 类型: JSON 确实有字符串和 64 位浮点数之类的类型,但是它仍然比 Pandas 支持的数据类型受到更多的限制。因此,最终您可能需要从字符串手动编码和解码数据。此外,JSON 不会在行之间或列之内强制执行一致的类型 (取决于您使用的结构,参见下文)。

    • 您可以使用一些外部模式系统 (如 JSON Schema) 来验证数据。或者,Pandas有内置的支持 表格模式 ,这是特定于它支持的表格数据类型,也允许它自动解析数据。
  • 高效的磁盘格式: 根据不同的结构,文件或多或少都是低效的,但没有一个最好的: installation_year 列作为 16 位整数可以很好地工作,需要两个字节来记录,但它总是以 4 个字节加上不同数量的开销来写出。通过压缩可以在一定程度上改进数据大小。

  • **高效的阅读和写作: ** 在阅读数据时,如果你使用 JSON-document-per-line ,Pandas可以使用 chunking 一次阅读几行。否则,它将在解析整个 JSON 文档之前将其读入内存。这会不必要地使用大量额外内存。

    • 在写入数据时,Pandas的实现在内存方面是非常低效的: 它会将数据呈现到内存中的一个字符串中,然后将其写入磁盘。

候选人 3: Parquet

Parquet 是一种专为Pandas处理的数据类型而设计的数据格式。

  • 类型: Parquet 支持各种整数和浮点数、日期、分类和 更多

  • 高效的磁盘格式: Parquet 使用数据的简洁表示,因此一个 16 位整数将占用两个字节。它还支持压缩。

  • 高效的读和写: 数据是按列存储的,所以你只能加载一些列,然后分成几块,这样你就不必在所有情况下加载所有行。此外,更加机器友好的数据表示意味着加载数据所需的解析要少得多。

对于所有这些标准,Parquet 是优越的。

标杆选择

为了了解真实世界的性能,我们将运行一个测试来加载一些由本地交通管理局生成的数据: 总线计时数据。它包括数字、分类和日期时间,因此允许我们查看加载各种数据类型的成本。

我们将使用以下 CSV 加载代码:

import sys
import pandas as pd


df = pd.read_csv(
    sys.argv[1],
    dtype={
        "route_id": "category",
        "direction_id": "category",
        "point_type": "category",
        "standard_type": "category",
    },
    parse_dates=["service_date", "scheduled", "actual"],
)

对于 JSON,我们将使用面向表的格式,其中包含一个由 DataFrame.to_json(orient='table') 创建的数据库:

import sys
import pandas as pd

df = pd.read_json(sys.argv[1], orient="table")

我们还将使用用 DataFrame.to_json(orient="records", lines=True) 创建的面向行的格式; Pandas为这种格式支持两种不同的实现或称为 “引擎”: “ujson”(默认值) ,以及 “pyarrow”

import sys
import pandas as pd

df = pd.read_json(
    sys.argv[1],
    orient="records",
    lines=True,
    dtype={
        "route_id": "category",
        "direction_id": "category",
        "point_type": "category",
        "standard_type": "category",
    },
    convert_dates=["service_date", "scheduled", "actual"],
    engine=sys.argv[2],
)

对于Parquet装载,我们将使用fastparquet,因为它似乎 更高效的内存 :

import sys
import panadas as pd

df = pd.read_parquet(sys.argv[1], engine="fastparquet")

结果

下面是加载数据的时间和内存的结果;对于所有数字,越小越好:

格式文件大小时钟秒CPU 秒峰值驻留内存
gzip CSV30 MB2.83.6787 MB
gzip JSON table36 MB10.010.86,796 MB
gzip JSON lines (ujson)31 MB11.212.08,056 MB
gzipped JSON lines (pyarrow)31MB1.04.41,330MB
Parquet (fast Parquet)20 MB0.41.2297 MB

性能测试的结果

  • 使用 Pandas 解析 JSON 代价很高;使用流式 JSON 解析器 可能更好

  • 如果你要使用JSON,使用JSON Lines格式,并使用“pyarrow”引擎

  • 如果你有一个数据格式的选择,Parquet 是最快的解析和使用最少的内存。

选择数据格式

技术解决方案是特定于上下文的,并且将根据您的特定情况而变化。尽管如此,Parquet 在许多情况下似乎是一种很好的数据格式:

  • 它支持多种类型。
  • 它是为了高效储存和高效装载而设计的。
  • 根据经验,它使用较少的 CPU 和内存加载比其他数据格式。
  • 很多库都支持: Pandas,还有其他替代品,比如 Polars 和 DuckDB。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

李星星BruceL

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值