一、StringIndexer
在使用Spark MLlib协同过滤ALS API的时候发现Rating的三个参数:用户id,商品名称,商品打分,前两个都需要是Int值。那么问题来了,当你的用户id,商品名称是String类型的情况下,我们必须寻找一个方法可以将海量String映射为数字类型。好在Spark MLlib可以answer这一切。
StringIndexer 将一列字符串标签编码成一列下标标签,下标范围在[0, 标签数量),顺序是标签的出现频率。所以最经常出现的标签获得的下标就是0。如果输入列是数字的,我们会将其转换成字符串,然后将字符串改为下标。当下游管道组成部分,比如说Estimator 或Transformer 使用将字符串转换成下标的标签时,你必须将组成部分的输入列设置为这个将字符串转换成下标后的列名。很多情况下,你可以使用setInputCol设置输入列。
val spark = SparkSession.builder().appName("db").master("local[*]").getOrCreate() val df = spark.createDataFrame( Seq((0,"a"),(1,"b"),(2,"c"),(3,"a"),(4,"a"),(5,"c")) ).toDF("id","category") val indexer =new StringIndexer() .setInputCol("category") .setOutputCol("categoryIndex") val indexed = indexer.fit(df) // 训练一个StringIndexer => StringIndexerModel .transform(df) // 用 StringIndexerModel transfer 数据集
此外,当你针对一个数据集训练了一个StringIndexer,然后使用其去transform另一个数据集的时候,针对不可见的标签StringIndexer 有两个应对策略:
- throw an exception (which is the default)默认是抛出异常
- skip the row containing the unseen label entirely跳过包含不可见标签的这一行
val df2 = spark.createDataFrame( Seq((0,"a"),(1,"b"),(2,"c"),(3,"d"),(4,"e"),(5,"f")) ).toDF("id","category") val indexed2 = indexer.fit(df)
.setHandleInvalid("skip") // 不匹配就跳过 .transform(df2) // 用 不匹配的stringIndexModel 来 transfer 数据集 indexed2.show()
二、IndexToString
IndexToString 和StringIndexer是对称的,它将一列下标标签映射回一列包含原始字符串的标签。常用的场合是使用StringIndexer生产下标,通过这些下标训练模型,通过IndexToString从预测出的下标列重新获得原始标签。不过,你也可以使用你自己的标签。
val converter =new IndexToString() .setInputCol("categoryIndex") .setOutputCol("originalCategory") val converted = converter.transform(indexed) // class IndexToString extends Transformer converted.select("id","originalCategory") .show()