2016-02-10 8 views
8

Kontekst: Mam ramkę danych z dwiema kolumnami: etykietą i funkcjami.Dołącz metadane do kolumny wektorowej w Spark

org.apache.spark.sql.DataFrame = [label: int, features: vector] 

Gdzie cechy jest mllib.linalg.VectorUDT typu numerycznego zbudowany przy użyciu VectorAssembler.

Pytanie: Czy istnieje sposób przypisania schematu do wektora funkcji? Chcę śledzić nazwę każdej funkcji.

próbowałem dotąd:

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}} 

Ale nie był pewien, jak zastosować to do istniejącej ramki danych.

Odpowiedz

9

tam bo dwie opcje:

  1. na istniejących DataFrame można użyć as metoda z metadata argumentem:

    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)) 
    
  2. Podczas tworzenia nowego DataFrame konwertować AttributeGrouptoStructField i używać go jako Schemat dla danej kolumny:

    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) 
    

Jeśli kolumna wektorowa została utworzona przy użyciu VectorAssembler, metadane kolumny opisujące kolumny nadrzędne powinny być już dołączone.

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} 

pola wektorowe nie są bezpośrednio dostępne za pomocą składni kropki (jak $features.feat1), ale może być używany przez wyspecjalizowanych narzędzi jak 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]| 
// +---+-------------+--------------+ 

Dla PySpark zobaczyć How can I declare a Column as a categorical feature in a DataFrame for use in ml

+1

Czyżby "feat1" kolumna być wyodrębnione przy użyciu nazwy zamiast indeksu? – gstvolvr

+0

Rodzaj. Możesz użyć 'VectorSlicer'. – zero323

+0

Rozumiem. Dziękuję Ci! – gstvolvr

Powiązane problemy