火花 >= 2.3, >= 3.0
由于 Spark 2.3OneHotEncoder
已被弃用,取而代之的是OneHotEncoderEstimator
. 如果您使用最新版本,请修改encoder
代码
from pyspark.ml.feature import OneHotEncoderEstimator
encoder = OneHotEncoderEstimator(
inputCols=["gender_numeric"],
outputCols=["gender_vector"]
)
在 Spark 3.0 中,此变体已重命名为OneHotEncoder
:
from pyspark.ml.feature import OneHotEncoder
encoder = OneHotEncoder(
inputCols=["gender_numeric"],
outputCols=["gender_vector"]
)
此外StringIndexer
已扩展为支持多个输入列:
StringIndexer(inputCols=["gender"], outputCols=["gender_numeric"])
火花 < 2.3
好吧,你可以写一个 UDF,但你为什么要写呢?已经有很多工具旨在处理此类任务:
from pyspark.sql import Row
from pyspark.ml.linalg import DenseVector
row = Row("gender", "foo", "bar")
df = sc.parallelize([
row("0", 3.0, DenseVector([0, 2.1, 1.0])),
row("1", 1.0, DenseVector([0, 1.1, 1.0])),
row("1", -1.0, DenseVector([0, 3.4, 0.0])),
row("0", -3.0, DenseVector([0, 4.1, 0.0]))
]).toDF()
首先StringIndexer
。
from pyspark.ml.feature import StringIndexer
indexer = StringIndexer(inputCol="gender", outputCol="gender_numeric").fit(df)
indexed_df = indexer.transform(df)
indexed_df.drop("bar").show()
## +------+----+--------------+
## |gender| foo|gender_numeric|
## +------+----+--------------+
## | 0| 3.0| 0.0|
## | 1| 1.0| 1.0|
## | 1|-1.0| 1.0|
## | 0|-3.0| 0.0|
## +------+----+--------------+
下一个OneHotEncoder
:
from pyspark.ml.feature import OneHotEncoder
encoder = OneHotEncoder(inputCol="gender_numeric", outputCol="gender_vector")
encoded_df = encoder.transform(indexed_df)
encoded_df.drop("bar").show()
## +------+----+--------------+-------------+
## |gender| foo|gender_numeric|gender_vector|
## +------+----+--------------+-------------+
## | 0| 3.0| 0.0|(1,[0],[1.0])|
## | 1| 1.0| 1.0| (1,[],[])|
## | 1|-1.0| 1.0| (1,[],[])|
## | 0|-3.0| 0.0|(1,[0],[1.0])|
## +------+----+--------------+-------------+
VectorAssembler
:
from pyspark.ml.feature import VectorAssembler
assembler = VectorAssembler(
inputCols=["gender_vector", "bar", "foo"], outputCol="features")
encoded_df_with_indexed_bar = (vector_indexer
.fit(encoded_df)
.transform(encoded_df))
final_df = assembler.transform(encoded_df)
如果bar
包含分类变量,您可以使用它VectorIndexer
来设置所需的元数据:
from pyspark.ml.feature import VectorIndexer
vector_indexer = VectorIndexer(inputCol="bar", outputCol="bar_indexed")
但这里不是这样。
最后,您可以使用管道包装所有这些:
from pyspark.ml import Pipeline
pipeline = Pipeline(stages=[indexer, encoder, vector_indexer, assembler])
model = pipeline.fit(df)
transformed = model.transform(df)
可以说,与从头开始编写所有内容相比,它更加健壮和干净。有一些警告,尤其是当您需要在不同数据集之间进行一致编码时。StringIndexer
您可以在和的官方文档中阅读更多信息VectorIndexer
。
获得可比输出的另一种方法RFormula
是:
RFormula
生成特征向量列和标签的双精度列或字符串列。就像在 R 中使用公式进行线性回归一样,字符串输入列将被一次性编码,而数字列将被转换为双精度值。如果标签列是字符串类型,它将首先转换为 double with StringIndexer
。如果 DataFrame 中不存在标签列,则将从公式中指定的响应变量创建输出标签列。
from pyspark.ml.feature import RFormula
rf = RFormula(formula="~ gender + bar + foo - 1")
final_df_rf = rf.fit(df).transform(df)
如您所见,它更简洁,但更难编写不允许太多自定义。然而,像这样的简单管道的结果将是相同的:
final_df_rf.select("features").show(4, False)
## +----------------------+
## |features |
## +----------------------+
## |[1.0,0.0,2.1,1.0,3.0] |
## |[0.0,0.0,1.1,1.0,1.0] |
## |(5,[2,4],[3.4,-1.0]) |
## |[1.0,0.0,4.1,0.0,-3.0]|
## +----------------------+
final_df.select("features").show(4, False)
## +----------------------+
## |features |
## +----------------------+
## |[1.0,0.0,2.1,1.0,3.0] |
## |[0.0,0.0,1.1,1.0,1.0] |
## |(5,[2,4],[3.4,-1.0]) |
## |[1.0,0.0,4.1,0.0,-3.0]|
## +----------------------+
关于你的问题:
制作一个具有类似功能的 UDF,我可以在 Spark SQL 查询中使用(或其他方式,我想)
它只是一个UDF,就像其他任何东西一样。确保您使用受支持的类型,除此之外,一切都应该正常工作。
获取上述地图产生的 RDD 并将其作为新列添加到 user_data 数据帧?
from pyspark.ml.linalg import VectorUDT
from pyspark.sql.types import StructType, StructField
schema = StructType([StructField("features", VectorUDT(), True)])
row = Row("features")
result.map(lambda x: row(DenseVector(x))).toDF(schema)
注意:
对于 Spark 1.x 替换pyspark.ml.linalg
为pyspark.mllib.linalg
.