“pyarrow.lib.ArrowInvalid:从时间戳 [ns] 转换为时间戳 [ms] 会丢失数据”在将数据发送到没有架构的 BigQuery 时

Posted

技术标签:

【中文标题】“pyarrow.lib.ArrowInvalid:从时间戳 [ns] 转换为时间戳 [ms] 会丢失数据”在将数据发送到没有架构的 BigQuery 时【英文标题】:"pyarrow.lib.ArrowInvalid: Casting from timestamp[ns] to timestamp[ms] would lose data" when sending data to BigQuery without schema 【发布时间】:2020-04-28 04:07:36 【问题描述】:

我正在编写一个向 BigQuery 发送数据帧的脚本:

load_job = bq_client.load_table_from_dataframe(
    df, '.'.join([PROJECT, DATASET, PROGRAMS_TABLE])
)

# Wait for the load job to complete
return load_job.result() 

这可以正常工作,但前提是已经在 BigQuery 中定义了架构,或者我在脚本中定义了我的作业架构。如果未定义任何架构,则会出现以下错误:

Traceback (most recent call last): File "/env/local/lib/python3.7/site-packages/google/cloud/bigquery/client.py", line 1661, in load_table_from_dataframe dataframe.to_parquet(tmppath, compression=parquet_compression) File "/env/local/lib/python3.7/site-packages/pandas/core/frame.py", line 2237, in to_parquet **kwargs File "/env/local/lib/python3.7/site-packages/pandas/io/parquet.py", line 254, in to_parquet **kwargs File "/env/local/lib/python3.7/site-packages/pandas/io/parquet.py", line 117, in write **kwargs File "/env/local/lib/python3.7/site-packages/pyarrow/parquet.py", line 1270, in write_table writer.write_table(table, row_group_size=row_group_size) File "/env/local/lib/python3.7/site-packages/pyarrow/parquet.py", line 426, in write_table self.writer.write_table(table, row_group_size=row_group_size) File "pyarrow/_parquet.pyx", line 1311, in pyarrow._parquet.ParquetWriter.write_table File "pyarrow/error.pxi", line 85, in pyarrow.lib.check_status pyarrow.lib.ArrowInvalid: Casting from timestamp[ns] to timestamp[ms] would lose data: 1578661876547574000 During handling of the above exception, another exception occurred: Traceback (most recent call last): File "/env/local/lib/python3.7/site-packages/google/cloud/functions/worker.py", line 383, in run_background_function _function_handler.invoke_user_function(event_object) File "/env/local/lib/python3.7/site-packages/google/cloud/functions/worker.py", line 217, in invoke_user_function return call_user_function(request_or_event) File "/env/local/lib/python3.7/site-packages/google/cloud/functions/worker.py", line 214, in call_user_function event_context.Context(**request_or_event.context)) File "/user_code/main.py", line 151, in main df = df(param1, param2) File "/user_code/main.py", line 141, in get_df df, '.'.join([PROJECT, DATASET, PROGRAMS_TABLE]) File "/env/local/lib/python3.7/site-packages/google/cloud/bigquery/client.py", line 1677, in load_table_from_dataframe os.remove(tmppath) FileNotFoundError: [Errno 2] No such file or directory: '/tmp/tmp_ps5xji9_job_634ff274.parquet'

为什么pyarrow 会产生这个错误?除了预定义架构之外,我该如何解决它?

【问题讨论】:

【参考方案1】:

从 pandas 转换为 Arrow 或 Parquet 时的默认行为是不允许静默数据丢失。在进行转换时可以设置一些选项,以允许不安全的强制转换导致时间戳精度丢失或其他形式的数据丢失。 BigQuery Python API 需要设置这些选项,因此它可能是 BigQuery 库中的一个错误。我建议报告他们的问题跟踪器https://github.com/googleapis/google-cloud-python

【讨论】:

我在尝试将查询从 bigquery 转换为_dataframe 时也遇到了类似的错误,而且关于 bigquery 的文档很少。 ArrowInvalid: Casting from timestamp[us, tz=UTC] to timestamp[ns] would result in out of bounds timestamp: -61535548800000000【参考方案2】:

我收到此错误:ArrowInvalid: Casting from timestamp[ns] to timestamp[us, tz=UTC] would lose data: 1602633600999999998

当我检查数据框时,我看到了这样的值:2021-09-30 23:59:59.999999998

然后我使用了这个代码:

df['date_column'] =df['date_column'].astype('datetime64[s]')

那么问题就为我解决了。

【讨论】:

【参考方案3】:

我认为出现这些错误是因为 BigQuery 库使用的 pyarrow.parquet 模块确实将 Python 的内置日期时间或时间类型转换为 BigQuery 默认识别的类型,但 BigQuery 库确实有自己的转换方法熊猫类型

我能够通过将 datetime.datetime 或 time.time 的所有实例更改为 pandas.Timestamp 来让它上传时间戳。例如:

my_df['timestamp'] = datetime.utcnow()

需要改成

my_df['timestamp'] = pd.Timestamp.now()

【讨论】:

这是一个严重的错误。如何处理从 API 调用加载数据并需要在那里设置 dtype 的情况?我只对保持日期感兴趣,但它会抛出错误说此转换将丢失timestamp @ShivamSahil 根据我的测试,pandas 1.1.0 修复了这个问题。如果您的应用程序需要旧版本的 pandas,我建议使用 pandas-gbq,它会序列化为 CSV 而不是 Parquet。【参考方案4】:

在我对https://github.com/googleapis/python-bigquery-pandas/pull/413 的测试中,通过升级到pandas 1.1.0+ 解决了这个问题。

查看pandas 1.1.0 changelog,已经修复了几个与时间戳数据相关的错误。我不确定哪一个特别会在这里有所帮助,但可能是混合和匹配不同时区的修复。 https://pandas.pydata.org/pandas-docs/dev/whatsnew/v1.1.0.html#parsing-timezone-aware-format-with-different-timezones-in-to-datetime

【讨论】:

【参考方案5】:

我的解决方案是将以下 kwargs 添加到 to_parquet:

parquet_args = 
    'coerce_timestamps': 'us',
    'allow_truncated_timestamps': True,

你必须同时设置它们。如果您只设置allow_truncated_timestamps,如果coerce_timestampsNone,它仍然会引发错误。我认为这个想法是,如果您明确要求强制,您只想抑制错误。无论如何,文档对此很清楚,但这种行为对我来说并不明显。

【讨论】:

以上是关于“pyarrow.lib.ArrowInvalid:从时间戳 [ns] 转换为时间戳 [ms] 会丢失数据”在将数据发送到没有架构的 BigQuery 时的主要内容,如果未能解决你的问题,请参考以下文章

将 Pandas Dataframe 转换为 Parquet 失败:列表子类型字符串溢出单个块的容量