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

28
我正在尝试在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.18589731365614548, 0.275173571416679, 0.0]

t统计量:[-23.348593508995318,-44.72813283953004,19.836508234714472,144.49248881747755,-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中,我只有一个名为“features”的列,其中包含稀疏向量的行。
当我有一个独热编码特征时,这更成为一个问题,因为如果我有一个长度为n的编码变量,我将得到n个对应的系数/ p值/ t值等。
3个回答

27

截至今日,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)
提取并展平机器学习属性:
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)]

嗨@zero323,我尝试了你的解决方案,但是在元数据中我无法找到attrs。当我检查lr_transformed.schema[lrm.summary.featuresCol].metadata时,我只得到了{'ml_attr': {'num_attrs': 105}}。你能否给我一些关于这个问题的指导?谢谢! - Anita

16
您可以在这里查看列的实际顺序。
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")

应该给出所有列的确切顺序


你可以用以下方式完成,无需使用pandas:[x["name"] for x in sorted(df.schema["features"].metadata["ml_attr"]["attrs"]["binary"]+ df.schema["features"].metadata["ml_attr"]["attrs"]["numeric"], key=lambda x: x["idx"])] - Andy Reagan
最佳答案! - Morteza
你是怎么知道二进制先出现的? - Jonathan Allen Grant

3

以下是一句话答案:

[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 提供了这个核心内容。

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