pandas.to_parquet pyarrow.lib.ArrowInvalid: 无法转换时间差
我有一个很大的多重索引数据框,格式是长格式,里面只有一列“值”。有些“值”的类型是 pd.Timedelta
。
当我尝试用 pd.to_parquet
把这个数据框保存为 parquet
文件时,出现了一个错误:
pyarrow.lib.ArrowInvalid: ("无法将 Timedelta('2903 days 00:00:00') 转换为 double 类型", '转换失败,值列的类型为对象')
如果我把错误信息中的特定值转换成 numpy
,我得到的是:
array([Timedelta('2903 days 00:00:00')], dtype=object)
我做了一个简单的例子,想看看是否可以把 pd.Timedelta
转换成 parquet
。下面的代码运行得很好:
import pandas as pd
idx = pd.MultiIndex.from_tuples(
[("A", "x"), ("A", "y"), ("B", "x"), ("B", "y")],
names=["idx1", "idx2"])
data = {"value": [
pd.Timedelta(days=10),
2.5,
pd.Timedelta(days=20),
5
]}
df = pd.DataFrame(data=data, index=idx)
df.to_parquet("test.parquet")
x = pd.read_parquet("test.parquet")
用 df.iloc[0, :].to_numpy()
得到的类型和我真实数据框中的完全一样:array([Timedelta('10 days 00:00:00')], dtype=object)
。
我在想,我的原始数据框和这个简单例子之间可能有什么不同呢?
1 个回答
你看到的这个错误信息 'Conversion failed for column value with type object'
,说明你的这一列数据里混合了浮点数和时间差(time delta),正如你所预期的那样。
问题出在,pyarrow在处理一个很大的数据表时,会根据pandas数据表的前几行来推测数据类型。在你的情况下,这些前几行很可能都是浮点数。下面这段代码(和你之前的例子类似,只是稍微改动了一下)会重现你的错误:
import pandas as pd
float_vals = [2.5 for i in range(10000)]
float_index_val = [("float", i) for i in range(10000)]
float_vals.extend([("A", "x"), ("A", "y"), ("B", "x"), ("B", "y")])
float_vals.extend([
pd.Timedelta(days=2903),
2.5,
pd.Timedelta(days=20),
5
])
idx = pd.MultiIndex.from_tuples(
float_vals,
names=["idx1", "idx2"])
data = {"value": float_vals}
df = pd.DataFrame(data=data, index=idx)
df.to_parquet("test.parquet")
x = pd.read_parquet("test.parquet")
这里的问题是,parquet格式的列不能同时包含多种类型。在你的例子中,如果你加载保存的parquet文件,你会发现所有数据都被转换成了时间差。例如,使用 x = pd.read_parquet("test.parquet")
然后 x.iloc[1, :].to_numpy()
会得到 array([2], dtype='timedelta64[us]')
这样的结果。
如果浮点数值并不表示时间差,你应该按照评论的建议,先把数据进行“解透视”(unpivot)。如果浮点数值确实表示时间差,你可以在保存之前进行转换,或者在保存时指定一个数据结构(schema)。
df.value = pd.to_timedelta(df.value)
df.to_parquet("test.parquet")
或者
df.to_parquet("test.parquet", schema=pa.schema([("value", pa.duration("s"))]))