>

컨텍스트 :모든 범주 형 값이 StringIndexer를 사용하여 색인화 된 데이터 프레임이 있습니다.

val categoricalColumns = df.schema.collect { case StructField(name, StringType, nullable, meta) => name }    
val categoryIndexers = categoricalColumns.map {
  col => new StringIndexer().setInputCol(col).setOutputCol(s"${col}Indexed") 
}

그런 다음 VectorAssembler를 사용하여 모든 피처 열 (인덱싱 된 범주 열 포함)을 벡터화했습니다.

val assembler = new VectorAssembler()
    .setInputCols(dfIndexed.columns.diff(List("label") ++ categoricalColumns))
    .setOutputCol("features")

분류기와 몇 가지 추가 단계를 적용한 후 레이블, 기능 및 예측이 포함 된 데이터 프레임으로 끝납니다. 색인화 된 값을 원래 문자열 형식으로 다시 변환하기 위해 기능 벡터를 확장하여 열을 분리하고 싶습니다.

val categoryConverters = categoricalColumns.zip(categoryIndexers).map {
colAndIndexer => new IndexToString().setInputCol(s"${colAndIndexer._1}Indexed").setOutputCol(colAndIndexer._1).setLabels(colAndIndexer._2.fit(df).labels)
}

질문 :이를 수행하는간단한방법이 있습니까, 아니면 어떻게 든 예측 열을 테스트 데이터 프레임에 연결하는 가장 좋은 방법입니까?

내가 시도한 것 :

val featureSlicers = categoricalColumns.map {
  col => new VectorSlicer().setInputCol("features").setOutputCol(s"${col}Indexed").setNames(Array(s"${col}Indexed"))
}

이것을 적용하면 원하는 열을 얻을 수 있지만 Double 형식이 아닌 Vector 형식입니다 (의도대로).

수정 : 원하는 출력은 원래 데이터 프레임 (예 : 인덱스가 아닌 문자열의 범주 기능)이며 예측 된 레이블을 나타내는 추가 열 (내 경우에는 0 또는 1)입니다.

예를 들어 분류기의 출력이 다음과 같다고 가정 해 봅시다.

+-----+---------+----------+
|label| features|prediction|
+-----+---------+----------+
|  1.0|[0.0,3.0]|       1.0|
+-----+---------+----------+

각 기능에 VectorSlicer를 적용하면 다음과 같은 이점을 얻을 수 있습니다.

+-----+---------+----------+-------------+-------------+
|label| features|prediction|statusIndexed|artistIndexed|
+-----+---------+----------+-------------+-------------+
|  1.0|[0.0,3.0]|       1.0|        [0.0]|        [3.0]|
+-----+---------+----------+-------------+-------------+

위대한 것이지만 필요합니다 :

+-----+---------+----------+-------------+-------------+
|label| features|prediction|statusIndexed|artistIndexed|
+-----+---------+----------+-------------+-------------+
|  1.0|[0.0,3.0]|       1.0|         0.0 |         3.0 |
+-----+---------+----------+-------------+-------------+

그러면 IndexToString을 사용하여 다음으로 변환 할 수 있습니다.

+-----+---------+----------+-------------+-------------+
|label| features|prediction|    status   |    artist   |
+-----+---------+----------+-------------+-------------+
|  1.0|[0.0,3.0]|       1.0|        good |  Pink Floyd |
+-----+---------+----------+-------------+-------------+

또는 심지어 :

+-----+----------+-------------+-------------+
|label|prediction|    status   |    artist   |
+-----+----------+-------------+-------------+
|  1.0|       1.0|        good |  Pink Floyd |
+-----+----------+-------------+-------------+

  • 답변 # 1

    글쎄, 매우 유용한 작업은 아니지만 열 메타 데이터를 사용하여 간단한 UDF로 필요한 정보를 추출 할 수 있어야합니다. 귀하의 데이터가 다음과 유사한 파이프 라인을 생성했다고 가정합니다.

    import org.apache.spark.ml.feature.{VectorSlicer, VectorAssembler, StringIndexer}
    import org.apache.spark.ml.Pipeline
    val df = sc.parallelize(Seq(
      (1L, "a", "foo", 1.0), (2L, "b", "bar", 2.0), (3L, "a", "bar", 3.0)
    )).toDF("id", "x1", "x2", "x3")
    val featureCols = Array("x1", "x2", "x3")
    val featureColsIdx = featureCols.map(c => s"${c}_i")
    val indexers = featureCols.map(
      c => new StringIndexer().setInputCol(c).setOutputCol(s"${c}_i")
    )
    val assembler = new VectorAssembler()
      .setInputCols(featureColsIdx)
      .setOutputCol("features")
    val slicer = new VectorSlicer()
      .setInputCol("features")
      .setOutputCol("string_features")
      .setNames(featureColsIdx.init)
    
    val transformed = new Pipeline()
      .setStages(indexers :+ assembler :+ slicer)
      .fit(df)
      .transform(df)
    
    

    먼저 기능에서 원하는 메타 데이터를 추출 할 수 있습니다 :

    val meta = transformed.select($"string_features")
      .schema.fields.head.metadata
      .getMetadata("ml_attr") 
      .getMetadata("attrs")
      .getMetadataArray("nominal")
    
    

    사용하기 쉬운 것으로 변환

    case class NominalMetadataWrapper(idx: Long, name: String, vals: Array[String])
    // In general it could a good idea to make it a broadcast variable
    val lookup = meta.map(m => NominalMetadataWrapper(
      m.getLong("idx"), m.getString("name"), m.getStringArray("vals")
    ))
    
    

    마지막으로 작은 UDF :

    import scala.util.Try
    val transFeatures = udf((v: Vector) => lookup.map{
      m => Try(m.vals(v(m.idx.toInt).toInt)).toOption
    })
    transformed.select(transFeatures($"string_features")).
    
    

  • 이전 windows - Paperclip 40 Rails 3을 사용하여 이미지를 업로드 할 수 없습니다
  • 다음 logging - 델파이 - 어설 션을 사용하지 않고 얻는 방법 (현재 코드 라인, 현재 단위, 현재 기능)?