如何将特征从 VectorAssembler 的输出映射回 Spark ML 中的列名?



我正在尝试在 PySpark 中运行线性回归,我想创建一个包含汇总统计数据的表,例如数据集中每一列的系数、P 值和 t 值。然而,为了训练线性回归模型,我必须使用 Spark 的VectorAssembler创建一个特征向量,现在每一行我都有一个特征向量和目标列。 当我尝试访问 Spark 的内置回归摘要统计信息时,它们为每个统计信息提供了一个非常原始的数字列表,并且无法知道哪个属性对应于哪个值,这真的很难手动计算出大量列。 如何将这些值映射回列名称?

例如,我当前的输出如下所示:

系数:[-187.807832407,-187.058926726,85.1716641376,10595.3352802,-127.258892837,-

39.2827730493,-1206.47228704,33.7078197705,99.9956812528]

P 值: [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,0.0, 0.18589731365614548, 0.275173571416679, 0.0]

t-statistics: [-23.348593508995318, -44.72813283953004, 19.836508234714472, 144.492488817477555, -16.547272230754242, -9.560681351483941, -19.563547400189073, 1.3228378389036228, 1.0912415361190977, 20.383256127350474]

系数标准误差: [8.043646497811427, 4.182131353367049, 4.293682291754585, 73.32793120907755, 7.690626652102948, 4.108783841348964, 61.669402913526625, 25.481445101737247,91.63478289909655, 609.7007361468519]

这些数字毫无意义,除非我知道它们对应于哪个属性。但是在我的DataFrame中,我只有一列称为"特征",其中包含稀疏向量行。

当我有一个 one-hot 编码特征时,这是一个更大的问题,因为如果我有一个编码长度为 n 的变量,我将得到 n 个相应的系数/p 值/t 值等。

截至今天,Spark 没有提供任何可以为你做到这一点的方法,所以如果你必须创建自己的方法。假设您的数据如下所示:

import random
random.seed(1)
df = sc.parallelize([(
random.choice([0.0, 1.0]), 
random.choice(["a", "b", "c"]),
random.choice(["foo", "bar"]),
random.randint(0, 100),
random.random(),
) for _ in range(100)]).toDF(["label", "x1", "x2", "x3", "x4"])

并使用以下管道进行处理:

from pyspark.ml.feature import StringIndexer, OneHotEncoder, VectorAssembler
from pyspark.ml import Pipeline
from pyspark.ml.regression import LinearRegression
indexers = [
StringIndexer(inputCol=c, outputCol="{}_idx".format(c)) for c in ["x1", "x2"]]
encoders = [
OneHotEncoder(
inputCol=idx.getOutputCol(),
outputCol="{0}_enc".format(idx.getOutputCol())) for idx in indexers]
assembler = VectorAssembler(
inputCols=[enc.getOutputCol() for enc in encoders] + ["x3", "x4"],
outputCol="features")
pipeline = Pipeline(
stages=indexers + encoders + [assembler, LinearRegression()])
model = pipeline.fit(df)

获取LinearRegressionModel

lrm = model.stages[-1]

转换数据:

transformed =  model.transform(df)

提取并展平 ML 属性:

from itertools import chain
attrs = sorted(
(attr["idx"], attr["name"]) for attr in (chain(*transformed
.schema[lrm.summary.featuresCol]
.metadata["ml_attr"]["attrs"].values())))

并映射到输出:

[(name, lrm.summary.pValues[idx]) for idx, name in attrs]
[('x1_idx_enc_a', 0.26400012641279824),
('x1_idx_enc_c', 0.06320192217171572),
('x2_idx_enc_foo', 0.40447778902400433),
('x3', 0.1081883594783335),
('x4', 0.4545851609776568)]
[(name, lrm.coefficients[idx]) for idx, name in attrs]
[('x1_idx_enc_a', 0.13874401585637453),
('x1_idx_enc_c', 0.23498565469334595),
('x2_idx_enc_foo', -0.083558932128022873),
('x3', 0.0030186112903237442),
('x4', -0.12951394186593695)]

您可以在此处查看列的实际顺序

df.schema["features"].metadata["ml_attr"]["attrs"]

通常会有两个类,["二进制]和["数字"]

pd.DataFrame(df.schema["features"].metadata["ml_attr"]["attrs"]["binary"]+df.schema["features"].metadata["ml_attr"]["attrs"]["numeric"]).sort_values("idx")

应给出所有列的确切顺序

这是一行答案:

[x["name"] for x in sorted(train_downsampled.schema["all_features"].metadata["ml_attr"]["attrs"]["binary"]+
train_downsampled.schema["all_features"].metadata["ml_attr"]["attrs"]["numeric"], 
key=lambda x: x["idx"])]

感谢@pratiklodha提供的核心。

最新更新