在处理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集群环境(驱动程序和执行程序为1gb)。
当我尝试运行上述功能时,遇到内存问题和stackoverflow错误。
java.lang.StackOverflowError
我不太了解1mb左右的数据如何引起内存问题。有人可以解释一下吗?
当我尝试为spark驱动程序和执行程序分配更多的内存(每个3gb并设置dynamicAllocation)时,上面的函数可以工作,但是每列的另一个计算工作又会导致相同的问题。 例如,功能如下:
df.select(*(F.sum(F.col(c).isNull().cast('int')).alias(c) for c in f.columns)).toPandas().T[0].to_dict()
除了火花配置之外,还有其他解决方法吗? (更好的代码编写方式)
答案 0 :(得分:0)
这里有两种方法可以尝试/挑战,但是都用Scala编写。注意,我尚未在宽数据帧上对其进行测试,但如有可能,可以随时共享一些匿名数据:
让我们说我们有一个宽的DataFrame(这里只有6列):
val wideDF = Seq(
("1","a","b","c","d","e","f"),
("2","a","b","c","d2","e","f"),
("3","a","b","c2","d3","e","f"),
("4","a2","b2","c3","d4","e2","f2")
).toDF("key","col1","col2","col3","col4","col5","col6")
+---+----+----+----+----+----+----+
|key|col1|col2|col3|col4|col5|col6|
+---+----+----+----+----+----+----+
| 1| a| b| c| d| e| f|
| 2| a| b| c| d2| e| f|
| 3| a| b| c2| d3| e| f|
| 4| a2| b2| c3| d4| e2| f2|
+---+----+----+----+----+----+----+
方法1:
在这里,我将所有DF列(3300)并创建100个存储桶,每个存储桶包含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的)Transpose方法,然后按列名称进行汇总(也可以使用RDD API完成)
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 |
+-----------+--------------+