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
,因为我的输入数据太大,这会导致驱动程序过载。无论如何,这绝对是一个明智的开始。谢谢。我很高兴能帮上忙:)