Как разделить один многократный категориальный столбец на двоичный, как один горячий кодер, с использованием искры scala? - PullRequest
0 голосов
/ 25 февраля 2019

Мои данные таковы:

+---+---------+
| id|cate_list|
+---+---------+
|  0|  a,b,c,d|
|  1|    b,c,d|
|  2|      a,b|
|  3|        a|
|  4|a,b,c,d,e|
|  5|        e|
+---+---------+

То, что я хочу, это примерно так:

-------------------------
| id|cate_list|a|b|c|d|e|
-------------------------
|  0|  a,b,c,d|1|1|1|1|0|
|  1|    b,c,d|0|1|1|1|0|
|  2|      a,b|1|1|0|0|0|
|  3|        a|1|0|0|0|0|
|  4|a,b,c,d,e|1|1|1|1|1|
|  5|        e|0|0|0|0|1|
-------------------------

Я использовал spark ML OneHotEncoder и перепробовал много способов, и наконец я получил это:

+---+---------+-------------+-------------+
| id|cate_list|categoryIndex|  categoryVec|
+---+---------+-------------+-------------+
|  0|        a|          0.0|(4,[0],[1.0])|
|  0|        b|          1.0|(4,[1],[1.0])|
|  0|        c|          2.0|(4,[2],[1.0])|
|  0|        d|          3.0|(4,[3],[1.0])|
|  1|        b|          1.0|(4,[1],[1.0])|
|  1|        c|          2.0|(4,[2],[1.0])|
|  1|        d|          3.0|(4,[3],[1.0])|
|  2|        a|          0.0|(4,[0],[1.0])|
|  2|        b|          1.0|(4,[1],[1.0])|
|  3|        a|          0.0|(4,[0],[1.0])|
|  4|        a|          0.0|(4,[0],[1.0])|
|  4|        b|          1.0|(4,[1],[1.0])|
|  4|        c|          2.0|(4,[2],[1.0])|
|  4|        d|          3.0|(4,[3],[1.0])|
|  4|        e|          4.0|    (4,[],[])|
|  5|        e|          4.0|    (4,[],[])|
+---+---------+-------------+-------------+

Это не то, что мне нужно.Когда я использую python, это действительно просто, и почти двухстрочный код может решить эту проблему.Скала слишком сложная.

Мой код:

val df_split = df.withColumn("cate_list", explode(split($"cate_list", ",")))

val indexer = new StringIndexer()
  .setInputCol("cate_list")
  .setOutputCol("categoryIndex")
  .fit(df_split)
val indexed = indexer.transform(df_split)

val encoder = new OneHotEncoder()
  .setInputCol("categoryIndex")
  .setOutputCol("categoryVec")
val encoded = encoder.transform(indexed)

1 Ответ

0 голосов
/ 25 февраля 2019

Наивный и прямой подход к исходным данным вопроса.

У нас должен быть udf, который вычисляет целевые значения ячеек, ожидая cate_list значение и имя столбца тагет:

val cateListContains = udf((cateList: String, item: String) => if (cateList.contains(item)) 1 else 0)

У нас есть последовательность имен столбцов для извлечения:

val targetColumns = Seq("a", "b", "c", "d", "e")

И давайте foldLeft в источнике DataFrame:

val resultDf = targetColumns.foldLeft(dfSrc) {
  case (df, item) => 
    df.withColumn(item, cateListContains($"cate_list", lit(item)))
}

Он производит точно:

+---+---------+---+---+---+---+---+
|id |cate_list|a  |b  |c  |d  |e  |
+---+---------+---+---+---+---+---+
|0  |a,b,c,d  |1  |1  |1  |1  |0  |
|1  |b,c,d    |0  |1  |1  |1  |0  |
|2  |a,b      |1  |1  |0  |0  |0  |
|3  |a        |1  |0  |0  |0  |0  |
|4  |a,b,c,d,e|1  |1  |1  |1  |1  |
|5  |e        |0  |0  |0  |0  |1  |
+---+---------+---+---+---+---+---+
...