将元数据附加到 Spark 中的向量列
Attach metadata to vector column in Spark
上下文:
我有一个包含两列的数据框:标签和特征。
org.apache.spark.sql.DataFrame = [label: int, features: vector]
其中 features 是使用 VectorAssembler 构建的 mllib.linalg.VectorUDT 数字类型。
问题:
有没有办法将模式分配给特征向量?我想跟踪每个功能的名称。
目前已尝试:
val defaultAttr = NumericAttribute.defaultAttr
val attrs = Array("feat1", "feat2", "feat3").map(defaultAttr.withName)
val attrGroup = new AttributeGroup("userFeatures", attrs.asInstanceOf[Array[Attribute]])
scala> attrGroup.toMetadata
res197: org.apache.spark.sql.types.Metadata = {"ml_attr":{"attrs":{"numeric":[{"idx":0,"name":"f1"},{"idx":1,"name":"f2"},{"idx":2,"name":"f3"}]},"num_attrs":3}}
但不确定如何将其应用于现有数据框。
至少有两个选项:
在现有 DataFrame
上,您可以使用带有 metadata
参数的 as
方法:
import org.apache.spark.ml.attribute._
val rdd = sc.parallelize(Seq(
(1, Vectors.dense(1.0, 2.0, 3.0))
))
val df = rdd.toDF("label", "features")
df.withColumn("features", $"features".as("_", attrGroup.toMetadata))
当您创建新的 DataFrame
转换 AttributeGroup
toStructField
并将其用作给定列的架构时:
import org.apache.spark.sql.types.{StructType, StructField, IntegerType}
val schema = StructType(Array(
StructField("label", IntegerType, false),
attrGroup.toStructField()
))
spark.createDataFrame(
rdd.map(row => Row.fromSeq(row.productIterator.toSeq)),
schema)
如果矢量列是使用 VectorAssembler
描述父列的列元数据创建的,则应该已经附加。
import org.apache.spark.ml.feature.VectorAssembler
val raw = sc.parallelize(Seq(
(1, 1.0, 2.0, 3.0)
)).toDF("id", "feat1", "feat2", "feat3")
val assembler = new VectorAssembler()
.setInputCols(Array("feat1", "feat2", "feat3"))
.setOutputCol("features")
val dfWithMeta = assembler.transform(raw).select($"id", $"features")
dfWithMeta.schema.fields(1).metadata
// org.apache.spark.sql.types.Metadata = {"ml_attr":{"attrs":{"numeric":[
// {"idx":0,"name":"feat1"},{"idx":1,"name":"feat2"},
// {"idx":2,"name":"feat3"}]},"num_attrs":3}
矢量字段不能使用点语法直接访问(如 $features.feat1
),但可以通过专用工具使用,如 VectorSlicer
:
import org.apache.spark.ml.feature.VectorSlicer
val slicer = new VectorSlicer()
.setInputCol("features")
.setOutputCol("featuresSubset")
.setNames(Array("feat1", "feat3"))
slicer.transform(dfWithMeta).show
// +---+-------------+--------------+
// | id| features|featuresSubset|
// +---+-------------+--------------+
// | 1|[1.0,2.0,3.0]| [1.0,3.0]|
// +---+-------------+--------------+
对于 PySpark,请参阅
上下文: 我有一个包含两列的数据框:标签和特征。
org.apache.spark.sql.DataFrame = [label: int, features: vector]
其中 features 是使用 VectorAssembler 构建的 mllib.linalg.VectorUDT 数字类型。
问题: 有没有办法将模式分配给特征向量?我想跟踪每个功能的名称。
目前已尝试:
val defaultAttr = NumericAttribute.defaultAttr
val attrs = Array("feat1", "feat2", "feat3").map(defaultAttr.withName)
val attrGroup = new AttributeGroup("userFeatures", attrs.asInstanceOf[Array[Attribute]])
scala> attrGroup.toMetadata
res197: org.apache.spark.sql.types.Metadata = {"ml_attr":{"attrs":{"numeric":[{"idx":0,"name":"f1"},{"idx":1,"name":"f2"},{"idx":2,"name":"f3"}]},"num_attrs":3}}
但不确定如何将其应用于现有数据框。
至少有两个选项:
在现有
DataFrame
上,您可以使用带有metadata
参数的as
方法:import org.apache.spark.ml.attribute._ val rdd = sc.parallelize(Seq( (1, Vectors.dense(1.0, 2.0, 3.0)) )) val df = rdd.toDF("label", "features") df.withColumn("features", $"features".as("_", attrGroup.toMetadata))
当您创建新的
DataFrame
转换AttributeGroup
toStructField
并将其用作给定列的架构时:import org.apache.spark.sql.types.{StructType, StructField, IntegerType} val schema = StructType(Array( StructField("label", IntegerType, false), attrGroup.toStructField() )) spark.createDataFrame( rdd.map(row => Row.fromSeq(row.productIterator.toSeq)), schema)
如果矢量列是使用 VectorAssembler
描述父列的列元数据创建的,则应该已经附加。
import org.apache.spark.ml.feature.VectorAssembler
val raw = sc.parallelize(Seq(
(1, 1.0, 2.0, 3.0)
)).toDF("id", "feat1", "feat2", "feat3")
val assembler = new VectorAssembler()
.setInputCols(Array("feat1", "feat2", "feat3"))
.setOutputCol("features")
val dfWithMeta = assembler.transform(raw).select($"id", $"features")
dfWithMeta.schema.fields(1).metadata
// org.apache.spark.sql.types.Metadata = {"ml_attr":{"attrs":{"numeric":[
// {"idx":0,"name":"feat1"},{"idx":1,"name":"feat2"},
// {"idx":2,"name":"feat3"}]},"num_attrs":3}
矢量字段不能使用点语法直接访问(如 $features.feat1
),但可以通过专用工具使用,如 VectorSlicer
:
import org.apache.spark.ml.feature.VectorSlicer
val slicer = new VectorSlicer()
.setInputCol("features")
.setOutputCol("featuresSubset")
.setNames(Array("feat1", "feat3"))
slicer.transform(dfWithMeta).show
// +---+-------------+--------------+
// | id| features|featuresSubset|
// +---+-------------+--------------+
// | 1|[1.0,2.0,3.0]| [1.0,3.0]|
// +---+-------------+--------------+
对于 PySpark,请参阅