Pyspark对行进行迭代,并使用结果列上的逻辑计算计数器

Pyspark对行进行迭代,并使用结果列上的逻辑计算计数器,pyspark,Pyspark,我有pyspark dataframe中的数据(这是一个有900M行的非常大的表) 这是我掌握的数据 +-------+---------+----------+ | key| time| cond| +-------+---------+----------+ | 6| 3704| null| | 6| 74967| 1062| | 6|151565068| null| | 6|1549

我有pyspark dataframe中的数据(这是一个有900M行的非常大的表)

这是我掌握的数据

+-------+---------+----------+
|    key|     time|      cond|
+-------+---------+----------+
|      6|     3704|      null|
|      6|    74967|      1062|
|      6|151565068|      null|
|      6|154999554|      null|
|      6|160595800|      null|
|      6|166192324|      null|
|      6|166549533|      null|
|      6|171318946|      null|
|      6|754759092|      null|
|      6|754999359|  18882624|
|      6|755171746|  11381128|
|      6|761097038|      null|
|      6|774496554|      null|
|      6|930609982|      null|
|      6|930809622|      null|
|      1|   192427|      null|
|      1|   192427|      2779|
|      1|   717931|      null|
|      1|  1110573|      null|
|      1|  1155854|      null|
|      1| 70049289|      null|
|      1| 70687548|      null|
|      1| 71222733|      null|
|      1| 85006084|      null|
|      1| 85029676|      null|
|      1| 85032605|   1424537|
|      1| 85240114|      null|
|      1| 85573757|      null|
|      1| 85710915|      null|
|      1| 85870370|      null|
+-------+---------+----------+
这就是我需要对数据帧执行的操作(中间步骤):

“result”列的逻辑如下:每个键都有一个运行计数器,如果“cond”列不为null,则将计数器归零

我们可以假设表是orderBy(“key”,asc(“time”))

我的最终结果实际上是平均值。行上的结果(每个键)条件不为null。 上述数据(最终结果)应如下所示:

我打算这样做:

df_results = df3[df3.cond.isNotNull()].groupby(['key']).agg(
    F.expr("avg(result)").alias("avg_per_key")
)
我认为它应该工作,但也许没有中间步骤的话,有更好的方法。< /P>
如何在pyspark中高效地实现这一点?(记住数据集是巨大的)

试试这个。计算结果的方法是在
条件下使用
增量和
,然后在另一个窗口中使用这些
分组作为
行数()-1的分区,以获得所需的
结果
过滤
之前的
分组依据
应通过
减少混洗
执行

from pyspark.sql import functions as F
from pyspark.sql.window import Window


w=Window().partitionBy("key").orderBy("time")
w1=Window().partitionBy("key","result").orderBy("time")

conditions=F.when((F.col("cond").isNotNull())&(F.col("lag").isNotNull()&\
                                                           F.col("lead").isNull()),F.lit(1))\
                        .when((F.col("cond").isNull())&(F.col("lag").isNotNull()),F.lit(1))\
                         .otherwise(F.lit(0))


df.withColumn("lag", F.lag("cond").over(w))\
  .withColumn("lead", F.lead("cond").over(w))\
  .withColumn("result",F.sum(conditions).over(w))\
  .withColumn("result", F.row_number().over(w1)-1).filter("cond is not null")\
  .groupBy("key").agg(F.mean(F.col("result")).alias("avg_per_key")).show()

#+---+------------------+
#|key|       avg_per_key|
#+---+------------------+
#|  6|2.6666666666666665|
#|  1|               4.5|
#+---+------------------+

试试这个。计算结果的方法是在
条件下使用
增量和
,然后在另一个窗口中使用这些
分组作为
行数()-1的分区,以获得所需的
结果
过滤
之前的
分组依据
应通过
减少混洗
执行

from pyspark.sql import functions as F
from pyspark.sql.window import Window


w=Window().partitionBy("key").orderBy("time")
w1=Window().partitionBy("key","result").orderBy("time")

