Apache spark Pyspark dataframe:如何按组应用scipy.optimize函数
我有一段代码,它运行良好,但使用pandas数据帧分组处理。 但是,由于文件很大(>7000万组),我需要将代码转换为使用PYSPARK数据帧。 以下是使用pandas dataframe和小示例数据的原始代码:Apache spark Pyspark dataframe:如何按组应用scipy.optimize函数,apache-spark,dataframe,group-by,pyspark,apache-spark-sql,Apache Spark,Dataframe,Group By,Pyspark,Apache Spark Sql,我有一段代码,它运行良好,但使用pandas数据帧分组处理。 但是,由于文件很大(>7000万组),我需要将代码转换为使用PYSPARK数据帧。 以下是使用pandas dataframe和小示例数据的原始代码: import pandas as pd import numpy as np from scipy.optimize import minimize df = pd.DataFrame({ 'y0': np.random.randn(20), 'y1': np.random.rand
import pandas as pd
import numpy as np
from scipy.optimize import minimize
df = pd.DataFrame({
'y0': np.random.randn(20),
'y1': np.random.randn(20),
'x0': np.random.randn(20),
'x1': np.random.randn(20),
'grpVar': ['a', 'b'] * 10})
# Starting values
startVal = np.ones(2)*(1/2)
#Constraint Sum of coefficients = 0
cons = ({'type':'eq', 'fun': lambda x: 1 - sum(x)})
# Bounds on coefficients
bnds = tuple([0,1] for x in startVal)
# Define a function to calculate sum of squared differences
def SumSqDif(a, df):
return np.sum((df['y0'] - a[0]*df['x0'])**2 + (df['y1'] - a[1]*df['x1']) **2)
# Define a function to call minimize function
def RunMinimize(data, startVal, bnds, cons):
ResultByGrp = minimize(SumSqDif, startVal, method='SLSQP',
bounds=bnds, constraints = cons, args=(data))
return ResultByGrp.x
# Do the calculation by applyng the function by group:
# Create GroupBy object
grp_grpVar = df.groupby('grpVar')
Results = grp_grpVar.apply(RunMinimize, startVal=startVal, bnds=bnds, cons=cons))
现在我正在尝试使用pySpark数据帧
为了测试代码,我将pandas数据帧转换为pyspark数据帧
sdf = sqlContext.createDataFrame(df)
type(sdf)
# <class 'pyspark.sql.dataframe.DataFrame'>
# Create GroupBy object
Sgrp_grpVar = sdf.groupby('grpVar')
# Redefine functions
def sSumSqDif(a, sdf):
return np.sum((sdf['y0'] - a[0]*sdf['x0'])**2 + (sdf['y1'] - a[1]*sdf['x1'])**2)
def sRunMinimize(data=sdf, startVal=startVal, bnds=bnds, cons=cons):
ResultByGrp = minimize(sSumSqDif, startVal, method='SLSQP',
bounds=bnds, constraints = cons, args=(data))
return ResultByGrp.x
from pyspark.sql.functions import UserDefinedFunction
from pyspark.sql.types import DoubleType
from pyspark.sql.types import StringType
udf = UserDefinedFunction(sRunMinimize , StringType())
Results = Sgrp_grpVar.agg(sRunMinimize())
sdf=sqlContext.createDataFrame(df)
类型(sdf)
#
#创建GroupBy对象
Sgrp_grpVar=sdf.groupby('grpVar'))
#重新定义函数
def sSumSqDif(a,sdf):
返回np.sum((sdf['y0']-a[0]*sdf['x0'])**2+(sdf['y1']-a[1]*sdf['x1'])**2)
def sRunMinimize(数据=sdf,startVal=startVal,bnds=bnds,cons=cons):
ResultByGrp=最小化(sSumSqDif,startVal,方法='SLSQP',
边界=bnds,约束=cons,参数=(数据))
返回结果bygrp.x
从pyspark.sql.functions导入UserDefinedFunction
从pyspark.sql.types导入DoubleType
从pyspark.sql.types导入StringType
udf=UserDefinedFunction(srunnimize,StringType())
结果=Sgrp_grpVar.agg(sRunMinimize())
但是,在我尝试定义用户定义的函数udf之后,我得到了以下错误-请参见下文。
非常感谢您对我纠正错误或提出替代方法的任何帮助
udf=UserDefinedFunction(srunnimize,StringType())
回溯(最近一次呼叫最后一次):
文件“”,第1行,在
文件“/usr/hdp/current/spark2 client/python/pyspark/sql/functions.py”,第1760行,在init
self.\u judf=self.\u create\u judf(name)….您正在尝试编写一个用户定义的聚合函数,该函数无法在pyspark中完成 您可以编写一个
UDF
,用于收集每个组中的数据,作为列表:
首先是设置:
将熊猫作为pd导入
将numpy作为np导入
从scipy.optimize导入最小化
将pyspark.sql.functions作为psf导入
从pyspark.sql.types导入*
df=pd.DataFrame({
“y0”:np.random.randn(20),
“y1”:np.random.randn(20),
“x0”:np.random.randn(20),
'x1':np.random.randn(20),
'grpVar':['a','b']*10})
sdf=sqlContext.createDataFrame(df)
#起始值
startVal=np.ones(2)*(1/2)
#系数的约束和=0
cons=({'type':'eq','fun':lambda x:1-sum(x)})
#系数的界
bnds=元组([0,1]表示startVal中的x)
我们将广播这些变量,因为我们需要在聚合数据帧的每一行上调用它们,它会将值复制到每个节点,这样它们就不必在驱动程序上获取它们:
sc.broadcast(startVal)
sc.广播(bnds)
让我们使用collect\u list
聚合数据,我们将更改周围数据的结构,使我们只有一列(您可以将每列收集到不同的列中,但随后必须修改将数据传递给函数的方式):
Sgrp\u grpVar=sdf\
.groupby(“grpVar”)\
.agg(psf.collect_list(psf.struct(“y0”、“y1”、“x0”、“x1”))。别名(“数据”))
Sgrp_grpVar.printSchema()
根
|--grpVar:string(nullable=true)
|--数据:数组(nullable=true)
||--元素:struct(containsnall=true)
|| |--y0:double(nullable=true)
|| |--y1:double(nullable=true)
|| |--x0:double(nullable=true)
|| |--x1:double(nullable=true)
我们现在可以创建我们的UDF
,返回的数据类型对于pyspark来说太复杂了,numpy数组
不受pyspark支持,因此我们需要对其进行一些更改:
def sSumSqDif(a,数据):
返回np.sum(
(数据['y0']-a[0]*数据['x0'])**2\
+(数据['y1']-a[1]*数据['x1'])**2)
def sRunMinimize(数据,startVal=startVal,bnds=bnds,cons=cons):
data=pd.DataFrame({k:v代表k,v在zip中([“y0”、“y1”、“x0”、“x1”],data)})
ResultByGrp=最小化(sSumSqDif,startVal,方法='SLSQP',
边界=bnds,约束=cons,参数=(数据))
返回结果bygrp.x.tolist()
srunnimize_udf=lambda startVal,bnds,cons:psf.udf(
lambda数据:sRunMinimize(数据、startVal、bnds、cons),
ArrayType(DoubleType())
)
我们现在可以将此功能应用于每个组中收集的数据:
Results=sdf\u agg.select(
“grpVar”,
sRunMinimize_udf(startVal、bnds、cons)(“数据”)。别名(“res”)
)
Results.show(truncate=False)
+------+-----------------------------------------+
|grpVar | res|
+------+-----------------------------------------+
|b |[0.4073139282953772,0.5926860717046227]|
|a |[0.8275186444565927,0.17248135554340727]|
+------+-----------------------------------------+
但我不认为pyspark是实现这一点的合适工具。我的第一个观察结果是,不能将整个Spark数据帧作为参数发送给udf,只能发送数据帧的列。玛丽,非常感谢你的解决方案。我将用我的真实数据尝试它。它很大:大约6亿条记录/7000万组和12个变量(在我的示例中是x0..x6和y0..y6)。在您看来,如果pyspark不适合此问题,那么什么是合适的?Marie,在对大型数据集运行代码之前,我尝试了代码,因为它使用的是小型示例数据。在最后一步中,它给了我一个错误:回溯(最近的调用last):文件“”,第1行,在NameError中:未定义名称“sdf_agg”。您需要在所有节点上安装scikit learn或zip anaconda并使用--archive。您为每行调用的函数都需要加载这些模块。最佳解决方案是在每个节点上安装anaconda