如何快速的在PySpark中跑原始的python代码
Introducing Pandas UDF for PySpark - The Databricks Blogdatabricks.com- Scalar Pandas UDFs(标量的pandas UDF函数)
在pyspark中假如要给一个dataframe的某一列执行+1的操作,以前的操作方式是
from pyspark.sql.functions import udf
#Use udf to define a row-at-a-time udf
@udf('double')
# Input/output are both a single double value
def plus_one(v):
return v + 1
df.withColumn('v2',plus_one(df.v))
使用Pandas UDFs:
from pyspark.sql.functions import pandas_udf, PandasUDFType
# Use pandas_udf to define a Pandas UDF
@pandas_udf('double', PandasUDFType.SCALAR)
# Input/output are both a pandas.Series of doubles
def pandas_plus_one(v):
return v+1
df.withColumn('v2',pandas_plus_one(df.v))在
在第一种方法中 row-at-a-time版本中,udf函数输入的是一个double类型的'v',结果也是double类型的'v+1',在pandas udf中,输入的是pandas.Series类型的'v',输出也是,所以pandas类型的速度是比第一种快很多的;
2. Cumulative Probability
下面这个例子是用户pandas UDF更具实战性的例子:使用scipy去计算一个值的累计正太分布概率值
import pandas as pd
from scipy import stats
@pandas_udf('double'):
def cdf(v):
return pd.Series(stats.norm.cdf(v))
df.withColumn('cumulative_probability',cdf(df.v))
stats.norm.cdf 可以使用在一个标量或者是一个pandas.Series上面,然后上面的这个例子是用row-at-a-time UDFs写的
3. Grouped Map Pandas UDFs
python的使用者都非常熟悉 split-apply-combine的数据分析的模式,Grouped Map Pandas UDFs也可以在这个场景中使用
Grouped map Pandas UDFs首先将一个Spark DataFrame根据groupby的操作分成多个组,然后应用user-defined function(pandas.DataFrame -> pandas.DataFrame)到每个组,然后将这些结果合并成一个新的Spark DataFrame.
Grouped map Pandas UDFs和在标量上使用的pandas udf 使用相同的函数操作,但是他们也有一些不同点
- Input of the user-defined function:
- Scalar : pandas.Series
- Grouped map : pandas.DataFrame
- Output of the user-defined function
- Scalar : pandas.Series
- Grouped map : pandas.DataFrame
- Grouping semantics:
- Scalar : no grouping semantics
- Grouped map : defined by "groupby" clause
- Output size:
- Scalar : same as input size
- Grouped map : any size
- Return types in the function decorator:
- Scalar : a DataType that specifies the type of the returned pandas.Series
- Grouped map : a StructType that specifies each column name and type of the returned pandas.DataFrame
下面将使用两个例子去演示grouped map Pandas UDFs的用法
Subtract Mean
这个例子展示了一个简单的grouped map Pandas UDFs的用法,减去每个组的组内均值
@pandas_udf(df.schema,PandasUDFType.GROUPED_MAP)
##Input/output are both a pandas.DataFrame
def subtract_mean(pdf):
return pdf.assign(v = pdf.v - pdf.v.mean()
df.groupby('id').apply(subtract_mean)
在这个例子中,我们在每个组的v列上面减去了v的均值,分组的语法是根据groupby函数实现的,然后每个输入UDF函数的pandas.DataFrame有同样的id值,输入的schema和输出的schema在这个udf是一样的,所以直接用df.schema作为参数输入到pandas_udf里面
Grouped map Pandas UDFs 也被叫做standalone Python functions,然后可以用下面的方式来进行debug
sample = df.filter(id == 1).toPandas()
# Run as a standalone function on a pandas.DataFrame and verify result
subtract_mean.func(sample)
# Now run with Spark
df.groupby('id').apply(substract_mean)
Ordinary Least Squares Linear Regression
下面的例子展示怎么在每个组上面去拟合一个OLS 线性回归,对于每个组,我们计算beta b = (b1,b2) for X = (x1,x2) 根据统计模型 Y = bX + c
import statsmodels.api as sm
## df has four columns : id ,y ,x1, x2
group_column = 'id'
y_column = 'y'
x_columns = ['x1','x2']
schema = df.select(group_column,*x_columns).schema
@pandas_udf(schema, PandasUDFType.GROUPED_MAP)
# Input/output are both a pandas.DataFrame
def ols(pdf):
group_key = pdf[group_column].iloc[0]
y = pdf[y_column]
X = pdf[x_columns]
X = sm.add_constant(X)
model = sm.OLS(y,X).fit()
return pd.DataFrame([[group_key] + [model.params[i] for i in x_columns]], columns=[group_column] + x_columns)
beta = df.groupby(group_column).apply(ols)