conditions=F.when((F.col("cond").isNotNull())&(F.col("lag").isNotNull()&\
                                                           F.col("lead").isNull()),F.lit(1))\
                        .when((F.col("cond").isNull())&(F.col("lag").isNotNull()),F.lit(1))\
                         .otherwise(F.lit(0))


df.withColumn("lag", F.lag("cond").over(w))\
  .withColumn("lead", F.lead("cond").over(w))\
  .withColumn("result",F.sum(conditions).over(w))\
  .withColumn("result", F.row_number().over(w1)-1).filter("cond is not null")\
  .groupBy("key").agg(F.mean(F.col("result")).alias("avg_per_key")).show()

#+---+------------------+
#|key|       avg_per_key|
#+---+------------------+
#|  6|2.6666666666666665|
#|  1|               4.5|
#+---+------------------+

这是我的解决方案,我不是说它是最优的,而是在我的情况下,当其他尝试使集群崩溃时,它起了作用。 我是spark的初学者,所以我知道这种方法可能会导致问题,因为它将数据集放入内存。 如果我有更多的时间玩它,我会尝试使用sortWithinPartitions

def handleRow(row):
    temp = list(row[1])
    temp = np.array([temp[x:x+2] for x in range(0, len(temp),2)])
    temp[:,0] = temp[:,0].astype(float)
    temp = temp[temp[:,0].argsort()]
    avg_per_key= []
    counter=0
    for time,cond in temp:
        if cond!=None:
            avg_per_key.append(counter) 
            counter=0
        else:
            counter=counter+1

    return [(row[0],-1 if len(avg_per_key)==0 else np.mean(avg_per_key))]


count = df3.rdd.map(lambda x: (x.key, (x.time, x.cond)))\
    .reduceByKey(lambda a, b: a + b)\
    .flatMap(handleRow)\
    .collect()

这是我的解决方案,我不是说它是最优的,而是在我的情况下,当其他尝试使集群崩溃时,它起了作用。 我是spark的初学者,所以我知道这种方法可能会导致问题,因为它将数据集放入内存。 如果我有更多的时间玩它,我会尝试使用sortWithinPartitions

def handleRow(row):
    temp = list(row[1])
    temp = np.array([temp[x:x+2] for x in range(0, len(temp),2)])
    temp[:,0] = temp[:,0].astype(float)
    temp = temp[temp[:,0].argsort()]
    avg_per_key= []
    counter=0
    for time,cond in temp:
        if cond!=None:
            avg_per_key.append(counter) 
            counter=0
        else:
            counter=counter+1

    return [(row[0],-1 if len(avg_per_key)==0 else np.mean(avg_per_key))]


count = df3.rdd.map(lambda x: (x.key, (x.time, x.cond)))\
    .reduceByKey(lambda a, b: a + b)\
    .flatMap(handleRow)\
    .collect()

我用windows lag尝试了一个类似的解决方案,但它对我不起作用,在运行2.5小时后崩溃了。我找到了另一个可行的解决方案,谢谢@justadev我很想看看这个解决方案是什么样子,以及它如何能够超越我的解决方案。为了社区的利益,请将其张贴在答案中,感谢教授的迟来回复。刚刚添加。@justadev感谢您的发布。你的解决方案会起作用,但它不是一种令人振奋的方式。正确使用的窗口函数总是优于任何rdd UDF,如urs。请记住,为了未来,我尝试了一个类似的解决方案,使用windows lag,但它对我不起作用,在运行2.5小时后崩溃了。我找到了另一个可行的解决方案,谢谢@justadev我很想看看这个解决方案是什么样子,以及它如何能够超越我的解决方案。为了社区的利益,请将其张贴在答案中,感谢教授的迟来回复。刚刚添加。@justadev感谢您的发布。你的解决方案会起作用,但它不是一种令人振奋的方式。正确使用的窗口函数总是优于任何rdd UDF,如urs。牢记未来