特征变换-标签和索引的转化

标签和索引的转化

介绍:
    在机器学习处理过程中,为了方便相关算法的实现,经常需要把标签数据(一般是字符串)转化成整数索引,
    或是在计算结束后将整数索引还原为相应的标签.
Spark ML 包中提供了几个相关的转换器:
    StringIndexer,IndexToString,OneHotEncoder,VectorIndexer,他们提供了十分方便的特征转换功能,
    这些转换器都位于org.apache.spark.ml.feature包下
注意:
    值得注意的是,用于特征转换的转换器和其他的机器学习算法一样,也属于Ml Pipeline模型的一部分,可以用来构成机器学习流水线,
eg:-->以StringIndexer为例
    其存储着进行标签数值化过程的相关超参数,是一个Estimator,对其调用fit(..)方法既可以生成相应的模型StringIndexrModel类,
    很显然,它存储了用于DataFrame进行相关处理的参数,是一个Transformer(其他转换器同理)

常用转换器介绍

StringIndexer
概念:
    StringIndexer转换器可以把一列特别型的特征(或标签)进行编码,使其数值化,索引的范围从0开始,该过程可以使得相应的特征索引化,
使得某些无法接受类别型特征的算法可以使用,并提高诸如决策树等机器学习算法的效率.
流程:
    索引构建的顺序为标签的频率,优先编码频率较大的标签,所以出现频率最高的标签为0号
    如果输入的是数值类型的,我们会吧它转化成字符型,然后再对其进行编码.
代码实现:
    import org.apache.spark.ml.feature.StringIndexer
    import org.apache.spark.sql.SparkSession
    
    object TranIndexDemo {
      def main(args: Array[String]): Unit = {
        //--首先创建一个简单的DataFrame,它只包含一个Id列和一个标签列(category)
        //--构建SparkSession对象
        val spark = SparkSession.builder().
          master("local").
          appName("TF-IDFDemo").
          getOrCreate()
        val df1 = spark.createDataFrame(Seq(
          (0, "a"), (1, "b"), (2, "c"), (3, "a"), (4, "a"),
          (5, "c"))).toDF("id", "category")
        //--2. 随后,我们创建一个StringIndexer对象,设定输入输出列名,其余参数用默认值,
        //并对这个DataFrame进行训练,产生StringIndexerModel对象
        val indexer = new StringIndexer()
          .setInputCol("category")
          .setOutputCol("categoryIndex")
        //--并带入数据生成模型
        val model = indexer.fit(df1)
        //--随后即可利用该对象对DataFrame进行转换操作,可以看到StringIndexerModel依次按照出现频率的高低,
        //把字符标签进行了排序,即出现最多的'a'被编号成0,"c"为1,出现最少的'b'为2
        val indexed1 = model.transform(df1)
        //--考虑这样一种情况,我们使用已有的数据构建了一个StringIndexerModel,然后再构建一个新的DataFrame,
        //这个DataFrame中有着模型内未曾出现的标签'd',用已有的模型去转换这一DataFrame会有什么效果
        val df2 = spark.createDataFrame(Seq(
          (0, "a"), (1, "b"), (2, "c"), (3, "a"), (4, "a"),
          (5, "d"))).toDF("id", "category")
        val indexed = model.transform(df2)
        //--如果直接转换的话,Spark会抛出异常,报出"Unseen label: d"的错误
        //--为了处理这种情况,在模型训练后,可以通过设置
        //--setHandleInvalid("skip")来忽略掉那些未出现的标签,这样,带有未出现标签行将直接被过滤掉,
        val indexed2 = model.setHandleInvalid(("skip")).transform(df2)
        indexed2.show()
      }
    }
IndexToString
概念:
    与StringIndexer相对应,IndexToString的作用是吧标签索引的一列重新映射回原有的字符型标签.
    其主要使用场景一版都是和StringIndexer配合,先用StringIndexer将标签转化成标签索引,进行模型训练,然后在预测标签的时候再把
    标签索引转化成原有的字符标签.当然你也可以另外定义其他的标签
