在对数据精度要求较高的任务中,将数据从数据源加载到数据库时不能损失精度,这时通常会使用到
D e c i m a l Decimal Decimal 函数,有时候这些数据会导致异常,因为 p a n d a s pandas pandas 不支持涉及 D e c i m a l Decimal Decimal 和 f l o a t float float 的混合运算,所以必须先统一数据类型
最简单粗暴的应对方法当然是直接对涉及运算的数值列进行
astype("float64")
操作了,不过缺点就是在代码量很大的情况下,这么做需要在多处修改,不仅麻烦还容易漏
所以我决定从源头解决问题 -> 把数据从Database捞过来后马上转型,这样就不用修改后续需要执行的代码了
捞数
from pyspark.sql import SparkSession
session = SparkSession.builder.getOrCreate()
spark_df = session.sql('select * from testing_table')# 展示列及其数据类型,返回的是元组列表
spark_df.dtypes
# [('name', 'string'),# ('price', 'decimal(16,8)'),# ('rate', 'decimal(16,8)')]
判断、转型
由于16位有效数字已经满足我任务的精度要求,所以下面的代码直接将
D e c i m a l ( 16 , 8 ) Decimal(16,8) Decimal(16,8) 转为双精度 D o u b l e T y p e DoubleType DoubleType ,因为它们的有效数字都是16位,但在更高精度的场景下则不能这么转,如 D e c i m a l ( 32 , 16 ) Decimal(32,16) Decimal(32,16) 转为 D o u b l e T y p e DoubleType DoubleType 会损失精度
pyspark -
cast
from pyspark.sql.types import DoubleType
from pyspark.sql.functions import col
for c in spark_df.columns:if'decimal'indict(spark_df.dtypes)[c]:
spark_df = spark_df.withColumn(c, col(c).cast(DoubleType()))
pandas -
astype
|
float
在
p a n d a s pandas pandas 这边转也行,但它 d t y p e s dtypes dtypes 接口的算法比较迷,经常会把数值列的类型判断为 o b j e c t object object 而不是 f l o a t 64 float64 float64,因此列类型的判断还是得在转 p a n d a s pandas pandas前做
decimal_cols =[k for k, v in spark_df.dtypes if'decimal'in v]
pandas_df = spark_df.toPandas()for col in decimal_cols:
pandas_df[col]= pandas_df[col].astype('float64')# 在有脏数据的情况下,只能一个一个地判断了# 备用方案from decimal import Decimal
pandas_df[col]= pandas_df[col].apply(lambda x:float(x)ifisinstance(x, Decimal)else x)
参考:
PySpark basics
版权归原作者 Python捞数人 所有, 如有侵权,请联系我们删除。