Spark DataFrame在OneHotEncoder中处理空字符串

Nik*_*shi 6 scala apache-spark spark-csv apache-spark-ml apache-spark-mllib

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

我可以在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()
Run Code Online (Sandbox Code Playgroud)

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

提前谢谢,Nikhil

eli*_*sah 7

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

java.lang.IllegalArgumentException:要求失败:名称不能包含空字符串.在scala.Predef $ .require(Predef.scala:233)atg.apache.spark.ml.attribute.Attribute $$ anonfun $ 5.apply(attributes.scala:33)at 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 }
Run Code Online (Sandbox Code Playgroud)

然后,我将在列上应用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|
// +---+--------+
Run Code Online (Sandbox Code Playgroud)

现在,您可以回到您的转型

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])|
// +---+--------+-------------+-------------+
Run Code Online (Sandbox Code Playgroud)

编辑:

@Anthony在Scala中的解决方案:

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

我希望这有帮助!


Ant*_*ony 5

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

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|
+---+--------+
Run Code Online (Sandbox Code Playgroud)