Apache Spark在遇到缺失特征时会抛出NullPointerException异常。

19

我在使用PySpark中索引包含字符串的特征列时遇到了奇怪的问题。这是我的tmp.csv文件:

x0,x1,x2,x3 
asd2s,1e1e,1.1,0
asd2s,1e1e,0.1,0
,1e3e,1.2,0
bd34t,1e1e,5.1,1
asd2s,1e3e,0.2,0
bd34t,1e2e,4.3,1

我有一个缺失的值在'x0'列中。 首先,我使用pyspark_csv从csv文件中读取特征到DataFrame中:https://github.com/seahboonsiew/pyspark-csv 然后使用StringIndexer对x0进行索引:

import pyspark_csv as pycsv
from pyspark.ml.feature import StringIndexer

sc.addPyFile('pyspark_csv.py')

features = pycsv.csvToDataFrame(sqlCtx, sc.textFile('tmp.csv'))
indexer = StringIndexer(inputCol='x0', outputCol='x0_idx' )
ind = indexer.fit(features).transform(features)
print ind.collect()

当调用 ''ind.collect()'' 时,Spark 抛出 java.lang.NullPointerException。完整数据集(例如'x1')的情况下一切正常运作。

有人知道是什么原因导致这种情况,并且如何解决吗?

先行致谢!

Sergey

更新:

我使用的是 Spark 1.5.1。精确错误信息如下:

File "/spark/spark-1.4.1-bin-hadoop2.6/python/pyspark/sql/dataframe.py", line 258, in show
print(self._jdf.showString(n))

File "/spark/spark-1.4.1-bin-hadoop2.6/python/lib/py4j-0.8.2.1-src.zip/py4j/java_gateway.py", line 538, in __call__

File "/spark/spark-1.4.1-bin-hadoop2.6/python/lib/py4j-0.8.2.1-src.zip/py4j/protocol.py", line 300, in get_return_value

py4j.protocol.Py4JJavaError: An error occurred while calling o444.showString.
: java.lang.NullPointerException
at org.apache.spark.sql.types.Metadata$.org$apache$spark$sql$types$Metadata$$hash(Metadata.scala:208)
at org.apache.spark.sql.types.Metadata$$anonfun$org$apache$spark$sql$types$Metadata$$hash$2.apply(Metadata.scala:196)
at org.apache.spark.sql.types.Metadata$$anonfun$org$apache$spark$sql$types$Metadata$$hash$2.apply(Metadata.scala:196)
... etc

我已经尝试创建相同的DataFrame而不是读取csv文件:

df = sqlContext.createDataFrame(
  [('asd2s','1e1e',1.1,0), ('asd2s','1e1e',0.1,0), 
  (None,'1e3e',1.2,0), ('bd34t','1e1e',5.1,1), 
  ('asd2s','1e3e',0.2,0), ('bd34t','1e2e',4.3,1)],
  ['x0','x1','x2','x3'])

而且它会产生相同的错误。 稍微不同的示例可以正常工作,

df = sqlContext.createDataFrame(
  [(0, None, 1.2), (1, '06330986ed', 2.3), 
  (2, 'b7584c2d52', 2.5), (3, None, .8), 
  (4, 'bd17e19b3a', None), (5, '51b5c0f2af', 0.1)],
  ['id', 'x0', 'num'])

// after indexing x0

+---+----------+----+------+
| id|        x0| num|x0_idx|
+---+----------+----+------+
|  0|      null| 1.2|   0.0|
|  1|06330986ed| 2.3|   2.0|
|  2|b7584c2d52| 2.5|   4.0|
|  3|      null| 0.8|   0.0|
|  4|bd17e19b3a|null|   1.0|
|  5|51b5c0f2af| 0.1|   3.0|
+---+----------+----+------+

更新2:

我刚刚发现了在Scala中出现了同样的问题,所以我猜这是Spark的问题而不仅仅是PySpark的问题。具体来说,数据框架

val df = sqlContext.createDataFrame(
  Seq(("asd2s","1e1e",1.1,0), ("asd2s","1e1e",0.1,0), 
      (null,"1e3e",1.2,0), ("bd34t","1e1e",5.1,1), 
      ("asd2s","1e3e",0.2,0), ("bd34t","1e2e",4.3,1))
).toDF("x0","x1","x2","x3")

在索引“x0”特征时抛出 java.lang.NullPointerException 异常。此外,在以下数据帧中索引“x0”时

val df = sqlContext.createDataFrame(
  Seq((0, null, 1.2), (1, "b", 2.3), 
      (2, "c", 2.5), (3, "a", 0.8), 
      (4, "a", null), (5, "c", 0.1))
).toDF("id", "x0", "num")

我遇到了'java.lang.UnsupportedOperationException: Schema for type Any is not supported'的问题,这是由于第5个向量中缺少'num'值引起的。如果将其替换为数字,则即使在第1个向量中缺少值,一切都能正常工作。

我还尝试过旧版的Spark(1.4.1),结果也是相同的。

2个回答

14

看起来你使用的模块将空字符串转换为null,这个问题在下游处理过程中造成了混乱。乍一看它似乎是 PySpark 的一个 bug

如何解决?一个简单的解决方法是在索引之前删除 null 值:

features.na.drop()

或替换空值为某个占位符:

from pyspark.sql.functions import col, when

features.withColumn(
    "x0", when(col("x0").isNull(), "__SOME_PLACEHOLDER__").otherwise(col("x0")))

此外,您还可以使用spark-csv。 它高效,经过测试,并且作为奖励不会将空字符串转换为nulls

features = (sqlContext.read
    .format('com.databricks.spark.csv')
    .option("inferSchema", "true")
    .option("header", "true")
    .load("tmp.csv"))

1

目前唯一的解决方案是像@zero323建议的那样去除NA,或者使用toPandas()方法将Spark DataFrame转换为Pandas DataFrame并使用sklearn Imputer或任何自定义Imputer来填充数据,例如在scikit-learn中填充分类缺失值,然后将Pandas Dataframe转换回Spark DataFrame并进行操作。尽管如此,问题仍然存在,如果有任何问题,我会尝试提交错误报告。因为我对Spark相对较新,所以可能会有遗漏。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接