使用 Spark 加载时,混合列中的空字符串会使一行无效
Empty string in mixed column nullifies a row when loaded using Spark
考虑以下 JSON:
{"col1": "yoyo", "col2": 1.5}
{"col1": "", "col2": 6}
{"col1": "456", "col2": ""}
{"col1": 444, "col2": 12}
{"col1": null, "col2": 1.7}
{"col1": 3.14, "col2": null}
我使用 (Py)Spark 加载如下:
from pyspark.sql import SparkSession
spark = SparkSession.builder.master("local[*]").getOrCreate()
df = spark.read.json("my.json")
df.show()
产生:
+----+----+
|col1|col2|
+----+----+
|yoyo| 1.5|
| | 6.0|
|null|null| <---===***
| 444|12.0|
|null| 1.7|
|3.14|null|
+----+----+
我很难理解为什么第三行无效。
似乎原因是第二列中唯一的字符串是空字符串 ""
并且这以某种方式导致无效。
请注意 col1
在第二行也包含一个空字符串,但该行 未 无效。
对我来说,这是一个非常令人困惑和意外的行为。
我无法在文档中找到提示。
- 这种行为是预期的吗?为什么会这样?
- 我希望第 3 行包含
col1
的字符串 "456"
和 col2
的空字符串 ""
。我怎样才能实现这种行为(对我来说感觉更自然)?
使用 Spark 时无法在单个列中混合不同的数据类型。
读取json文件时,Spark会尝试推断每一列的数据类型(详情见底部注释)。这里,Spark认为col1
是string类型,col2
是double类型。这可以通过阅读 json 文件并在数据帧上使用 printSchema
来确认。
这意味着数据是根据这些推断的数据类型进行解析的。因此,Spark 将尝试将 ""
解析为显然失败的双精度值。 (对于 col1
中的第二行,它有效,因为 col1
被推断为字符串类型,因此 ""
是一个有效输入。)
使用 spark.read.json
时可以设置不同的模式。从 documentation 我们有:
mode -
allows a mode for dealing with corrupt records during parsing. If None is
set, it uses the default value, PERMISSIVE
.
PERMISSIVE
: when it meets a corrupted record, puts the malformed string into a field configured by columnNameOfCorruptRecord, and sets other fields to null. To keep corrupt records, an user can set a string type field named columnNameOfCorruptRecord in an user-defined schema. If a schema does not have the field, it drops corrupt records during parsing. When inferring a schema, it implicitly adds a columnNameOfCorruptRecord field in an output schema.
DROPMALFORMED
: ignores the whole corrupted records.
FAILFAST
: throws an exception when it meets corrupted records.
从上面我们可以看出默认使用PERMISSIVE
模式,如果遇到损坏的记录,所有字段都设置为null
。这就是这种情况下发生的情况。为了确认,可以将 mode
设置为 FAILFAST
、
spark.read.json("my.json", mode='FAILFAST')
这会给出一个例外。
这可以通过不推断数据类型并将所有内容读取为字符串来解决。
spark.read.json("my.json", primitivesAsString='true')
注意: json 的模式推断与 csv 和 txt 等其他来源相比有点不同,请参阅 here。对于 json 文件,""
和 null
都有特殊处理来处理不区分两者的 json 生成器。对于 csv 文件,具有空字符串 ""
的列仍然会使整个列被推断为字符串,但对于 json.
则不是这种情况
作为旁注,将 ""
替换为例如"5"
in col2
将使推断的列类型为字符串。
考虑以下 JSON:
{"col1": "yoyo", "col2": 1.5}
{"col1": "", "col2": 6}
{"col1": "456", "col2": ""}
{"col1": 444, "col2": 12}
{"col1": null, "col2": 1.7}
{"col1": 3.14, "col2": null}
我使用 (Py)Spark 加载如下:
from pyspark.sql import SparkSession
spark = SparkSession.builder.master("local[*]").getOrCreate()
df = spark.read.json("my.json")
df.show()
产生:
+----+----+
|col1|col2|
+----+----+
|yoyo| 1.5|
| | 6.0|
|null|null| <---===***
| 444|12.0|
|null| 1.7|
|3.14|null|
+----+----+
我很难理解为什么第三行无效。
似乎原因是第二列中唯一的字符串是空字符串 ""
并且这以某种方式导致无效。
请注意 col1
在第二行也包含一个空字符串,但该行 未 无效。
对我来说,这是一个非常令人困惑和意外的行为。 我无法在文档中找到提示。
- 这种行为是预期的吗?为什么会这样?
- 我希望第 3 行包含
col1
的字符串"456"
和col2
的空字符串""
。我怎样才能实现这种行为(对我来说感觉更自然)?
使用 Spark 时无法在单个列中混合不同的数据类型。
读取json文件时,Spark会尝试推断每一列的数据类型(详情见底部注释)。这里,Spark认为col1
是string类型,col2
是double类型。这可以通过阅读 json 文件并在数据帧上使用 printSchema
来确认。
这意味着数据是根据这些推断的数据类型进行解析的。因此,Spark 将尝试将 ""
解析为显然失败的双精度值。 (对于 col1
中的第二行,它有效,因为 col1
被推断为字符串类型,因此 ""
是一个有效输入。)
使用 spark.read.json
时可以设置不同的模式。从 documentation 我们有:
mode -
allows a mode for dealing with corrupt records during parsing. If None is set, it uses the default value,PERMISSIVE
.
PERMISSIVE
: when it meets a corrupted record, puts the malformed string into a field configured by columnNameOfCorruptRecord, and sets other fields to null. To keep corrupt records, an user can set a string type field named columnNameOfCorruptRecord in an user-defined schema. If a schema does not have the field, it drops corrupt records during parsing. When inferring a schema, it implicitly adds a columnNameOfCorruptRecord field in an output schema.DROPMALFORMED
: ignores the whole corrupted records.FAILFAST
: throws an exception when it meets corrupted records.
从上面我们可以看出默认使用PERMISSIVE
模式,如果遇到损坏的记录,所有字段都设置为null
。这就是这种情况下发生的情况。为了确认,可以将 mode
设置为 FAILFAST
、
spark.read.json("my.json", mode='FAILFAST')
这会给出一个例外。
这可以通过不推断数据类型并将所有内容读取为字符串来解决。
spark.read.json("my.json", primitivesAsString='true')
注意: json 的模式推断与 csv 和 txt 等其他来源相比有点不同,请参阅 here。对于 json 文件,""
和 null
都有特殊处理来处理不区分两者的 json 生成器。对于 csv 文件,具有空字符串 ""
的列仍然会使整个列被推断为字符串,但对于 json.
作为旁注,将 ""
替换为例如"5"
in col2
将使推断的列类型为字符串。