案例:
    首先,和StringIndexer的实验相同,我们用StringIndexer读取数据集中的"category"列,把字符型标签转化成标签索引,然后输出到"categoryIndex"列上
构建出新的DataFrame
代码实现:
    import org.apache.spark.ml.feature.{IndexToString, StringIndexer}
    import org.apache.spark.sql.SparkSession
    object Index2StringDemo {
      def main(args: Array[String]): Unit = {
        //--首先创建一个简单的DataFrame,它只包含一个Id列和一个标签列(category)
        //--构建SparkSession对象
        val spark = SparkSession.builder().
          master("local").
          appName("TF-IDFDemo").
          getOrCreate()
        val df1 = spark.createDataFrame(Seq(
          (0, "a"), (1, "b"), (2, "c"), (3, "a"), (4, "a"),
          (5, "c"))).toDF("id", "category")
        //--1. 首先创建StringIndexer模型,将数值特征转换为索引
        val model = new StringIndexer()
          .setInputCol("category")
          .setOutputCol("categoryIndex")
          .fit(df1)
        val indexed = model.transform(df1)
        //--2. 随后,创建IndexToString对象,读取"categoryIndex"上的标签索引,获得原有数据集的字符型标签,
        //--然后再输出到"originalCategory"列上.最后,通过输出"originalCategory"列,可以看到数据集中原有的字符标签.
        val converter = new IndexToString()
          .setInputCol("categoryIndex")
          .setOutputCol("originalCategory")
         val converted = converter.transform(indexed)
        //可以看到结果和做转换之前的字符一致
        converted.select("id","originalCategory").show()
      }
    }
OneHotEncoder
概念:
    独热编码(One-Hot Encoding) 是指把一列类别性特征(或称名词性特征,nominal/categorical features) 映射称一系列的二元连续特征的过程,原有的类别性特征有
几种可能取值,这一特征就会被映射称几个二元连续特征,每一个特征代表一种取值,若该样本表现出该特征,则取1,否则取0
One-Hot编码适合一些期望类别特征为连续特征的算法,比如说逻辑斯蒂回归
编码实现:
    首先创建一个DataFrame,其包含一列类别性特征,需要注意的是,在使用OneHotEncoder进行转换前,DataFrame需要先使用StringIndexer将原始标签数值化:
代码实现:
    package PipeLineDemo
    import org.apache.spark.ml.feature.{OneHotEncoder, StringIndexer}
    import org.apache.spark.sql.SparkSession
    
    object OneHotEDemo {
      def main(args: Array[String]): Unit = {
        //--首先创建一个简单的DataFrame,它只包含一个Id列和一个标签列(category)
        //--构建SparkSession对象
        val spark = SparkSession.builder().
          master("local").
          appName("TF-IDFDemo").
          getOrCreate()
        val df = spark.createDataFrame(Seq(
         (0, "a"),
         (1, "b"),
         (2, "c"),
         (3, "a"),
         (4, "a"),
         (5, "c"),
         (6, "d"),
         (7, "d"),
         (8, "d"),
         (9, "d"),
         (10, "e"),
         (11, "e"),
         (12, "e"),
         (13, "e"),
         (14, "e"))).toDF("id","category")
    
        //--1. 我们需要使用StringIndexer对DataFrame进行转换,将原始标签进行数值化
        //--使用原始数据训练StringIndexed模型
        val s2IndModel = new StringIndexer()
          .setInputCol("category")
          .setOutputCol("categoryIndex").fit(df)
        //--使用模型进行转换
        val indexed = s2IndModel.transform(df)
        //--2. 随后,我们创建OneHotEncoder对象对处理后的DataFrame进行编码,可以看见,编码后的二进制特征
        //呈稀疏向量形式,与StringIndexer编码的顺序相同,需注意的是最后一个Category("b")被编码为全0向量,
        //若希望"b"也占有一个二进制特征,则可以在创建OneHotEncoder时指定---> setDropLast(false)
        val encoder = new OneHotEncoder()
          .setInputCol("categoryIndex")
          .setOutputCol("categoryVec")
        //--3.使用OneHotEncoder 对数据进行转换
        val encoded = encoder.transform(indexed)
        encoded.show()
      }
    }
