【问题标题】:how to filter out a null value from spark dataframe如何从火花数据框中过滤出空值
【发布时间】:2017-02-05 06:24:52
【问题描述】:

我使用以下架构在 spark 中创建了一个数据框:

root
 |-- user_id: long (nullable = false)
 |-- event_id: long (nullable = false)
 |-- invited: integer (nullable = false)
 |-- day_diff: long (nullable = true)
 |-- interested: integer (nullable = false)
 |-- event_owner: long (nullable = false)
 |-- friend_id: long (nullable = false)

数据如下:

+----------+----------+-------+--------+----------+-----------+---------+
|   user_id|  event_id|invited|day_diff|interested|event_owner|friend_id|
+----------+----------+-------+--------+----------+-----------+---------+
|   4236494| 110357109|      0|      -1|         0|  937597069|     null|
|  78065188| 498404626|      0|       0|         0| 2904922087|     null|
| 282487230|2520855981|      0|      28|         0| 3749735525|     null|
| 335269852|1641491432|      0|       2|         0| 1490350911|     null|
| 437050836|1238456614|      0|       2|         0|  991277599|     null|
| 447244169|2095085551|      0|      -1|         0| 1579858878|     null|
| 516353916|1076364848|      0|       3|         1| 3597645735|     null|
| 528218683|1151525474|      0|       1|         0| 3433080956|     null|
| 531967718|3632072502|      0|       1|         0| 3863085861|     null|
| 627948360|2823119321|      0|       0|         0| 4092665803|     null|
| 811791433|3513954032|      0|       2|         0|  415464198|     null|
| 830686203|  99027353|      0|       0|         0| 3549822604|     null|
|1008893291|1115453150|      0|       2|         0| 2245155244|     null|
|1239364869|2824096896|      0|       2|         1| 2579294650|     null|
|1287950172|1076364848|      0|       0|         0| 3597645735|     null|
|1345896548|2658555390|      0|       1|         0| 2025118823|     null|
|1354205322|2564682277|      0|       3|         0| 2563033185|     null|
|1408344828|1255629030|      0|      -1|         1|  804901063|     null|
|1452633375|1334001859|      0|       4|         0| 1488588320|     null|
|1625052108|3297535757|      0|       3|         0| 1972598895|     null|
+----------+----------+-------+--------+----------+-----------+---------+

我想过滤掉“friend_id”字段中具有空值的行。

scala> val aaa = test.filter("friend_id is null")

scala> aaa.count

我得到 :res52: Long = 0 这显然是不对的。获得它的正确方法是什么?

还有一个问题,我想替换friend_id 字段中的值。对于除 null 之外的任何其他值,我想用 0 和 1 替换 null。我能弄清楚的代码是:

val aaa = train_friend_join.select($"user_id", $"event_id", $"invited", $"day_diff", $"interested", $"event_owner", ($"friend_id" != null)?1:0)

此代码也不起作用。谁能告诉我该如何解决?谢谢

【问题讨论】:

  • friend_id: long (nullable = false) ??你怎么会有空值?它们真的是空值还是文本?
  • 你从哪里读取数据?
  • 嗨,Zahiro Mor,空值来自上一步的左外连接,我在这里没有介绍。对此感到抱歉

标签: scala apache-spark apache-spark-sql spark-dataframe


【解决方案1】:

假设您有这样的数据设置(以便结果可重现):

// declaring data types
case class Company(cName: String, cId: String, details: String)
case class Employee(name: String, id: String, email: String, company: Company)

// setting up example data
val e1 = Employee("n1", null, "n1@c1.com", Company("c1", "1", "d1"))
val e2 = Employee("n2", "2", "n2@c1.com", Company("c1", "1", "d1"))
val e3 = Employee("n3", "3", "n3@c1.com", Company("c1", "1", "d1"))
val e4 = Employee("n4", "4", "n4@c2.com", Company("c2", "2", "d2"))
val e5 = Employee("n5", null, "n5@c2.com", Company("c2", "2", "d2"))
val e6 = Employee("n6", "6", "n6@c2.com", Company("c2", "2", "d2"))
val e7 = Employee("n7", "7", "n7@c3.com", Company("c3", "3", "d3"))
val e8 = Employee("n8", "8", "n8@c3.com", Company("c3", "3", "d3"))
val employees = Seq(e1, e2, e3, e4, e5, e6, e7, e8)
val df = sc.parallelize(employees).toDF

数据是:

