Apache spark 如何为pyspark中列过多的数据计算不同的值

Apache spark 如何为pyspark中列过多的数据计算不同的值,apache-spark,pyspark,Apache Spark,Pyspark,我在spark中处理大量列的数据时遇到问题 我目前正在使用countDistinct函数,如下所示: from pyspark.sql import functions as F distinct_cnts = df.agg(*(F.countDistinct(col).alias(col) for col in df.columns)).toPandas().T[0].to_dict() 对于具有3300列且只有50行的数据(采样数据约1mb) 我使用的是spark cluster环境(驱动

我在spark中处理大量列的数据时遇到问题

我目前正在使用countDistinct函数,如下所示:

from pyspark.sql import functions as F
distinct_cnts = df.agg(*(F.countDistinct(col).alias(col) for col in df.columns)).toPandas().T[0].to_dict()
对于具有3300列且只有50行的数据(采样数据约1mb)

我使用的是spark cluster环境(驱动程序和执行程序为1gb)

当我试图运行上述函数时,我遇到了内存问题和堆栈溢出错误

java.lang.StackOverflowError
我真的不明白1mb左右的数据是如何导致内存问题的。有人能解释一下吗

当我试图为spark驱动程序和执行器分配更多内存(每个3gb,并设置DynamicLocation)时,上面的函数起作用,但每个列的另一个计算作业会再次导致相同的问题。 例如,函数如下所示:

df.select(*(F.sum(F.col(c).isNull().cast('int')).alias(c) for c in f.columns)).toPandas().T[0].to_dict()

除了spark配置之外,还有其他解决此问题的方法吗?(编写代码的更好方法)

这里有两种方法可以尝试/挑战,但都是用Scala编写的。注意,我还没有在宽数据帧上测试它,但是如果可能的话,可以随意共享一些匿名数据:

假设我们有一个宽数据帧(这里只有6列):

方法1:

在这里,我获取所有DF列(3300)并创建100个bucket,每个bucket包含33列。我的存储桶可以并行化以获得更好的可扩展性:

val allColumns = wideDF.columns.grouped(100).toList

val reducedDF = allColumns.par.map{colBucket:Array[String] =>
    //We will process aggregates on narrowed dataframes
    wideDF.select(colBucket.map(c => countDistinct($"$c").as(s"distinct_$c")):_*)
  }.reduce(_ join _)

reducedDF.show(false)

+------------+------------+------------+------------+------------+------------+
|distinctcol1|distinctcol2|distinctcol3|distinctcol4|distinctcol5|distinctcol6|
+------------+------------+------------+------------+------------+------------+
|2           |2           |3           |4           |2           |2           |
+------------+------------+------------+------------+------------+------------+
方法2

创建(或使用Panda的)转置方法,然后在列名上聚合(也可以使用RDDAPI完成)


您可以尝试基于所有列连接(两个内置函数)创建md5。类似md5(concat_ws(“|”,yourColumns))@baitmbarek非常感谢您的帮助。我需要连接所有列以计算每列的唯一值的原因仍然令人困惑。你能详细说明一下吗?如果你知道在只处理1mb数据时消耗如此多内存背后的逻辑,你能解释一下吗?再次阅读你的问题后,我认为我对你的问题理解得不够透彻。目前,您有“巨大”的对象,每个记录都是3300字段结构。它的内存非常大,无法分发(每个属性都与特定记录相关)。您应该考虑将主数据文件拆分为多个小数据框,用“联合”操作符还原它们,然后在每一个列名上计算某种类型的还原ByKEK/COMPEYBYKY。如果你认为有帮助的话,我可以分享一个Spark/Scala示例。因此,你的意思是我需要使用RDD函数,即reduceByKey等来计算每列的唯一值,而不是使用“countDistinct”?如果可能的话,请在这里和我分享spark的例子?没错,这就是我的意思。在接下来的几个小时里,我将尝试提供一些例子作为答案
val allColumns = wideDF.columns.grouped(100).toList

val reducedDF = allColumns.par.map{colBucket:Array[String] =>
    //We will process aggregates on narrowed dataframes
    wideDF.select(colBucket.map(c => countDistinct($"$c").as(s"distinct_$c")):_*)
  }.reduce(_ join _)

reducedDF.show(false)

+------------+------------+------------+------------+------------+------------+
|distinctcol1|distinctcol2|distinctcol3|distinctcol4|distinctcol5|distinctcol6|
+------------+------------+------------+------------+------------+------------+
|2           |2           |3           |4           |2           |2           |
+------------+------------+------------+------------+------------+------------+
def transposeDF(dataframe: DataFrame, transposeBy: Seq[String]): DataFrame = {
    val (cols, types) = dataframe.dtypes.filter{ case (c, _) => !transposeBy.contains(c)}.unzip
    require(types.distinct.size == 1)

    val kvs = explode(array(
      cols.map(c => struct(lit(c).alias("column_name"), col(c).alias("column_value"))): _*
    ))

    val byExprs = transposeBy.map(col)

    dataframe
      .select(byExprs :+ kvs.alias("_kvs"): _*)
      .select(byExprs ++ Seq($"_kvs.column_name", $"_kvs.column_value"): _*)
  }

// We get 1 record per key and column name
transposeDF(wideDF, Seq("key")).groupBy("column_name").agg(countDistinct($"column_value").as("distinct_count")).show(false)

+-----------+--------------+
|column_name|distinct_count|
+-----------+--------------+
|col3       |3             |
|col4       |4             |
|col1       |2             |
|col6       |2             |
|col5       |2             |
|col2       |2             |
+-----------+--------------+