VectorIndexer
概念:
    之前介绍的StringIndexer是针对单个类别型特征进行转换,倘若所有特征都已经被组织在一个向量中,又想对其中某些单个分量进行处理时,
Spark ML提供了VectorIndexer类来解决向量数据集中的类别性特征转换。
    通过为其提供maxCategories超参数,它可以自动识别哪些特征是类别型的,并且将原始值转换为类别索引。
    它基于不同特征值的数量来识别哪些特征需要被类别化,
    那些取值可能性最多不超过maxCategories的特征需要会被认为是类别型的。
案例解析:
    在下面的例子中,我们读入一个数据集,然后使用VectorIndexer训练出模型,
    来决定哪些特征需要被作为类别特征,将类别特征转换为索引,这里设置maxCategories为2
    ,即只有种类小于2的特征才被认为是类别型特征,否则被认为是连续型特征
代码实现:
    package PipeLineDemo
    import org.apache.spark.ml.feature.VectorIndexer
    import org.apache.spark.ml.linalg.Vectors
    import org.apache.spark.sql.SparkSession
    
    object VectorIndeDemo {
      def main(args: Array[String]): Unit = {
        //--首先创建一个简单的DataFrame,它只包含一个Id列和一个标签列(category)
        //--构建SparkSession对象
        val spark = SparkSession.builder().
          master("local").
          appName("TF-IDFDemo").
          getOrCreate()
        val data = Seq(Vectors.dense(-1.0, 1.0, 1.0)
          , Vectors.dense(-1.0, 3.0, 1.0), Vectors.dense(0.0, 5.0, 1.0))
        val df = spark.createDataFrame(data.map(Tuple1.apply)).toDF("features")
        val indexer = new VectorIndexer().setInputCol("features").setOutputCol("indexed").setMaxCategories(2)
        val indexerModel = indexer.fit(df)
        //可以通过VectorIndexerModel的categoryMaps成员来获得被转换的特征及其映射,这里可以看到共有两个特征被转换,分别是0号和2号。
        val categoricalFeatures: Set[Int] = indexerModel.categoryMaps.keys.toSet
        println(s"Chose ${categoricalFeatures.size} categorical features: " + categoricalFeatures.mkString(", "))
        val indexed = indexerModel.transform(df)
        indexed.show()
    
      }
    
    }
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • 搜索引擎优化(Search engine optimization,简称SEO),指为了提升网页在搜索引擎自然搜索...
    翻滚吧海阔天空阅读 9,496评论 5 14
  • 索引是应用程序设计和开发的一个重要方面。 若索引太多, 应用程序的性能可能会受到影响。 而索引太少, 对查询性能又...
    好好学习Sun阅读 4,695评论 0 4
  • —— Kurny 人海茫茫 只一眼 你便将我认出 人山人海 你将我紧紧拥住 我们便不会走散 万水千山...
    Kurny91阅读 1,427评论 2 2
  • 昨日,浏览微博的时候看到了一个关注的博主,是好友推荐的一位学霸男生,名叫邱汐岩。真正了解他也正是从昨天刷了他的微博...
    jianshubala123阅读 1,427评论 0 0
  • 第一次反围剿 革命武装初建成, 惹得蒋军来攻占。 八百里河山军线, 可是捉到张辉瓒。 工农红军初场名, 围剿却用张...
    王晷阅读 5,296评论 2 8

友情链接更多精彩内容