Spark DataFrame在OneHotEncoder中处理空字符串

时间:2015-10-12 20:36:11

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

我正在将CSV文件(使用spark-csv)导入var s = ""; for(var i = 1; i < 11; i += 1) { s += i + " "; } console.log(s);,其值为DataFrame。应用String后,应用程序崩溃并显示错误OneHotEncoder。有没有办法解决这个问题?

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

requirement failed: Cannot have an empty string for name.

这很烦人,因为缺失/空值是一种非常普遍的情况。

提前致谢, NIKHIL

3 个答案:

答案 0 :(得分:7)

由于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&#39;

我创建一个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])|
// +---+--------+-------------+-------------+

修改

Scala 中的@Anthony解决方案:

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

我希望这有帮助!

答案 1 :(得分:4)

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

GL_RGBA

答案 2 :(得分:0)

如果列包含null,则OneHotEncoder会因NullPointerException而失败。 因此我也将udf扩展为tanslate null值

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()
  }
}