+----+----+---------+---------+
|name|  id|    email|  company|
+----+----+---------+---------+
|  n1|null|n1@c1.com|[c1,1,d1]|
|  n2|   2|n2@c1.com|[c1,1,d1]|
|  n3|   3|n3@c1.com|[c1,1,d1]|
|  n4|   4|n4@c2.com|[c2,2,d2]|
|  n5|null|n5@c2.com|[c2,2,d2]|
|  n6|   6|n6@c2.com|[c2,2,d2]|
|  n7|   7|n7@c3.com|[c3,3,d3]|
|  n8|   8|n8@c3.com|[c3,3,d3]|
+----+----+---------+---------+

现在要过滤具有null id 的员工,您将这样做 --

df.filter("id is null").show

这将正确地向您显示以下内容:

+----+----+---------+---------+
|name|  id|    email|  company|
+----+----+---------+---------+
|  n1|null|n1@c1.com|[c1,1,d1]|
|  n5|null|n5@c2.com|[c2,2,d2]|
+----+----+---------+---------+

来到您问题的第二部分,您可以将 null id 替换为 0,并将其他值替换为 1 --

df.withColumn("id", when($"id".isNull, 0).otherwise(1)).show

这会导致:

+----+---+---------+---------+
|name| id|    email|  company|
+----+---+---------+---------+
|  n1|  0|n1@c1.com|[c1,1,d1]|
|  n2|  1|n2@c1.com|[c1,1,d1]|
|  n3|  1|n3@c1.com|[c1,1,d1]|
|  n4|  1|n4@c2.com|[c2,2,d2]|
|  n5|  0|n5@c2.com|[c2,2,d2]|
|  n6|  1|n6@c2.com|[c2,2,d2]|
|  n7|  1|n7@c3.com|[c3,3,d3]|
|  n8|  1|n8@c3.com|[c3,3,d3]|
+----+---+---------+---------+

【讨论】:

  • 能否提供有关spark版本的详细信息。
  • 不确定确切的版本。这是一年前的事了。但我认为 2.0
  • 适用于 Spark 2.4.0
【解决方案2】:

或者喜欢df.filter($"friend_id".isNotNull)

