在Spark DataFrame中计算非空值的数量

7
我有一个数据框,其中有一些列,在进行分析之前,我想了解数据框的完整性。因此,我想要过滤数据框,并计算每个列中非空值的数量,可能会返回一个数据框。
基本上,我试图实现与这个问题相同的结果,但是使用Scala而不是Python。
假设你有:
val row = Row("x", "y", "z")
val df = sc.parallelize(Seq(row(0, 4, 3), row(None, 3, 4), row(None, None, 5))).toDF()

如何总结每列的非空值数量并返回一个具有相同列数和仅包含答案的单行数据框?

非常感谢,如果您能分享一些片段以更好地理解您提出的逻辑,我将不胜感激。我可以编写一个UDF来完成这个任务,但我不知道如何编写一个代码来执行每一列的UDF。 - user299791
6个回答

14

一个简单的选择是使用.describe()函数来获得数据框的摘要,其中计数行包括非空值的计数:

一个直截了当的选项是使用.describe()函数来获取您的数据框的摘要信息,其中计数行包括非空值的计数:

df.describe().filter($"summary" === "count").show
+-------+---+---+---+
|summary|  x|  y|  z|
+-------+---+---+---+
|  count|  1|  2|  3|
+-------+---+---+---+

1
这仅适用于数字列,而不适用于字符串...对吗? - user299791
这适用于Spark 2.2.1中的字符串列:使用模式加载,以便我们获得一个仅填充空值的字符串列:val xx = spark.read.schema(StructType(Seq(StructField("a", LongType, true), StructField("b", LongType, true), StructField("c", StringType, true), StructField("d", StringType, true)))).json("/tmp/toy.jline") 然后进行描述。 - Max Murphy

9
尽管我喜欢Psidom的答案,但通常我更关心空值的比例,因为仅有非空值的数量并不能提供足够的信息...你可以这样做:
import org.apache.spark.sql.functions.{sum,when, count}

df.agg(
   (sum(when($"x".isNotNull,0).otherwise(1))/count("*")).as("x : fraction null"),
   (sum(when($"y".isNotNull,0).otherwise(1))/count("*")).as("y : fraction null"),
   (sum(when($"z".isNotNull,0).otherwise(1))/count("*")).as("z : fraction null")
 ).show()

编辑:sum(when($"x".isNotNull,0).otherwise(1))也可以直接替换为count($"x"),它只计算非空值。我认为这并不明显,所以我倾向于使用更清晰的sum符号。


1
这是我在Scala 2.11,Spark 2.3.1中的实现方式:

import org.apache.spark.sql.functions._
import org.apache.spark.sql.types._

df.agg(
    count("x").divide(count(lit(1)))
        .as("x: percent non-null")
    // ...copy paste that for columns y and z
).head()

count(*) 统计非空行,count(1) 在每一行上运行。

如果您想要统计人口中的 百分比空值,可以找到基于计数的等式的补集:

lit(1).minus(
    count("x").divide(count(lit(1)))
    )
    .as("x: percent null")

值得注意的是,您可以将空值转换为整数,然后进行求和。但这可能会降低性能。
// cast null-ness to an integer
sum(col("x").isNull.cast(IntegerType))
    .divide(count(lit(1)))
    .as("x: percent null")

你也可以使用 count("*") 替代 count(lit(1)),我认为这样更易读。 - Raphael Roth

0

Spark 2.3+
(适用于字符串和数值类型的列)

df.summary("count").show()
+-------+---+---+---+
|summary|  x|  y|  z|
+-------+---+---+---+
|  count|  1|  2|  3|
+-------+---+---+---+

0
这是最简单的查询:
d.filter($"x" !== null ).count

0
df.select(df.columns map count: _*)

或者

df.select(df.columns map count: _*).toDF(df.columns: _*)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接