pyspark randomForest 特征重要性:如何从列号中获取列名
Posted
技术标签:
【中文标题】pyspark randomForest 特征重要性:如何从列号中获取列名【英文标题】:pyspark randomForest feature importance: how to get column names from the column numbers 【发布时间】:2017-12-14 21:43:07 【问题描述】:我在spark中使用的是标准的(string indexer + one hot encoder + randomForest) pipeline,如下图
labelIndexer = StringIndexer(inputCol = class_label_name, outputCol="indexedLabel").fit(data)
string_feature_indexers = [
StringIndexer(inputCol=x, outputCol="int_0".format(x)).fit(data)
for x in char_col_toUse_names
]
onehot_encoder = [
OneHotEncoder(inputCol="int_"+x, outputCol="onehot_0".format(x))
for x in char_col_toUse_names
]
all_columns = num_col_toUse_names + bool_col_toUse_names + ["onehot_"+x for x in char_col_toUse_names]
assembler = VectorAssembler(inputCols=[col for col in all_columns], outputCol="features")
rf = RandomForestClassifier(labelCol="indexedLabel", featuresCol="features", numTrees=100)
labelConverter = IndexToString(inputCol="prediction", outputCol="predictedLabel", labels=labelIndexer.labels)
pipeline = Pipeline(stages=[labelIndexer] + string_feature_indexers + onehot_encoder + [assembler, rf, labelConverter])
crossval = CrossValidator(estimator=pipeline,
estimatorParamMaps=paramGrid,
evaluator=evaluator,
numFolds=3)
cvModel = crossval.fit(trainingData)
现在,在拟合之后,我可以使用 cvModel.bestModel.stages[-2].featureImportances
获得随机森林和特征重要性,但这并没有给我特征/列名称,而只是特征编号。
我得到的如下:
print(cvModel.bestModel.stages[-2].featureImportances)
(1446,[3,4,9,18,20,103,766,981,983,1098,1121,1134,1148,1227,1288,1345,1436,1444],[0.109898803421,0.0967396441648,4.24568235244e-05,0.0369705839109,0.0163489685127,3.2286694534e-06,0.0208192703688,0.0815822887175,0.0466903663708,0.0227619959989,0.0850922269211,0.000113388896956,0.0924779490403,0.163835022713,0.118987129392,0.107373548367,3.35577640585e-05,0.000229569946193])
如何将其映射回某些列名或列名+值格式? 基本上是为了获得随机森林的特征重要性以及列名。
【问题讨论】:
阿比舍克你最后是怎么做到的? 【参考方案1】:转换后的数据集元数据具有所需的属性。这是一个简单的方法 -
创建一个 pandas 数据框(通常特征列表不会很大,因此存储 pandas DF 不会出现内存问题)
pandasDF = pd.DataFrame(dataset.schema["features"].metadata["ml_attr"]
["attrs"]["binary"]+dataset.schema["features"].metadata["ml_attr"]["attrs"]["numeric"]).sort_values("idx")
然后创建一个广播字典来映射。在分布式环境中广播是必要的。
feature_dict = dict(zip(pandasDF["idx"],pandasDF["name"]))
feature_dict_broad = sc.broadcast(feature_dict)
你也可以看看here和here
【讨论】:
这应该是正确答案——简洁有效。谢谢!【参考方案2】:嘿,为什么不通过列表扩展将其映射回原始列。这是一个例子:
# in your case: trainingData.columns
data_frame_columns = ["A", "B", "C", "D", "E", "F"]
# in your case: print(cvModel.bestModel.stages[-2].featureImportances)
feature_importance = (1, [1, 3, 5], [0.5, 0.5, 0.5])
rf_output = [(data_frame_columns[i], feature_importance[2][j]) for i, j in zip(feature_importance[1], range(len(feature_importance[2])))]
dict(rf_output)
'B': 0.5, 'D': 0.5, 'F': 0.5
【讨论】:
是的,但是您错过了在 stringindexer/onehotencoder 之后列名发生变化的点。由汇编程序组合的一个,我想映射到它们。我当然可以做很长的路要走,但我更担心 spark(ml) 是否有一些更短的路,比如 scikit learn 一样:) 啊,好吧,我的错。但是,很长的路应该仍然有效。我认为目前没有短期解决方案。 Spark ML API 不像 scikit learn 那样强大和冗长。 是的,我知道 :),只是想让问题保持开放以征求建议 :)。谢谢大佬【参考方案3】:在 ml 算法之后,我无法找到任何方法来获取列的真实初始列表,我将其用作当前的解决方法。
print(len(cols_now))
FEATURE_COLS=[]
for x in cols_now:
if(x[-6:]!="catVar"):
FEATURE_COLS+=[x]
else:
temp=trainingData.select([x[:-7],x[:-6]+"tmp"]).distinct().sort(x[:-6]+"tmp")
temp_list=temp.select(x[:-7]).collect()
FEATURE_COLS+=[list(x)[0] for x in temp_list]
print(len(FEATURE_COLS))
print(FEATURE_COLS)
我在所有索引器 (_tmp) 和编码器 (_catVar) 中保持一致的后缀命名,例如:
column_vec_in = str_col
column_vec_out = [col+"_catVar" for col in str_col]
indexers = [StringIndexer(inputCol=x, outputCol=x+'_tmp')
for x in column_vec_in ]
encoders = [OneHotEncoder(dropLast=False, inputCol=x+"_tmp", outputCol=y)
for x,y in zip(column_vec_in, column_vec_out)]
tmp = [[i,j] for i,j in zip(indexers, encoders)]
tmp = [i for sublist in tmp for i in sublist]
这可以进一步改进和推广,但目前这种繁琐的工作效果最好
【讨论】:
以上是关于pyspark randomForest 特征重要性:如何从列号中获取列名的主要内容,如果未能解决你的问题,请参考以下文章
sklearn 的 RandomForest 中如何计算特征重要性?
R语言使用randomForest包构建随机森林模型(Random forests)使用importance函数查看特征重要度使用table函数计算混淆矩阵评估分类模型性能包外错误估计OOB