【讨论】:

    【解决方案3】:
    df.where(df.col("friend_id").isNull)
    

    【讨论】:

      【解决方案4】:

      有两种方法:创建过滤条件1)手动2)动态。

      示例数据帧:

      val df = spark.createDataFrame(Seq(
        (0, "a1", "b1", "c1", "d1"),
        (1, "a2", "b2", "c2", "d2"),
        (2, "a3", "b3", null, "d3"),
        (3, "a4", null, "c4", "d4"),
        (4, null, "b5", "c5", "d5")
      )).toDF("id", "col1", "col2", "col3", "col4")
      
      +---+----+----+----+----+
      | id|col1|col2|col3|col4|
      +---+----+----+----+----+
      |  0|  a1|  b1|  c1|  d1|
      |  1|  a2|  b2|  c2|  d2|
      |  2|  a3|  b3|null|  d3|
      |  3|  a4|null|  c4|  d4|
      |  4|null|  b5|  c5|  d5|
      +---+----+----+----+----+
      

      1) 手动创建过滤条件 即使用DataFrame wherefilter 函数

      df.filter(col("col1").isNotNull && col("col2").isNotNull).show
      

      df.where("col1 is not null and col2 is not null").show
      

      结果:

      +---+----+----+----+----+
      | id|col1|col2|col3|col4|
      +---+----+----+----+----+
      |  0|  a1|  b1|  c1|  d1|
      |  1|  a2|  b2|  c2|  d2|
      |  2|  a3|  b3|null|  d3|
      +---+----+----+----+----+
      

      2)动态创建过滤条件:当我们不希望任何列具有空值并且有大量列时,这很有用,大多数情况下都是这种情况。

      在这些情况下手动创建过滤条件会浪费很多时间。在下面的代码中,我们在 DataFrame 列上使用 mapreduce 函数动态包含所有列:

      val filterCond = df.columns.map(x=>col(x).isNotNull).reduce(_ && _)
      

      filterCond 的外观:

      filterCond: org.apache.spark.sql.Column = (((((id IS NOT NULL) AND (col1 IS NOT NULL)) AND (col2 IS NOT NULL)) AND (col3 IS NOT NULL)) AND (col4 IS NOT NULL))
      

      过滤:

      val filteredDf = df.filter(filterCond)
      

      结果:

      +---+----+----+----+----+
      | id|col1|col2|col3|col4|
      +---+----+----+----+----+
      |  0|  a1|  b1|  c1|  d1|
      |  1|  a2|  b2|  c2|  d2|
      +---+----+----+----+----+
      

      【讨论】:

        【解决方案5】:

        对我来说一个好的解决方案是删除具有任何空值的行:

        Dataset<Row> filtered = df.filter(row => !row.anyNull);

        如果有人对另一种情况感兴趣,请致电row.anyNull。 (使用 Java API 的 Spark 2.1.0)

        【讨论】:

          【解决方案6】:

          以下几行运行良好:

          test.filter("friend_id is not null")
          

          【讨论】:

            【解决方案7】:

            根据 Michael Kopaniov 的提示,以下作品

            df.where(df("id").isNotNull).show
            

            【讨论】:

              【解决方案8】:

              这是 Java 中 spark 的解决方案。选择包含空值的数据行。当您拥有数据集数据时,您可以:

              Dataset<Row> containingNulls =  data.where(data.col("COLUMN_NAME").isNull())
              

              要过滤掉没有空值的数据:

              Dataset<Row> withoutNulls = data.where(data.col("COLUMN_NAME").isNotNull())
              

              通常数据帧包含字符串类型的列,其中我们有空字符串,例如“”,而不是空字符串。为了过滤掉这些数据,我们也会这样做:

              Dataset<Row> withoutNullsAndEmpty = data.where(data.col("COLUMN_NAME").isNotNull().and(data.col("COLUMN_NAME").notEqual("")))
              

              【讨论】:

                【解决方案9】:

                对于第一个问题,您过滤掉空值是正确的,因此计数为零。

                第二次替换:使用如下:

                val options = Map("path" -> "...\\ex.csv", "header" -> "true")
                val dfNull = spark.sqlContext.load("com.databricks.spark.csv", options)
                
                scala> dfNull.show
                
                +----------+----------+-------+--------+----------+-----------+---------+
                |   user_id|  event_id|invited|day_diff|interested|event_owner|friend_id|
                +----------+----------+-------+--------+----------+-----------+---------+
                |   4236494| 110357109|      0|      -1|         0|  937597069|     null|
                |  78065188| 498404626|      0|       0|         0| 2904922087|     null|
                | 282487230|2520855981|      0|      28|         0| 3749735525|     null|
                | 335269852|1641491432|      0|       2|         0| 1490350911|     null|
                | 437050836|1238456614|      0|       2|         0|  991277599|     null|
                | 447244169|2095085551|      0|      -1|         0| 1579858878|        a|
                | 516353916|1076364848|      0|       3|         1| 3597645735|        b|
                | 528218683|1151525474|      0|       1|         0| 3433080956|        c|
                | 531967718|3632072502|      0|       1|         0| 3863085861|     null|
                | 627948360|2823119321|      0|       0|         0| 4092665803|     null|
                | 811791433|3513954032|      0|       2|         0|  415464198|     null|
                | 830686203|  99027353|      0|       0|         0| 3549822604|     null|
                |1008893291|1115453150|      0|       2|         0| 2245155244|     null|
                |1239364869|2824096896|      0|       2|         1| 2579294650|        d|
                |1287950172|1076364848|      0|       0|         0| 3597645735|     null|
                |1345896548|2658555390|      0|       1|         0| 2025118823|     null|
                |1354205322|2564682277|      0|       3|         0| 2563033185|     null|
                |1408344828|1255629030|      0|      -1|         1|  804901063|     null|
                |1452633375|1334001859|      0|       4|         0| 1488588320|     null|
                |1625052108|3297535757|      0|       3|         0| 1972598895|     null|
                +----------+----------+-------+--------+----------+-----------+---------+
                
                dfNull.withColumn("friend_idTmp", when($"friend_id".isNull, "1").otherwise("0")).drop($"friend_id").withColumnRenamed("friend_idTmp", "friend_id").show
                
                +----------+----------+-------+--------+----------+-----------+---------+
                |   user_id|  event_id|invited|day_diff|interested|event_owner|friend_id|
                +----------+----------+-------+--------+----------+-----------+---------+
                |   4236494| 110357109|      0|      -1|         0|  937597069|        1|
                |  78065188| 498404626|      0|       0|         0| 2904922087|        1|
                | 282487230|2520855981|      0|      28|         0| 3749735525|        1|
                | 335269852|1641491432|      0|       2|         0| 1490350911|        1|
                | 437050836|1238456614|      0|       2|         0|  991277599|        1|
                | 447244169|2095085551|      0|      -1|         0| 1579858878|        0|
                | 516353916|1076364848|      0|       3|         1| 3597645735|        0|
                | 528218683|1151525474|      0|       1|         0| 3433080956|        0|
                | 531967718|3632072502|      0|       1|         0| 3863085861|        1|
                | 627948360|2823119321|      0|       0|         0| 4092665803|        1|
                | 811791433|3513954032|      0|       2|         0|  415464198|        1|
                | 830686203|  99027353|      0|       0|         0| 3549822604|        1|
                |1008893291|1115453150|      0|       2|         0| 2245155244|        1|
                |1239364869|2824096896|      0|       2|         1| 2579294650|        0|
                |1287950172|1076364848|      0|       0|         0| 3597645735|        1|
                |1345896548|2658555390|      0|       1|         0| 2025118823|        1|
                |1354205322|2564682277|      0|       3|         0| 2563033185|        1|
                |1408344828|1255629030|      0|      -1|         1|  804901063|        1|
                |1452633375|1334001859|      0|       4|         0| 1488588320|        1|
                |1625052108|3297535757|      0|       3|         0| 1972598895|        1|
                +----------+----------+-------+--------+----------+-----------+---------+
                

                【讨论】:

                  【解决方案10】:

                  另一种从 spark 数据框中的多列中过滤掉空值的简单方法。请注意列之间有AND

                  df.filter(" COALESCE(col1, col2, col3, col4, col5, col6) IS NOT NULL")
                  

                  如果您需要过滤掉包含任何 null(OR 连接)的行,请使用

                  df.na.drop()
                  

                  【讨论】:

                    【解决方案11】:
                    val df = Seq(
                      ("1001", "1007"),
                      ("1002", null),
                      ("1003", "1005"),
                      (null, "1006")
                    ).toDF("user_id", "friend_id")
                    

                    数据是:

                    +-------+---------+
                    |user_id|friend_id|
                    +-------+---------+
                    |   1001|     1007|
                    |   1002|     null|
                    |   1003|     1005|
                    |   null|     1006|
                    +-------+---------+
                    

                    在 Seq 的指定列中删除包含任何 null 或 NaN 值的行:

                    df.na.drop(Seq("friend_id"))
                      .show()
                    

                    输出:

                    +-------+---------+
                    |user_id|friend_id|
                    +-------+---------+
                    |   1001|     1007|
                    |   1003|     1005|
                    |   null|     1006|
                    +-------+---------+
                    

                    如果不指定列,只要行的任何列包含 null 或 NaN 值,就删除行:

                    df.na.drop()
                      .show()
                    

                    输出:

                    +-------+---------+
                    |user_id|friend_id|
                    +-------+---------+
                    |   1001|     1007|
                    |   1003|     1005|
                    +-------+---------+
                    

                    【讨论】:

                      【解决方案12】:

                      我使用以下代码来解决我的问题。有用。但众所周知,我在一个国家的英里范围内工作以解决它。那么,有没有捷径呢?谢谢

                      def filter_null(field : Any) : Int = field match {
                          case null => 0
                          case _    => 1
                      }
                      
                      val test = train_event_join.join(
                          user_friends_pair,
                          train_event_join("user_id") === user_friends_pair("user_id") &&
                          train_event_join("event_owner") === user_friends_pair("friend_id"),
                          "left"
                      ).select(
                          train_event_join("user_id"),
                          train_event_join("event_id"),
                          train_event_join("invited"),
                          train_event_join("day_diff"),
                          train_event_join("interested"),
                          train_event_join("event_owner"),
                          user_friends_pair("friend_id")
                      ).rdd.map{
                          line => (
                              line(0).toString.toLong,
                              line(1).toString.toLong,
                              line(2).toString.toLong,
                              line(3).toString.toLong,
                              line(4).toString.toLong,
                              line(5).toString.toLong,
                              filter_null(line(6))
                              )
                          }.toDF("user_id", "event_id", "invited", "day_diff", "interested", "event_owner", "creator_is_friend")
                      

                      【讨论】:

                      • 您不需要转换为中间 rdd 然后再转换回数据框只是为了替换空值。请参阅我的答案以获取类似示例。
                      • 谢谢你,Sachin Tyagi。我使用以下代码:val aaa = test.filter("friend_id is null")。但我无法过滤掉friend_id 字段中具有空值的任何行。我比较了你的代码和我的代码。 friend_id 类型很长。这可能是我们得到不同结果的原因吗?
                      • 嗨,我也检查了 long 并且它的工作原理相同(如预期的那样)。请您仔细检查您的代码。或者粘贴一个也可以在这里测试的端到端可重现的 sn-p?
                      猜你喜欢
                      • 2015-07-10
                      • 1970-01-01
                      • 2019-08-23
                      • 2020-03-11
                      • 1970-01-01
                      • 2015-11-06
                      • 1970-01-01
                      • 1970-01-01
                      • 2021-04-04
                      相关资源
                      最近更新 更多