You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

pyarrow存储含None的18位大整数到parquet时读取值异常

Parquet存储混合None的18位大整数出现精度丢失问题

问题现象

  • 使用Parquet格式存储和读取18位大整数时,读取结果与原始值不一致
  • 仅当存储的列表同时包含None和实际数值时触发该问题,列表无None时读写完全正常
  • 已排除显示类问题:使用cat、vi等Unix命令直接查看文件,确认不是展示偏差导致的数值异常,例如原始值235313013750949476读取后变为235313013750949472

测试代码

import pandas as pd
import pyarrow as pa
import pyarrow.parquet as pq

def get_row_list():
    row_list = []
    row_list.append(None)
    row_list.append(235313013750949476)
    row_list.append(None)
    row_list.append(135313013750949496)
    row_list.append(935313013750949406)
    row_list.append(835313013750949456)
    row_list.append(None)
    row_list.append(None)
    return row_list

def get_row_list_with_no_none():
    row_list = []
    row_list.append(235313013750949476)
    row_list.append(135313013750949496)
    row_list.append(935313013750949406)
    row_list.append(835313013750949456)
    return row_list

def create_parquet(row_list, col_list, parquet_filename):
    df = pd.DataFrame(row_list, columns=col_list)
    schema_field_list = [('tree_id', pa.int64())]
    pa_schema = pa.schema(schema_field_list)
    table = pa.Table.from_pandas(df, pa_schema)
    pq_writer = pq.ParquetWriter(parquet_filename,
                                 schema=pa_schema)
    pq_writer.write_table(table)
    pq_writer.close()
    print("Parquet file [%s] created" % parquet_filename)

def main():
    col_list = ['tree_id']
    # 无空值的测试列表
    row_list = get_row_list_with_no_none()
    print(row_list)
    create_parquet(row_list, col_list, 'without_none.parquet')
    # 混合空值的测试列表
    row_list = get_row_list()
    print(row_list)
    create_parquet(row_list, col_list, 'with_none.parquet')

# ==== 主程序入口 =====
if __name__ == '__main__':
    main()

执行日志

python test-parquet.py

[235313013750949476, 135313013750949496, 935313013750949406, 835313013750949456]
Parquet file [without_none.parquet] created
[None, 235313013750949476, None, 135313013750949496, 935313013750949406, 835313013750949456, None, None]
Parquet file [with_none.parquet] created

依赖版本

pyarrow                  5.0.0
pandas                   1.1.5
Python 3.6.6

Spark读取验证结果

无None的Parquet文件读取结果

>>> dfwithoutnone = spark.read.parquet("s3://some-bucket/without_none.parquet/")
>>> dfwithoutnone.count()
4
>>> dfwithoutnone.printSchema()
root
 |-- tree_id: long (nullable = true)

>>> dfwithoutnone.show(10, False)
+------------------+                                                            
|tree_id           |
+------------------+
|235313013750949476|
|135313013750949496|
|935313013750949406|
|835313013750949456|
+------------------+

含None的Parquet文件读取结果

>>> df_with_none = spark.read.parquet("s3://some-bucket/with_none.parquet/")
>>> df_with_none.count()
8                                                                               
>>> df_with_none.printSchema()
root
 |-- tree_id: long (nullable = true)

>>> df_with_none.show(10, False)
+------------------+
|tree_id           |
+------------------+
|null              |
|235313013750949472|
|null              |
|135313013750949504|
|935313013750949376|
|835313013750949504|
|null              |
|null              |
+------------------+

解决方案

根因

你使用的pandas 1.1.5版本原生整数类型不支持缺失值,当传入的列表同时包含整数和None时,pandas会自动将该列隐式转换为float64类型。而64位浮点数的有效精度只有53个二进制位,对应十进制最多可以精确表示16位整数,你用到的18位大整数超出了这个精度范围,转换为float时就已经发生了不可逆的精度丢失,后续即使指定pyarrow按int64存储,保存的也是精度丢失后的值。当列表中没有None时,pandas会直接将列识别为int64类型,没有转换过程,所以读写都正常。

修复方法

两种方案任选其一即可:

方案1:指定DataFrame使用pandas可空整数类型

构建DataFrame时,显式指定dtype为Int64(首字母大写,是pandas专门支持缺失值的整数类型):

df = pd.DataFrame(row_list, columns=col_list, dtype='Int64')

方案2:直接构造pyarrow数组,绕过pandas类型转换

不需要先转DataFrame,直接用Python列表构造pyarrow的int64数组,自动处理None为空值,从根源避免类型转换导致的精度丢失:

def create_parquet(row_list, col_list, parquet_filename):
    arr = pa.array(row_list, type=pa.int64())
    table = pa.Table.from_arrays([arr], names=col_list)
    pq_writer = pq.ParquetWriter(parquet_filename, schema=table.schema)
    pq_writer.write_table(table)
    pq_writer.close()
    print("Parquet file [%s] created" % parquet_filename)

修改后重新生成Parquet文件,再用Spark读取就不会出现精度丢失的问题。


内容的提问来源于stack exchange,提问作者madmatrix

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.29 09:09:02