如何删除具有太多 NULL 值的行?

Posted

技术标签:

【中文标题】如何删除具有太多 NULL 值的行?【英文标题】:How to drop rows with too many NULL values? 【发布时间】:2016-03-17 14:05:10 【问题描述】:

我想对我的数据进行一些预处理,并且我想删除稀疏的行(对于某些阈值)。

例如,我有一个包含 10 个特征的数据框表,并且我有一行包含 8 个空值,那么我想删除它。

我找到了一些相关主题,但找不到任何有用的信息。

***.com/questions/3473778/count-number-of-nulls-in-a-row

上面链接中的示例对我不起作用,因为我想自动执行此预处理。我不能写列名并做相应的事情。

那么有没有办法在不使用 Apache Spark 和 scala 中的列名的情况下执行此删除操作?

【问题讨论】:

【参考方案1】:

我很惊讶没有答案指出 Spark SQL 带有几个满足要求的标准函数:

例如,我有一个包含 10 个特征的数据框表,并且我有一行包含 8 个空值,那么我想删除它。

您可以使用DataFrameNaFunctions.drop 方法的变体之一,并适当设置minNonNulls,例如2。

drop(minNonNulls: Int, cols: Seq[String]): DataFrame 返回一个新的 DataFrame,它会删除指定列中包含小于 minNonNulls 非 null 和非 NaN 值的行。

为了满足列名的可变性要求:

我不能写列名并做相应的事情。

你可以简单地使用Dataset.columns:

columns: Array[String] 将所有列名作为数组返回。


假设您有以下数据集,其中包含 5 个特征(列)和几行几乎所有 nulls。

val ns: String = null
val features = Seq(("0","1","2",ns,ns), (ns, ns, ns, ns, ns), (ns, "1", ns, "2", ns)).toDF
scala> features.show
+----+----+----+----+----+
|  _1|  _2|  _3|  _4|  _5|
+----+----+----+----+----+
|   0|   1|   2|null|null|
|null|null|null|null|null|
|null|   1|null|   2|null|
+----+----+----+----+----+

// drop rows with more than (5 columns - 2) = 3 nulls
scala> features.na.drop(2, features.columns).show
+----+---+----+----+----+
|  _1| _2|  _3|  _4|  _5|
+----+---+----+----+----+
|   0|  1|   2|null|null|
|null|  1|null|   2|null|
+----+---+----+----+----+

【讨论】:

【参考方案2】:

考试日期:

case class Document( a: String, b: String, c: String)
val df = sc.parallelize(Seq(new Document(null, null, null), new Document("a", null, null), new Document("a", "b", null), new Document("a", "b", "c"), new Document(null, null, "c"))).df

使用 UDF

通过David 和我的 RDD 版本重新混合答案,您可以使用占用一行的 UDF:

def nullFilter = udf((x:Row) => Range(0, x.length).count(x.isNullAt(_)) < 2)
df.filter(nullFilter(struct(df.columns.map(df(_)) : _*))).show

使用 RDD

你可以把它变成一个 rdd,Row 中的列的循环并计算有多少是空的。

sqlContext.createDataFrame(df.rdd.filter( x=> Range(0, x.length).count(x.isNullAt(_)) < 2 ), df.schema).show

【讨论】:

我可以在不转换为 RDD 的情况下做到这一点。等等。【参考方案3】:

使用 UDF 更简洁:

import org.apache.spark.sql.functions.udf
def countNulls = udf((v: Any) => if (v == null) 1; else 0;))
df.registerTempTable("foo")

sqlContext.sql(
  "select " + df.columns.mkString(", ") + ", " + df.columns.map(c => 
    "countNulls(" + c + ")"
  ).mkString(" + ") + "as nullCount from foo"
).filter($"nullCount" > 8).show

如果制作查询字符串让你感到紧张,那么你可以试试这个:

var countCol: org.apache.spark.sql.Column = null
df.columns.foreach(c => 
  if (countCol == null) countCol = countNulls(col(c))
  else countCol = countCol + countNulls(col(c)) 
);

df.select(Seq(countCol as "nullCount") ++ df.columns.map(c => col(c)):_*)
  .filter($"nullCount" > 8)

【讨论】:

我完全支持不涉及切换到 RDD 的解决方案,我并不太热衷于基于字符串的查询构建。没有那个能做到吗? 完全同意你的看法。现在正在尝试。最坏的情况,我想我可以制作一个UDF,它需要一组列并一口气吐出计数。给我一点时间。 我想我可能有一个混合两个答案的解决方案。等等。【参考方案4】:

这是 Spark 2.0 中的替代方案:

val df = Seq((null,"A"),(null,"B"),("1","C"))
         .toDF("foo","bar")
         .withColumn("foo", 'foo.cast("Int"))

df.show()

+----+---+
| foo|bar|
+----+---+
|null|  A|
|null|  B|
|   1|  C|
+----+---+

df.where('foo.isNull).groupBy('foo).count().show()

+----+-----+
| foo|count|
+----+-----+
|null|    2|
+----+-----+

【讨论】:

也许我的问题错了,但这似乎不能回答问题,因为它:1)只看一个字段。 2) 需要指定字段。

以上是关于如何删除具有太多 NULL 值的行?的主要内容,如果未能解决你的问题,请参考以下文章

在为选定列分配变量后,如何删除一些具有 NA 值的行?

如何更改我的 sql 查询,以便删除列中具有某些值的行 [重复]

VBA比较两个列表并删除不同列中具有重复值的行

Pyspark:如何仅在具有 NotNull 值的行上应用 UDF

删除具有特定值的行

SQL删除存在值的行和具有该值的后续列