toh*_*ong 4 broadcast user-defined-functions dataframe apache-spark pyspark
在另一个数据帧上执行UDF时,如何引用pyspark数据帧?
这是一个虚拟的例子。我创建了两个dataframes scores和lastnames,并在各躺着一个列,它是在两个dataframes相同。在应用的UDF中scores,我要过滤lastnames并返回在中找到的字符串lastname。
from pyspark import SparkContext
from pyspark import SparkConf
from pyspark.sql import SQLContext
from pyspark.sql.types import *
sc = SparkContext("local")
sqlCtx = SQLContext(sc)
# Generate Random Data
import itertools
import random
student_ids = ['student1', 'student2', 'student3']
subjects = ['Math', 'Biology', 'Chemistry', 'Physics']
random.seed(1)
data = []
for (student_id, subject) in itertools.product(student_ids, subjects):
data.append((student_id, subject, random.randint(0, 100)))
from pyspark.sql.types import StructType, StructField, IntegerType, StringType
schema = StructType([
StructField("student_id", StringType(), nullable=False),
StructField("subject", StringType(), nullable=False),
StructField("score", IntegerType(), nullable=False)
])
# Create DataFrame
rdd = sc.parallelize(data)
scores = sqlCtx.createDataFrame(rdd, schema)
# create another dataframe
last_name = ["Granger", "Weasley", "Potter"]
data2 = []
for i in range(len(student_ids)):
data2.append((student_ids[i], last_name[i]))
schema = StructType([
StructField("student_id", StringType(), nullable=False),
StructField("last_name", StringType(), nullable=False)
])
rdd = sc.parallelize(data2)
lastnames = sqlCtx.createDataFrame(rdd, schema)
scores.show()
lastnames.show()
from pyspark.sql.functions import udf
def getLastName(sid):
tmp_df = lastnames.filter(lastnames.student_id == sid)
return tmp_df.last_name
getLastName_udf = udf(getLastName, StringType())
scores.withColumn("last_name", getLastName_udf("student_id")).show(10)
Run Code Online (Sandbox Code Playgroud)
以下是跟踪的最后一部分:
Py4JError: An error occurred while calling o114.__getnewargs__. Trace:
py4j.Py4JException: Method __getnewargs__([]) does not exist
at py4j.reflection.ReflectionEngine.getMethod(ReflectionEngine.java:335)
at py4j.reflection.ReflectionEngine.getMethod(ReflectionEngine.java:344)
at py4j.Gateway.invoke(Gateway.java:252)
at py4j.commands.AbstractCommand.invokeMethod(AbstractCommand.java:133)
at py4j.commands.CallCommand.execute(CallCommand.java:79)
at py4j.GatewayConnection.run(GatewayConnection.java:209)
at java.lang.Thread.run(Thread.java:745)
Run Code Online (Sandbox Code Playgroud)
将对更改为字典以便轻松查找名称
data2 = {}
for i in range(len(student_ids)):
data2[student_ids[i]] = last_name[i]
Run Code Online (Sandbox Code Playgroud)
而不是创建rdd并使其df创建广播变量
//rdd = sc.parallelize(data2)
//lastnames = sqlCtx.createDataFrame(rdd, schema)
lastnames = sc.broadcast(data2)
Run Code Online (Sandbox Code Playgroud)
现在使用values广播变量 ( lastnames) 上的 attr 在 udf 中访问它。
from pyspark.sql.functions import udf
def getLastName(sid):
return lastnames.value[sid]
Run Code Online (Sandbox Code Playgroud)
您不能直接从UDF内部引用数据框(或RDD)。DataFrame对象是驱动程序上的句柄,spark用来表示将在集群上发生的数据和操作。在选择Spark时,UDF中的代码将在集群上用完。Spark通过序列化该代码,并制作闭包中包含的任何变量的副本并将其发送给每个工作程序来实现。
相反,您要做的是使用Spark在其API中提供的构造来联接/组合两个DataFrame。如果其中一个数据集很小,则可以手动将数据发送到广播变量中,然后从UDF访问它。否则,您可以像创建一样创建两个数据框,然后使用联接操作将它们组合在一起。这样的事情应该起作用:
joined = scores.withColumnRenamed("student_id", "join_id")
joined = joined.join(lastnames, joined.join_id == lastnames.student_id)\
.drop("join_id")
joined.show()
+---------+-----+----------+---------+
| subject|score|student_id|last_name|
+---------+-----+----------+---------+
| Math| 13| student1| Granger|
| Biology| 85| student1| Granger|
|Chemistry| 77| student1| Granger|
| Physics| 25| student1| Granger|
| Math| 50| student2| Weasley|
| Biology| 45| student2| Weasley|
|Chemistry| 65| student2| Weasley|
| Physics| 79| student2| Weasley|
| Math| 9| student3| Potter|
| Biology| 2| student3| Potter|
|Chemistry| 84| student3| Potter|
| Physics| 43| student3| Potter|
+---------+-----+----------+---------+
Run Code Online (Sandbox Code Playgroud)
还值得注意的是,Spark DataFrames在幕后进行了优化,可以将联接中的DataFrame转换为广播变量,以免其变小(如果足够小)。因此,如果执行上面列出的join方法,则应在不牺牲处理更大数据集的能力的情况下获得最佳性能。
| 归档时间: |
|
| 查看次数: |
3656 次 |
| 最近记录: |