Spark and Scala
- topbykey:
Returns the top k (largest) elements for each key from this RDD as defined by the specified implicit Ordering[T]. If the number of elements for a certain key is less than k, all of them will be returned. - spark的调试问题
spark运行过程中的数据总是以RDD的方式存储,使用Logger等日志模块时,对RDD内数据无法识别,应先使用行为操作转化为scala数据结构然后输出。 scala Map 排序
对于scala Map数据的排序,使用 scala.collection.immutable.ListMap 和 sortWiht(sortBy),具体用法如下val sortedScore = ListMap(outScore.toSeq.sortWith(_._2._5>_._2._5):_*)
即, 使用outScore value的第5个值按降序排列
spark Logger 的输出
spark rdd数据为惰性数据,在map的过程中无法打印中间数据,需要通过行为操作将其转化为scala数据格式,然后遍历打印。spark rdd 转换为map
contenttypeHit是一个spark pair RDD,需要转化为一个Map,maxHit = contenttypeHit.collect()
执行后获取的是一个Array,不能通过key值获取value,因此先定义一个可变映射val score = new scala.collection.mutable.HashMap[Int, Long]
,然后对maxHit执行map操作更新score的值,maxHit.map(x => if(x._2==0) score(x._1)=1L else score(x._1)=x._2)
,缺点是maxHit得到的是一个unit值。 这是一个蠢方法,正确的方法是val maxHit = contenttypeHit.map(x=>(x._1, x._2)).collectAsMap()
转换操作中不要掺杂行为操作
正则匹配貌似在转化操作中也无法正常执行,会抛出空指针的异常,暂时还未确定 更正:正则表达式可以在转化操作中进行,空指针是因为读数据返回的空值不是“”而是Null- scala 字符串 split 对于字符串s =”11010002|11010003|11019999”,如果想按照竖线分割,需要val lst = s.split(“//|”),如果直接s.split(“|”),会分割成 Array(1, 1, 0, 1, 0, 0, 0, 2, |, 1, 1, 0, 1, 0, 0, 0, 3, |, 1, 1, 0, 1, 9, 9, 9, 9)
spark flatmap
val subtype_pid_score = sub_type.filter(v => v._4.isInstanceOf[String]) .flatMap(vv => vv._4.split("|") .filter(vvf => vvf.length==8) .map(vvv => (vvv.toLong,(vv._1, pid_score.getOrElse(vv._1, 0.0)))) )
flatMap操作后产生了[[“12112”, “343434”], [“8787”, “77987”]]并展开成[“12112”, “343434”,”8787”, “77987”, vvv] 然后进行map操作是对所有展开后的元素,并且可对应到vv。
HiveContext 读取 JDBC
Jdbc的读取分为两种方法,一种是使用sqlContext,一种是使用HiveContext,前种方法返回的是一个rdd,后种方法返回的是dataFrame
JDBC的sqlContext读取
def getNewsDataRankInfo(sqlContext: SQLContext) = { sqlContext.read.jdbc(JDBCUrl, "t_news_info", Prop).registerTempTable("t_news_info") sqlContext.sql("select f_news_id as f_pid, UNIX_TIMESTAMP(f_release_time) as f_time, f_sub_type from t_news_info where f_status = 65002") }
sqlContext.read.jdbc相当于将JDBC的配置读取到sqlContext中,然后使用sqlContext类提供了一个sql方法,使用sql语句select需要的数据。返回值是一个DataFrame类实例。
JDBC的hiveContext读取
val hiveCtx = new HiveContext(sc) val url = "jdbc:mysql://192.168.36.94/homed_spider" val tableName = "t_spider_program_final" import java.util.Properties val connProperties = new java.util.Properties connProperties.setProperty("user", "root") connProperties.setProperty("password", "123456") val jdbcDF = hiveContext.read.jdbc(url, tableName, connProperties)
返回的值jdbcDF即整个表,类型为DataFrame
Map的更新
scala中映射分为两种
- 可变映射 import scala.collection.mutable.HashMap
- 不变映射 val score = Map((“a”,1))
对于可变映射,值的更新可以直接通过key赋值,对于不变映射你无法更新它的值,但是你 可以得到一个新的
val newScore = score + ("v"->12, "a"->13)
新赋的值将覆盖原来的值,如果想通过Map来更新Map,使用++运算符
val nScore = score ++ newScore
groupBy
对于rdd使用groupBy,可以生成以指定项为key的Map
val exp =Array((都市,0.3), (爱情,0.3), (纪录,0.28),(都市, 0.4))
val expMap =exp.groupBy(x=>x._1).map(v=>(v._1, v._2.map(x=>x._2)))
生成的数据格式为 Map(都市->(0.3,0,4),爱情->(0.3),记录->(0.28))
sample
sample对RDD采样,以及是否替换,调用方法为
rdd.sample(false,0.5)
- true 表示有放回的取样,即该次抽样所得样本有重复
- false 表示无放回的取样,抽样无重复
scala 时间戳与时间互转
依赖包:
import java.util.Date import java.text.SimpleDateFormat
1.时间戳转时间字符串
def tmiestampTostr(ts :Long) = { val SimpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss") SimpleDateFormat.format(new Date(ts*1000L)) }
2.时间转时间戳
def getTimestamp(t: String):Long = { val date1 = """(\d\d\d\d)(\d\d)(\d\d)""".r val date2 = """(\d\d\d\d)-(\d\d)-(\d\d).*""".r val fm1 = new SimpleDateFormat("yyyyMMdd") val fm2 = new SimpleDateFormat("y-M-d") t match { case date1(y, m, d) => fm1.parse(t).getTime / 1000 case date2(y, m, d) => fm2.parse(t).getTime / 1000 case _ => { //Logger.info(s"can't parse time from this string:$t") //curTime.set(1970, 0, 0) 0L } } }
scala 函数柯里化
scala> def sum(x:Int)(y:Int)=x+y sum: (x: Int)(y: Int)Int scala> val second = sum(1)_ second: Int => Int = <function1> scala> second(2) res1: Int = 3
柯里化函数执行时,分解为两个函数执行,步骤与下面的方法调用过程类似
scala> def first(x:Int) = (y:Int)=>x+y first: (x: Int)Int => Int scala> val second = first(1) second: Int => Int = <function1> scala> second(2) res4: Int = 3
curry化最大的意义在于把多个参数的function等价转化成多个单参数function的级联,这样所有的函数就都统一了,方便做lambda演算。 在scala里,curry化对类型推演也有帮助,scala的类型推演是局部的,在同一个参数列表中后面的参数不能借助前面的参数类型进行推演,curry化以后,放在两个参数列表里,后面一个参数列表里的参数可以借助前面一个参数列表里的参数类型进行推演。这就是为什么 foldLeft这种函数的定义都是curry的形式
Top操作取前几项
top方法返回一个由原RDD中前N大的元素构成的RDD,并且可以指定由哪个数据排序
val