pandas 搞乱多级索引 parquet 浮动精度

pandas mess up multi level index parquet float accuracy

我有一些数据具有多级索引和列。我的目标是将数据存储为 float32 而不是 double/float64 到镶木地板文件中,以节省磁盘 space。我不关心单元格值的准确性,但我确实希望索引和列本身(此处为 number 级别)保持为 float64 并保持准确。

然而,在下面的例子中,它把 7080.39 弄乱了,变成了 7080.39013671875。我知道它可能是怎么发生的,但想不出解决它的方法。

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

df = pd.DataFrame({"col1": [1.0, 2.0, 3.0], "col2": [2.3, 2.4, 2.5], "col3": [3.1, 3.2, 3.3]})
df.index = pd.MultiIndex.from_tuples([('a', '2021-01-01', 100), ('a', '2021-01-01', 200), ('a', '2021-01-01', 7080.39)], names=('name', 'date', 'number'))
df.columns = pd.MultiIndex.from_tuples([('a', '2021-01-01', 100), ('a', '2021-01-01', 200), ('a', '2021-01-01', 7080.39)], names=('name', 'date', 'number'))

# write to parquet as float64/double
table = pa.Table.from_pandas(df)
pq.write_table(table, 'double.parquet')

# cast to float32 and write to parquet
schema = pa.schema([pa.field(field.name, pa.float32() if field.type == pa.float64() else field.type) for field in table.schema], metadata=table.schema.metadata)
table = table.cast(schema)
pq.write_table(table, 'float.parquet')

# now I read the data in, but as you can see, the "number" level in df2 index & column is messed up. I didn't change the type of it, but 7080.39 becomes 7080.39013671875, which is the float32 representation of 7080.39
df1 = pd.read_parquet('double.parquet')
df2 = pd.read_parquet('float.parquet')

df1.index
#MultiIndex([('a', '2021-01-01',   100.0),
#            ('a', '2021-01-01',   200.0),
#            ('a', '2021-01-01', 7080.39)],
#           names=['name', 'date', 'number'])

df2.index
#MultiIndex([('a', '2021-01-01',            100.0),
#            ('a', '2021-01-01',            200.0),
#            ('a', '2021-01-01', 7080.39013671875)],
#       names=['name', 'date', 'number'])

这真的很烦人,因为我需要将这个 DataFrame 与许多其他复杂的东西连接起来,而这个 7080.39013671875 找不到匹配项。

关于如何修复它有什么建议吗? (不是如何将 7080.39013671875 转换回 7080.39 而是如何首先防止这种情况发生,因为我不想改变我的索引类型)

如果你这样做 print(table.schema) 你可以看到它来自:

('a', '2021-01-01', '100.0'): double
('a', '2021-01-01', '200.0'): double
('a', '2021-01-01', '7080.39'): double
name: string
date: string
number: double

至(投后):

('a', '2021-01-01', '100.0'): float
('a', '2021-01-01', '200.0'): float
('a', '2021-01-01', '7080.39'): float
name: string
date: string
number: float

您只需确保索引列未从 double/float64 转换为 float/float32。这里有一个 hacky 的方法来做到这一点:

schema = pa.schema([pa.field(field.name, pa.float32() if field.type == pa.float64() and '(' not in field.name else field.type) for field in table.schema], metadata=table.schema.metadata)
table = table.cast(schema)

这给你:

('a', '2021-01-01', '100.0'): double
('a', '2021-01-01', '200.0'): double
('a', '2021-01-01', '7080.39'): double
name: string
date: string
number: float