【问题标题】:Spark DataFrame handing empty String in OneHotEncoderSpark DataFrame 在 OneHotEncoder 中处理空字符串
【发布时间】:2016-01-10 10:38:11
【问题描述】:

我正在将一个 CSV 文件(使用 spark-csv)导入一个 DataFrame,它的 String 值为空。应用OneHotEncoder 时,应用程序崩溃并出现错误requirement failed: Cannot have an empty string for name.。有没有办法解决这个问题?

我可以在example provided on Spark ml 页面中重现错误:

val df = sqlContext.createDataFrame(Seq(
  (0, "a"),
  (1, "b"),
  (2, "c"),
  (3, ""),         //<- original example has "a" here
  (4, "a"),
  (5, "c")
)).toDF("id", "category")

val indexer = new StringIndexer()
  .setInputCol("category")
  .setOutputCol("categoryIndex")
  .fit(df)
val indexed = indexer.transform(df)

val encoder = new OneHotEncoder()
  .setInputCol("categoryIndex")
  .setOutputCol("categoryVec")
val encoded = encoder.transform(indexed)

encoded.show()

这很烦人,因为缺失/空值是一种高度通用的情况。

提前致谢, 尼基尔

【问题讨论】:

    标签: scala apache-spark apache-spark-mllib apache-spark-ml spark-csv


    【解决方案1】:

    由于OneHotEncoder/OneHotEncoderEstimator 不接受空字符串作为名称,否则您将收到以下错误:

    java.lang.IllegalArgumentException:要求失败:名称不能有空字符串。 在 scala.Predef$.require(Predef.scala:233) 在 org.apache.spark.ml.attribute.Attribute$$anonfun$5.apply(attributes.scala:33) 在 org.apache.spark.ml.attribute.Attribute$$anonfun$5.apply(attributes.scala:32) [...]

    这就是我的做法:(还有其他方法可以做到,rf。@Anthony 的回答)

    我将创建一个UDF 来处理空类别:

    import org.apache.spark.sql.functions._
    
    def processMissingCategory = udf[String, String] { s => if (s == "") "NA"  else s }
    

    然后,我将在列上应用 UDF:

    val df = sqlContext.createDataFrame(Seq(
       (0, "a"),
       (1, "b"),
       (2, "c"),
       (3, ""),         //<- original example has "a" here
       (4, "a"),
       (5, "c")
    )).toDF("id", "category")
      .withColumn("category",processMissingCategory('category))
    
    df.show
    // +---+--------+
    // | id|category|
    // +---+--------+
    // |  0|       a|
    // |  1|       b|
    // |  2|       c|
    // |  3|      NA|
    // |  4|       a|
    // |  5|       c|
    // +---+--------+
    

    现在,您可以返回到您的转换中

    val indexer = new StringIndexer().setInputCol("category").setOutputCol("categoryIndex").fit(df)
    val indexed = indexer.transform(df)
    indexed.show
    // +---+--------+-------------+
    // | id|category|categoryIndex|
    // +---+--------+-------------+
    // |  0|       a|          0.0|
    // |  1|       b|          2.0|
    // |  2|       c|          1.0|
    // |  3|      NA|          3.0|
    // |  4|       a|          0.0|
    // |  5|       c|          1.0|
    // +---+--------+-------------+
    
    // Spark <2.3
    // val encoder = new OneHotEncoder().setInputCol("categoryIndex").setOutputCol("categoryVec")
    // Spark +2.3
    val encoder = new OneHotEncoderEstimator().setInputCols(Array("categoryIndex")).setOutputCols(Array("category2Vec"))
    val encoded = encoder.transform(indexed)
    
    encoded.show
    // +---+--------+-------------+-------------+
    // | id|category|categoryIndex|  categoryVec|
    // +---+--------+-------------+-------------+
    // |  0|       a|          0.0|(3,[0],[1.0])|
    // |  1|       b|          2.0|(3,[2],[1.0])|
    // |  2|       c|          1.0|(3,[1],[1.0])|
    // |  3|      NA|          3.0|    (3,[],[])|
    // |  4|       a|          0.0|(3,[0],[1.0])|
    // |  5|       c|          1.0|(3,[1],[1.0])|
    // +---+--------+-------------+-------------+
    

    编辑:

    @Anthony 在 Scala 中的解决方案:

    df.na.replace("category", Map( "" -> "NA")).show
    // +---+--------+
    // | id|category|
    // +---+--------+
    // |  0|       a|
    // |  1|       b|
    // |  2|       c|
    // |  3|      NA|
    // |  4|       a|
    // |  5|       c|
    // +---+--------+
    

    我希望这会有所帮助!

    【讨论】:

      【解决方案2】:

      是的,这有点棘手,但也许您可以将空字符串替换为肯定与其他值不同的值。请注意,我使用的是 pyspark DataFrameNaFunctions API,但 Scala's 应该类似。

      df = sqlContext.createDataFrame([(0,"a"), (1,'b'), (2, 'c'), (3,''), (4,'a'), (5, 'c')], ['id', 'category'])
      df = df.na.replace('', 'EMPTY', 'category')
      df.show()
      
      +---+--------+
      | id|category|
      +---+--------+
      |  0|       a|
      |  1|       b|
      |  2|       c|
      |  3|   EMPTY|
      |  4|       a|
      |  5|       c|
      +---+--------+
      

      【讨论】:

      • 这里真的需要.na吗?不只是df.replace('', 'EMPTY', 'category') 工作吗?
      • 感谢大卫的提问。 DataFrameNaFunctions.replace 和 DataFrame.replace 是同一个别名
      • 在这里,我们知道确切的列名。如果我们不知道列名怎么办?也就是说,如果我试图将选项卡文件列表加载到表中,如何将空值替换为某些东西?
      【解决方案3】:

      如果列包含 null,OneHotEncoder 将失败并出现 NullPointerException。 因此我也将 udf 扩展为解析空值

      object OneHotEncoderExample {
        def main(args: Array[String]): Unit = {
          val conf = new SparkConf().setAppName("OneHotEncoderExample Application").setMaster("local[2]")
          val sc = new SparkContext(conf)
          val sqlContext = new SQLContext(sc)
      
          // $example on$
          val df1 = sqlContext.createDataFrame(Seq(
            (0.0, "a"),
            (1.0, "b"),
            (2.0, "c"),
            (3.0, ""),
            (4.0, null),
            (5.0, "c")
          )).toDF("id", "category")
      
      
          import org.apache.spark.sql.functions.udf
          def emptyValueSubstitution = udf[String, String] {
            case "" => "NA"
            case null => "null"
            case value => value
          }
          val df = df1.withColumn("category", emptyValueSubstitution( df1("category")) )
      
      
          val indexer = new StringIndexer()
            .setInputCol("category")
            .setOutputCol("categoryIndex")
            .fit(df)
          val indexed = indexer.transform(df)
          indexed.show()
      
          val encoder = new OneHotEncoder()
            .setInputCol("categoryIndex")
            .setOutputCol("categoryVec")
            .setDropLast(false)
          val encoded = encoder.transform(indexed)
          encoded.show()
          // $example off$
          sc.stop()
        }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-08-20
        • 1970-01-01
        • 1970-01-01
        • 2016-06-16
        • 2018-10-01
        • 1970-01-01
        相关资源
        最近更新 更多