PySpark DataFrame调用erfinv函数报scipy缺失,求替代实现方案
问题复现
在CDSW环境中运行如下Spark代码:
df = spark.createDataFrame([ (1, 0.008), (2, -1.23), (3, 4.56), ], ['id', 'value']) def normal_cdf(x): val = (math.sqrt(2) * erfinv(x*2-1)) return float(val) my_udf1 = udf(normal_cdf) #my_udf1 = udf(normal_cdf,T.DoubleType()) for returning double df1 = df.withColumn('prob', my_udf1(F.col('value'))) df1.show()
执行后报错根因为:ModuleNotFoundError: No module named 'scipy'
但单独运行如下代码可以正常执行:
n=math.sqrt(2)*erfinv(2*0.008-1) print(n)
当前环境无权限安装或升级第三方依赖,需要无需额外依赖的erfinv计算方案。
问题根因
你本地Driver节点的Python环境已经导入了scipy的erfinv函数,所以单独运行代码正常。但Spark UDF会被分发到Executor节点执行,Executor节点的Python环境没有安装scipy,因此触发依赖报错。
解决方案
方案1:使用Spark内置函数(最优)
Spark 3.0及以上版本自带probit内置函数,逻辑完全等价于你需要实现的√2 * erfinv(2p-1),也就是标准正态分布的分位数函数,无需自定义UDF,完全避免依赖问题,同时执行性能远高于Python UDF。
实现代码如下:
from pyspark.sql import functions as F df1 = df.withColumn('prob', F.probit(F.col('value'))) df1.show()
方案2:手动实现erfinv近似算法(兼容低版本Spark)
如果你的Spark版本低于3.0没有内置probit函数,可以使用精度足够的erfinv近似展开实现,仅依赖Python标准库math,无需任何第三方包:
import math from pyspark.sql import functions as F from pyspark.sql import types as T def erfinv_approx(x): # 近似算法精度可达1e-4,满足绝大多数业务场景使用 a = 0.147 if abs(x) > 1: return float('nan') elif abs(x) == 1: return float('inf') * x ln1mx2 = math.log(1 - x**2) part1 = 2/(math.pi * a) + ln1mx2 / 2 part2 = ln1mx2 / a return math.copysign(math.sqrt(math.sqrt(part1**2 - part2) - part1), x) def normal_cdf(x): val = math.sqrt(2) * erfinv_approx(x * 2 - 1) return float(val) my_udf1 = F.udf(normal_cdf, T.DoubleType()) df1 = df.withColumn('prob', my_udf1(F.col('value'))) df1.show()
内容的提问来源于stack exchange,提问作者Amy Jack
相关产品推荐
相关产品推荐

