Sparkml庫標籤和索引之間轉化
StringIndexer
StringIndexer將一串字串標籤編碼為一列標籤索引。這些索引範圍是[0, numLabels)按照標籤頻率排序,因此最頻繁的標籤獲得索引0。如果使用者選擇保留它們,那麼看不見的標籤將被放在索引numLabels處。如果輸入列是數字,我們將其轉換為字串值並將為其建索引。當下遊管道元件(例如Estimator或 Transformer使用此字串索引標籤)時,必須將元件的輸入列設定為此字串索引列名稱。在許多情況下,您可以使用設定輸入列setInputCol。
例1, 假如我們有下面的DataFrame,帶有id和category列:
Id | category |
0 | a |
1 | b |
2 | c |
3 | a |
4 | a |
5 | c |
對著個Dataframe使用StringIndexer,輸入列式category,categoryIndex作為輸出列,得到如下值:
Id | Category | CategoryIndex |
0 | a | 0.0 |
1 | b | 2.0 |
2 | c | 1.0 |
3 | a | 0.0 |
4 | a | 0.0 |
5 | c | 1.0 |
字元a,索引值是0,原因是a出現的頻率最高,接著就是c:1,b:2。
另外,對於不可見的標籤,StringIndexer有是三種處理策略:
1, 丟擲異常,這是預設行為
2, 跳過不可見的標籤
3, 把不可見的標籤,標記為numLabels(這個是無用的)。
還用上面的例子,資料如下:
Id | Category |
0 | a |
1 | b |
2 | c |
3 | a |
4 | a |
5 | c |
6 | d |
7 | e |
如果你沒有設定StringIndexer如何處理這些不可見的詞,或者設定為了error,他將會丟擲一個異常。然而,你如果設定setHandleInvalid("skip"),將會得到如下結果:
Id | Category | CategoryIndex |
0 | a | 0.0 |
1 | b | 2.0 |
2 | c | 1.0 |
注意,包含d,e的行並沒有出現。
如果,呼叫setHandleInvalid("keep"),會得到下面的結果:
Id | Category | CategoryIndex |
0 | a | 0.0 |
1 | b | 2.0 |
2 | c | 1.0 |
3 | d | 3.0 |
4 | e | 3.0 |
注意,d,e獲得的索引值是3.0
程式碼用例如下:
importorg.apache.spark.ml.feature.StringIndexer
val df = spark.createDataFrame(
Seq((0,"a"),(1,"b"),(2,"c"),(3,"a"),(4,"a"),(5,"c"))
).toDF("id","category"
val indexer =newStringIndexer()
.setInputCol("category")
.setOutputCol("categoryIndex")
val indexed = indexer.fit(df).transform(df)
indexed.show()
IndexToString
對稱地StringIndexer,IndexToString將一列標籤索引映射回包含作為字串的原始標籤的列。一個常見的用例是從標籤生成索引StringIndexer,用這些索引對模型進行訓練,並從預測索引列中檢索原始標籤IndexToString。但是,您可以自由提供自己的標籤。
例如,假如我們有dataframe格式如下:
Id | CategoryIndex |
0 | 0.0 |
1 | 2.0 |
2 | 1.0 |
3 | 0.0 |
4 | 0.0 |
5 | 1.0 |
使用IndexToString 並且使用categoryIndex作為輸入列,originalCategory作為輸出列,可以檢索到原始標籤如下:
Id | originalCategory | CategoryIndex |
0 | a | 0.0 |
1 | b | 2.0 |
2 | c | 1.0 |
3 | a | 0.0 |
4 | a | 0.0 |
5 | c | 1.0 |
程式碼案例如下:
importorg.apache.spark.ml.attribute.Attribute
importorg.apache.spark.ml.feature.{IndexToString,StringIndexer}
valdf=spark.createDataFrame(Seq(
(0,"a"),
(1,"b"),
(2,"c"),
(3,"a"),
(4,"a"),
(5,"c")
)).toDF("id","category")
valindexer=newStringIndexer()
.setInputCol("category")
.setOutputCol("categoryIndex")
.fit(df)
valindexed=indexer.transform(df)
println(s"Transformed string column '${indexer.getInputCol}' "+
s"to indexed column '${indexer.getOutputCol}'")
indexed.show()
valinputColSchema=indexed.schema(indexer.getOutputCol)
println(s"StringIndexer will store labels in output column metadata: "+
s"${Attribute.fromStructField(inputColSchema).toString} ")
valconverter=newIndexToString()
.setInputCol("categoryIndex")
.setOutputCol("originalCategory")
valconverted=converter.transform(indexed)
println(s"Transformed indexed column '${converter.getInputCol}' back to original string "+
s"column '${converter.getOutputCol}' using labels in metadata")
converted.select("id","categoryIndex","originalCategory").show()
推薦閱讀:
本文主要參考翻譯整理自Spark官網,打原創標籤純屬為了保證,翻譯勞動成果,謝謝大家諒解。
kafka,hbase,spark,Flink等入門到深入源碼,spark機器學習,大資料安全,大資料運維,請關注浪尖公眾號,看高質量文章。
更多文章,敬請期待