Spark ML 是基于DataFrame/ Dataset进行机器学习API的开发,随着Spark 2.0的发展,Dataset将成为主流,会逐步取代RDD、DataFrame,当然这个取代只是在Dataset实现已有RDD、DataFrame的API,大家以后就可以用Dataset的API来实现计算逻辑,所以大家不用担心之前学会的RDD、DataFrame没有用处。
博主一般喜欢从源码的角度来看问题和分析问题,也许不太适合你们的口味,所以请大家见谅。
首先看下DataFrame/Dataset的API。
DataFrame/Dataset都是在org.apache.spark.sql,因为都是结构化的数据,所以都是从sql这里来的,具体怎么创建DataFrame和Dataset就不细说了,比较简单的事情。
对于大家平常工作中,重点关注:DataFrame、Dataset、functions这三个的API接口,基本上都能满足大家的工作需要。DataFrame/Dataset的API类似于RDD的,大家可以去看下。对于functions的API大家要重点关注,这里面包括:聚合操作函数、集合操作函数、时间日期函数、数学函数、排序函数、字符串处理函数等等,可支持的函数太多,大家一定要好好细看。
综合上所述,还是写几个实例代码,要不大家就得吐槽我了。
import org.apache.spark.SparkConf
import org.apache.spark.SparkContext
import org.apache.spark.sql.SQLContext
import org.apache.spark.sql.functions._
case classEmployee(id: Int, name: String)
// 创建DataFrame
vallistOfEmployees= List(Employee(1," huangmeiling "), Employee(2," sunbow "), Employee(3," json "))
valempFrame= sqlContext.createDataFrame(listOfEmployees)
empFrame.show()
// 聚合操作
valaa1= empFrame.groupBy().agg(max(empFrame("name")), avg(empFrame("id")))
// 字符串操作
valdf2= empFrame.select(empFrame("id"), trim(empFrame("name")))
val df3= empFrame.select(empFrame("id"), split(empFrame("name"), "o"))
// 其它操作
valbb = empFrame.select(cos(empFrame("id")), empFrame("id"), empFrame("id") + 10, empFrame("name").substr(0, 3))
val cc = empFrame.select(max(empFrame("id")), max(empFrame("name")))
DataFrame/Dataset只是铺垫,因为我觉得和RDD差不多,只是底层不一样,对开发者来说大同小异,所以重点进入feature的讲解。这里拿第一个特征处理的API:Binarizer进行讲解。
源码解析如下:
/** * :: Experimental :: * 二元化,根据指定的阈值对连续特征列生成0\1二元值. */ @Experimental final class Binarizer(override val uid: String) extends Transformer with HasInputCol with HasOutputCol with DefaultParamsWritable { def this() = this(Identifiable.randomUID("binarizer")) /** * 参数阈值. * 如果特性大于该阈值,二元值为1. * 如果特性小于等于该阈值,二元值为0. * 默认值: 0.0 * @group param */ val threshold: DoubleParam = new DoubleParam(this, "threshold", "threshold used to binarize continuous features") /** 取参数阈值 */ def getThreshold: Double = $(threshold) /** 设置参数阈值 */ def setThreshold(value: Double): this.type = set(threshold, value) setDefault(threshold -> 0.0) /** 设置输入列 */ def setInputCol(value: String): this.type = set(inputCol, value) /** 设置输出列 */ def setOutputCol(value: String): this.type = set(outputCol, value) // 计算部分,对输入列的每个元素进行二元值计算 override def transform(dataset: DataFrame): DataFrame = { // DataFrame的结构变化,增加输出列 transformSchema(dataset.schema, logging = true) // 取阈值 val td = $(threshold) // 计算核心部分:二元值的计算过程,这里采用的用户自定义sql函数方法的接口,该函数可以直接对DataFrame的列进行操作。 val binarizer = udf { in: Double => if (in > td) 1.0 else 0.0 } // 输出的结果列 val outputColName = $(outputCol) // DataFrame的元数据更新,增加了结果输出列 val metadata = BinaryAttribute.defaultAttr.withName(outputColName).toMetadata() // 根据指定的输入列,进行函数操作,其中函数操作是自定义的函数 dataset.select(col("*"), binarizer(col($(inputCol))).as(outputColName, metadata)) } // DataFrame的结构变化,增加输出列 override def transformSchema(schema: StructType): StructType = { SchemaUtils.checkColumnType(schema, $(inputCol), DoubleType) val inputFields = schema.fields val outputColName = $(outputCol) require(inputFields.forall(_.name != outputColName), s"Output column $outputColName already exists.") val attr = BinaryAttribute.defaultAttr.withName(outputColName) val outputFields = inputFields :+ attr.toStructField() StructType(outputFields) } override def copy(extra: ParamMap): Binarizer = defaultCopy(extra) } @Since("1.6.0") object Binarizer extends DefaultParamsReadable[Binarizer] { @Since("1.6.0") override def load(path: String): Binarizer = super.load(path) }
其中重点是transform,该方法就是主要实现特征处理的逻辑计算,该方法中最主要部分就是用户自定义函数:
valbinarizer = udf { in: Double => if (in > td) 1.0else0.0}
该自定义函数是我们特征的逻辑处理过程的实现。
其中udf就是UserDefinedFunction,说个例子:
我自定义了一个函数:
val predict = udf((score: Double) => if(score > 0.5) true else false)
该函数可以直接应用在DataFrame对列进行处理:
df.select(predict(df("score")) )
所以综上所述,如果我们需要自己写一个ML 特征处理API接口或者需要修改源码什么的,重点部分在transform方法上的实现,以及需要自已定义一个sql用户自定义函数udf。
ML 的feature特征处理API有好多:
大家自己一个个去看吧。
今天就讲解个Demo,希望对大家有所帮助。
等有时间,我把DataFrame、Dataset、feature所有API接口整理一个说明文档吧。哎,每天太多事,忙不过来。
转载请注明出处:
http://blog.csdn.net/sunbow0