Pyspark 使用RDD list作为数据帧筛选器操作的参数
我有以下代码片段Pyspark 使用RDD list作为数据帧筛选器操作的参数,pyspark,spark-dataframe,rdd,pyspark-sql,apache-spark-2.0,Pyspark,Spark Dataframe,Rdd,Pyspark Sql,Apache Spark 2.0,我有以下代码片段 from pyspark import SparkContext from pyspark.sql import SparkSession from pyspark.sql.types import * sc = SparkContext() spark = SparkSession.builder.appName("test").getOrCreate() schema = StructType([
from pyspark import SparkContext
from pyspark.sql import SparkSession
from pyspark.sql.types import *
sc = SparkContext()
spark = SparkSession.builder.appName("test").getOrCreate()
schema = StructType([
StructField("name", StringType(), True),
StructField("a", StringType(), True),
StructField("b", StringType(), True),
StructField("c", StringType(), True),
StructField("d", StringType(), True),
StructField("e", StringType(), True),
StructField("f", StringType(), True)])
arr = [("Alice", "1", "2", None, "red", None, None), \
("Bob", "1", None, None, None, None, "apple"), \
("Charlie", "2", "3", None, None, None, "orange")]
df = spark.createDataFrame(arr, schema)
df.show()
#+-------+---+----+----+----+----+------+
#| name| a| b| c| d| e| f|
#+-------+---+----+----+----+----+------+
#| Alice| 1| 2|null| red|null| null|
#| Bob| 1|null|null|null|null| apple|
#|Charlie| 2| 3|null|null|null|orange|
#+-------+---+----+----+----+----+------+
现在,我有一个RDD,它类似于:
lrdd = sc.parallelize([['a', 'b'], ['c', 'd', 'e'], ['f']])
我的目标是找到属性子集为空的名称,即在上面的示例中:
{'c,d,e': ['Bob', 'Charlie'], 'f': ['Alice']}
现在,我找到了一个相当简单的解决方案,即收集列表,然后循环查询数据帧的子集
def build_filter_condition(l):
return ' AND '.join(["({} is NULL)".format(x) for x in l])
res = {}
for alist in lrdd.collect():
cond = build_filter_condition(alist)
p = df.select("name").where(cond)
if p and p.count() > 0:
res[','.join(alist)] = p.rdd.map(lambda x: x[0]).collect()
print(res)
这很有效,但效率很低。
还可以考虑,目标属性模式类似于10000个属性,导致LRDD中超过600个不相交的列表。
所以,我的问题是:
如何有效地使用分布式集合的内容作为查询sql数据帧的参数?
任何暗示都将不胜感激
非常感谢。您可以尝试这种方法 首先交叉连接两个数据帧
from pyspark.sql.types import *
lrdd = sc.parallelize([['a', 'b'], ['c', 'd', 'e'], ['f']]).
map(lambda x: ("key", x))
schema = StructType([StructField("K", StringType()),
StructField("X", ArrayType(StringType()))])
df2 = spark.createDataFrame(lrdd, schema).select("X")
df3 = df.crossJoin(df2)
交叉连接结果
+-------+---+----+----+----+----+------+---------+
| name| a| b| c| d| e| f| X|
+-------+---+----+----+----+----+------+---------+
| Alice| 1| 2|null| red|null| null| [a, b]|
| Alice| 1| 2|null| red|null| null|[c, d, e]|
| Alice| 1| 2|null| red|null| null| [f]|
| Bob| 1|null|null|null|null| apple| [a, b]|
|Charlie| 2| 3|null|null|null|orange| [a, b]|
| Bob| 1|null|null|null|null| apple|[c, d, e]|
| Bob| 1|null|null|null|null| apple| [f]|
|Charlie| 2| 3|null|null|null|orange|[c, d, e]|
|Charlie| 2| 3|null|null|null|orange| [f]|
+-------+---+----+----+----+----+------+---------+
现在使用udf过滤掉这些行
from pyspark.sql.functions import udf, struct, collect_list
def foo(data):
d = list(filter(lambda x: data[x], data['X']))
print(d)
if len(d)>0:
return(False)
else:
return(True)
udf_foo = udf(foo, BooleanType())
df4 = df3.filter(udf_foo(struct([df3[x] for x in df3.columns]))).select("name", 'X')
df4.show()
+-------+---------+
| name| X|
+-------+---------+
| Alice| [f]|
| Bob|[c, d, e]|
|Charlie|[c, d, e]|
+-------+---------+
然后使用groupby和collect_list获得所需的输出
df4.groupby("X").agg(collect_list("name").alias("name")).show()
+--------------+---------+
| name | X|
+--------------+---------+
| [ Alice] | [f]|
|[Bob, Charlie]|[c, d, e]|
+--------------+---------+
您应该重新考虑数据的格式。不要有那么多的列,你应该
分解它以获得更多的行以允许分布式计算:
import pyspark.sql.函数作为psf
df=df.select(
“姓名”,
爆炸(
psf阵列(
*[psf.struct(
psf.lit(c)别名(“特征名称”),
df[c]。别名(“特征值”)
)如果c!=“name”]
)
).别名(“特征”)
).选择(“名称”、“特征。*”)
+-------+------------+-------------+
|名称|特征|名称|特征|值|
+-------+------------+-------------+
|爱丽丝| a | 1|
|爱丽丝| b | 2|
|爱丽丝| c |空|
|爱丽斯·d·瑞德|
|爱丽丝| e |空|
|爱丽丝| f |空|
|鲍勃| a | 1|
|鲍勃| b |零|
|鲍勃| c |空|
|鲍勃| d |空|
|鲍勃| e |空|
|苹果公司|
|查理| a | 2|
|查理| b | 3|
|查理| c |空|
|查理| d |空|
|查理| e |空|
|查理| f |橙|
+-------+------------+-------------+
我们将对lrdd
执行相同的操作,但我们将首先对其进行一些更改:
subset=spark\
.createDataFrame(lrdd.map(lambda l:[l]),[“特征集”])\
.withColumn(“特征名称”,psf.explode(“特征集”))
+-----------+------------+
|特征|集合|特征|名称|
+-----------+------------+
|[a,b]| a|
|[a,b]| b|
|[c,d,e]| c|
|[c,d,e]| d|
|[c,d,e]| e|
|[f]| f|
+-----------+------------+
现在,我们可以在feature\u name
上加入这些元素,并在feature\u集合
和name
上进行过滤,其feature\u值
仅为空。如果lrdd表不是太大,您应该广播它
df_join=df.join(psf.broadcast(子集),“功能名称”)
res=df_join.groupBy(“功能集”、“名称”).agg(
psf.count(“*”).alias(“count”),
psf.sum(psf.isnull(“特征值”).cast(“int”)。别名(“nb\u null”)
).filter(“nb_null=count”)
+-----------+-------+-----+-------+
|特征集|名称|计数| nb|null|
+-----------+-------+-----+-------+
|[c,d,e]|查理| 3 | 3|
|[f]|爱丽丝| 1 | 1|
|[c,d,e]|鲍勃| 3 | 3|
+-----------+-------+-----+-------+
您可以始终groupBy
feature\u-set
然后Wow。这看起来太棒了。我没有想到交叉连接。谢谢不幸的是,在最大的场景中,它无法org.apache.spark.sql.catalyst.expressions.GeneratedClass$SpecificUnsafeRowJoiner增长超过64 KB
。我会进一步调查你的方法。再次感谢你,你让我高兴极了!我将此标记为“接受答案”。当然,我应该避免使用groupBy
,因为我的输入数据太大,这会导致驱动程序过载。无论如何,这绝对是一个明智的开始。谢谢。我很高兴能帮上忙:)