Spark:在多个数据帧上使用相同的OneHotEncoder

时间:2016-04-04 14:04:19

标签: python apache-spark dataframe pyspark apache-spark-ml

我有两个DataFrames具有相同的列,我想转换 使用One-Hot-Encoding的分类列到矢量中。问题是 例如,在训练集3中,可能会出现3个唯一值 测试集你可能少于那个。

Training Set:        Test Set:

+------------+       +------------+
|    Type    |       |    Type    |
+------------+       +------------+
|     0      |       |     0      | 
|     1      |       |     1      | 
|     1      |       |     1      | 
|     3      |       |     1      | 
+------------+       +------------+

在这种情况下,OneHotEncoder在训练和测试集上创建具有不同长度的向量(因为向量的每个元素表示存在唯一值)。

是否可以在多个OneHotEncoder上使用相同的DataFrames? 没有fit功能,因此我不知道如何做到这一点。 感谢。

2 个答案:

答案 0 :(得分:4)

Spark> = 2.3

Spark 2.3添加了新的OneHotEncoderEstimatorOneHotEncoderModel类,它们可以按照您的期望在这里工作。

from pyspark.ml.feature import OneHotEncoderEstimator, OneHotEncoderModel

encoder = (OneHotEncoderEstimator()
    .setInputCols(["type"])
    .setOutputCols(["encoded"])
    .setDropLast(False))
model = encoder.fit(training)  # type: OneHotEncoderModel

model.transform(training).show()
# +----+-------------+
# |type|      encoded|
# +----+-------------+
# | 0.0|(4,[0],[1.0])|
# | 1.0|(4,[1],[1.0])|
# | 1.0|(4,[1],[1.0])|
# | 3.0|(4,[3],[1.0])|
# +----+-------------+

model.transform(testing).show()
# +----+-------------+
# |type|      encoded|
# +----+-------------+
# | 0.0|(4,[0],[1.0])|
# | 1.0|(4,[1],[1.0])|
# | 1.0|(4,[1],[1.0])|
# | 1.0|(4,[1],[1.0])|
# +----+-------------+

Spark< 2.3

OneHotEncoder无意单独使用。相反,它应该是Pipeline的一部分,它可以利用列元数据。请考虑以下示例:

training = sc.parallelize([(0., ), (1., ), (1., ), (3., )]).toDF(["type"])
testing  = sc.parallelize([(0., ), (1., ), (1., ), (1., )]).toDF(["type"])

直接使用编码器时,它不了解上下文:

from pyspark.ml.feature import OneHotEncoder

encoder = OneHotEncoder().setOutputCol("encoded").setDropLast(False)


encoder.setInputCol("type").transform(training).show()
## +----+-------------+
## |type|      encoded|
## +----+-------------+
## | 0.0|(4,[0],[1.0])|
## | 1.0|(4,[1],[1.0])|
## | 1.0|(4,[1],[1.0])|
## | 3.0|(4,[3],[1.0])|
## +----+-------------+


encoder.setInputCol("type").transform(testing).show()
## +----+-------------+
## |type|      encoded|
## +----+-------------+
## | 0.0|(2,[0],[1.0])|
## | 1.0|(2,[1],[1.0])|
## | 1.0|(2,[1],[1.0])|
## | 1.0|(2,[1],[1.0])|
## +----+-------------+

现在让我们添加所需的元数据。例如,可以使用StringIndexer

indexer = (StringIndexer()
  .setInputCol("type")
  .setOutputCol("type_idx")
  .fit(training))

如果在索引列上应用编码器,您将在两个数据集上获得一致的编码:

(encoder.setInputCol("type_idx")
   .transform(indexer.transform(training))
   .show())

## +----+--------+-------------+
## |type|type_idx|      encoded|
## +----+--------+-------------+
## | 0.0|     1.0|(3,[1],[1.0])|
## | 1.0|     0.0|(3,[0],[1.0])|
## | 1.0|     0.0|(3,[0],[1.0])|
## | 3.0|     2.0|(3,[2],[1.0])|
## +----+--------+-------------+

(编码器        .setInputCol( “type_idx”)        .transform(indexer.transform(测试))        .show())

## +----+--------+-------------+
## |type|type_idx|      encoded|
## +----+--------+-------------+
## | 0.0|     1.0|(3,[1],[1.0])|
## | 1.0|     0.0|(3,[0],[1.0])|
## | 1.0|     0.0|(3,[0],[1.0])|
## | 1.0|     0.0|(3,[0],[1.0])|
## +----+--------+-------------+

请注意,您通过这种方式获得的标签不会反映输入数据中的值。如果一致编码是一项硬性要求,则应手动提供架构:

from pyspark.sql.types import StructType, StructField, DoubleType

meta = {"ml_attr": {
    "name": "type",
    "type": "nominal", 
    "vals": ["0.0", "1.0", "3.0"]
}}

schema = StructType([StructField("type", DoubleType(), False, meta)])

training = sc.parallelize([(0., ), (1., ), (1., ), (3., )]).toDF(schema)
testing  = sc.parallelize([(0., ), (1., ), (1., ), (1., )]).toDF(schema)

assert (
    encoder.setInputCol("type").transform(training).first()[-1].size == 
    encoder.setInputCol("type").transform(testing).first()[-1].size
)

答案 1 :(得分:0)

我们可以通过创建元矩阵并创建多个OneHotEncoders将其扩展到多个柱状数据集。 这些步骤可以在管道中进行。