scala - 将元数据附加到 Spark 中的向量列

标签 scala apache-spark apache-spark-mllib apache-spark-ml

上下文:
我有一个包含两列的数据框:标签和特征。

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您可以使用 as方法与 metadata争论:
    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,请参阅 How can I declare a Column as a categorical feature in a DataFrame for use in ml

    关于scala - 将元数据附加到 Spark 中的向量列,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35305154/

    相关文章:

    scala - Scala 风格的元素?

    python - 是否可以使用 unicode 列实例化 DataFrame?

    apache-spark - 为什么我无法加载 PySpark RandomForestClassifier 模型?

    apache-spark - 将 PySpark 数据框转换为增量表

    apache-spark - Pyspark 中的 JSON 文件解析

    apache-spark - 无法将类型 <class 'pyspark.ml.linalg.SparseVector' > 转换为 Vector

    apache-spark - 对象 ml 不是包 org.apache.spark 的成员

    sql - 带动态最后的 Spark 高级窗口

    scala - 从 Scala 列表中删除一个元素

    scala - 使用 Scala-cats 隐式解析