使用Polars读取Excel文件时,将数值列转换为String(Utf8)类型如何保留所有小数位数
使用Polars读取Excel文件时,将数值列转换为String(Utf8)类型如何保留所有小数位数
我完全理解你的困扰——前端明明显示的是12位小数的精确数值,后端用Polars读取时,哪怕指定了列类型为Utf8,结果还是被截断了。这背后的原因其实很关键:
Polars默认依赖的Excel读取引擎(比如openpyxl)会先把Excel的数值单元格解析为Python浮点数,而浮点数本身的精度限制会导致小数位丢失,之后再转成字符串就已经是截断后的结果了——哪怕你提前用schema_overrides指定了Utf8类型也没用,因为精度丢失发生在Polars处理之前。
下面给你提供两种解决方案,推荐第一种,能彻底解决问题:
方案一:直接读取Excel单元格的显示文本(推荐)
要完全保留前端看到的精确小数位数,我们需要绕过浮点数解析,直接读取Excel单元格的显示文本内容(也就是前端xlsx库读到的那个原始字符串)。可以用openpyxl直接读取单元格文本,再构建Polars DataFrame:
- 确保安装了
openpyxl(Polars读取xlsx文件默认依赖它)
pip install openpyxl
- 修改后端的
parse_user_data函数:
from io import BytesIO from openpyxl import load_workbook import polars as pl def parse_user_data(contents: bytes): buffer = BytesIO(contents) # 用openpyxl加载Excel文件,保留单元格对象而非直接取值 wb = load_workbook(buffer, data_only=True) sheet = wb.active # 取第一个工作表,和你之前pl.read_excel的逻辑一致 # 遍历所有行,读取每个单元格的显示文本(和前端看到的完全一致) rows_data = [] for row in sheet.iter_rows(values_only=False): # 把每个单元格的显示文本转成字符串,空单元格设为None row_str = [cell.text.strip() if cell.value is not None else None for cell in row] rows_data.append(row_str) # 构建Polars DataFrame,列名规则和pl.read_excel(has_header=False)一致:column_0, column_1... df_raw = pl.DataFrame( rows_data, schema=[f"column_{i}" for i in range(len(rows_data[0]))] if rows_data else [] ) # 处理column_5:转小写,和你之前的逻辑保持一致 df_raw = df_raw.with_columns(pl.col("column_5").str.to_lowercase()) print(df_raw.select(pl.col("column_5")).head(20))
这个方法的核心是cell.text——它会直接返回Excel单元格显示的原始文本内容,也就是前端看到的12位小数的精确字符串,完全绕过了浮点数解析的精度丢失问题。生成的Polars DataFrame列类型默认是Utf8,自然能保留所有小数位。
方案二:尝试修改Polars读取的引擎参数(不推荐,稳定性差)
如果你想坚持用pl.read_excel,可以尝试通过engine_kwargs传递参数给openpyxl,但这个方法依赖引擎的具体实现,稳定性不如方案一:
def parse_user_data(contents: bytes): buffer = BytesIO(contents) df_raw = pl.read_excel( buffer, has_header=False, engine="openpyxl", # 传递给openpyxl的参数,尝试保留原始值的字符串表示 engine_kwargs={"data_only": True}, schema_overrides={"column_5": pl.Utf8}, ).with_columns(pl.col(pl.Utf8).str.to_lowercase()) print(df_raw.select(pl.col("column_5")).head(20))
不过这个方案大概率还是会遇到截断问题,因为openpyxl本身读取数值单元格时还是会先转成浮点数,所以更推荐方案一。




