使用Spark完成基于TF-IDF特征的新闻热点聚类

来源:互联网 发布:轩辕剑 还原数据库 编辑:程序博客网 时间:2024/06/05 18:20

写在前面

互联网各个地方时时刻刻都在发生着这样或者那样的事件,如果使用人工去观察根本无法及时的知晓哪地方发生了什么热点事件,做为主流媒体更希望第一时间得知热点事件的发生,好及时的紧靠热点。舆情监控的目的就是及时的得知互联网上发生的热点事件,舆情监控也是媒体大数据的一项重要工作。本文实现一个简单的新闻件事聚类。后期可以从各大主流媒体网站爬取数据,每隔一段时间聚类一次,如果一类报道中文章数据比较多,增加速度比较快,那么这个事件可能就是热点事件。

数据准备

原始数据如下图所示,文件格式为:

ID====title====content

这里写图片描述
需要输出的结果如下:

事件---------------------龙排龙舟迎端午 浙江山水好热闹2152208421524756 21524756 不一样的民俗!嘉兴端午踏白船荡漾浓浓水乡情21524710 21524710 贵州碧江:中外龙舟端午节“九九争一”赛龙舟21524842 21524842 碧水龙舟渡 逐浪竞风流21522988 21522988 端午西溪龙船鼓 百舸竞渡劈波来21522084 21522084 余杭:“龙腾西溪·荷韵洪园” 等你来耍21524016 21524016 “我们的节日——端午看云龙” 大型民俗活动举行21525288 21525288 [南湖] 水乡踏白船21524722 21524722 杭州西溪“龙舟盛会”展非遗魅力 11大名校龙舟竞渡21525232 21525232 香港端午节里看民俗:“放纸龙”、龙舟竞渡、游龙舟水21524736 21524736 2017杭州西溪龙舟文化节:龙舟竞渡传统胜会 精彩纷呈活力端午21524830 21524830 西溪龙舟赛添洋味 骆家庄龙船宴寄乡愁21524786 21524786 龙舟赛龙船宴 杭州骆家庄改造后的首个端午太壮观21524758 21524758 今天端午节,ta刷屏了西湖人的朋友圈!21524302 21524302 2017年中国·嘉兴端午民俗文化节开幕21524570 21524570 龙排龙舟迎端午 浙江山水好热闹21522736 21522736 西溪国际龙舟赛十强战队出炉 世界赛艇冠军强势加盟21525673 21525673 300桌!昨天骆家庄摆了一场超大规模的端午宴21525277 21525277 300桌!杭州骆家庄昨天摆了超大规模的端午宴21524557 21524557 龙排龙舟迎端午 浙江山水好热闹21524507 21524507 2017年中国·嘉兴端午民俗文化节开幕21524845 21524845 水乡踏白船 山溪放龙排21524995 21524995 水乡踏白船!浙江日报关注嘉兴端午踏白船表演赛21525467 21525467 赛龙舟、吃粽子,外国友人也爱过中国传统佳节21524959 21524959 浓浓文化味 处处庆端阳21524031 21524031 龙腾西溪·荷韵洪园:杭州西溪龙舟文化节即将开场21525463 21525463 赛艇世界杯冠军强势助阵 观众选手嗨翻天21522963 21522963 端午西溪龙船鼓百舸竞渡劈波来21523201 21523201 2017杭州西溪龙舟文化节于528日-30日举行21523461 21523461 龙舟竞渡争上游 鄞州云龙举行特色民俗活动庆端午21525131 21525131 中华大赛为全运预演21525461 21525461 遂昌千年“龙排”盛景 民俗独特事件---------------------温州市开展社区反邪教宣传活动2152035121526682 21526682 杭州市下城区文晖街道创建反邪教警示教育阵地群21526686 21526686 温州泰顺县反邪教宣传进校园21526198 21526198 温岭市开展反邪教宣传教育活动21521420 21521420 宁波市海曙区举办知识竞赛21520366 21520366 警惕全能神的黑色暴力(图)21521410 21521410 温州借文艺巡演开展反邪宣传21526196 21526196 湖州市吴兴区开展平安文化广场反邪宣传21525360 21525360 绍兴市南明街道举办反邪教现场书画比赛21525366 21525366 江山市举办科普反邪文艺演出21526166 21526166 金华市举办“崇尚科学,反对邪教”主题漫画展21525358 21525358 丽水市利用首个科技工作者日开展反邪宣传21522462 21522462 温州永嘉县召开村居反邪教工作业务培训会议21526192 21526192 嘉兴市召开“宗教反邪”推进会21521402 21521402 绍兴市城南社区安装显示屏 反邪宣传开辟新渠道21526172 21526172 舟山反邪宣传礼献六一21520352 21520352 绍兴市南明街道反邪知识墙绘扮靓社区21520351 21520351 丽水市缙云县借助科技活动周全面开展反邪宣传活动21525357 21525357 温州市反邪宣传进校园21521425 21521425 衢州柯城区开展反邪教党员日21525369 21525369 杭州滨江在端午节传递反邪知识21525359 21525359 嘉兴建设街道开展反邪宣传21525349 21525349 平湖市1个镇和3个村社区获批全国无邪教创建示范单位21522463 21522463 台州临海开展广场反邪宣传21520359 21520359 舟山群岛科技周开展反邪宣传21529645 21529645 黄岩:红叶不负好韶华  愿做春泥更护花21529511 21529511 瑞安市开展平安反邪宣传活动21522461 21522461 温州市开展社区反邪教宣传活动21521415 21521415 兰溪市反邪教漫画进车亭21529643 21529643 黄岩:红叶不负好韶华  愿做春泥更护花21520361 21520361 江山市开展反邪教进文化礼堂互学互促活动事件---------------------昨天杭州气温34.9℃ 创入夏以来新高2152207821524772 21524772 昨天杭州气温34.9℃ 创入夏以来新高21527308 21527308 双休日天气不错,高考前有雨21526448 21526448 感觉有点闷?距离入梅还早!双休日杭州的天会放晴21524970 21524970 昨天杭州气温34.9℃ 创入夏以来新高21528814 21528814 芒种时节雨量充沛,昨天的大雨很应景,不过——高考期间全省多云为主,比较闷热21524808 21524808 这个六一儿童节,要湿哒哒地度过了21529122 21529122 本周将迎来2017年高考 其间天气晴好午间较热 考生要注意防暑降温21527516 21527516 好天气明起暂别 宁波周一起将转为阵雨或雷雨的天气21524628 21524628 今天端午节杭州最高气温33℃ 雨就要来了!21524832 21524832 这个六一儿童节,要湿哒哒地度过了21522078 21522078 端午小长假热三天 气温30摄氏度朝上21527924 21527924 本周将迎来2017年高考 其间天气晴好午间较热21525682 21525682 六月天 孩子脸 你准备好迎接这个下雨的儿童节了吗?21524740 21524740 今天端午 你有没有吃“五黄六白” 家里挂菖蒲插艾叶了吗21523028 21523028 宁波端午节期间晴好宜出游 最高气温在30℃上下21528046 21528046 高考期间有点热 考生需做好防暑21527213 21527213 双休日天气不错,高考前有雨相伴21526449 21526449 感觉有点闷?距离入梅还早 好在这个双休日,天晴啦21523533 21523533 端午小长假 天气“心情好”21527803 21527803 凉爽将至 六月雨水要变多21524775 21524775 昨天杭州气温34.9℃ 创入夏以来新高21524815 21524815 六月份杭州只有5天不下雨?这是真的吗21522995 21522995 宁波端午晴好宜出游 最高气温在30℃上下21527331 21527331 双休日天气不错,高考前有雨21527227 21527227 双休日杭州的天气不错 高考前有雨21527817 21527817 凉爽将至 六月雨水要变多21526515 21526515 六月是杭州降水集中期 不过距离入梅还早21524787 21524787 朋友圈热传"6月份宁波只晴6天" 气象台回应21526421 21526421 感觉有点闷?距离入梅还早 好在这个双休日,天晴啦事件---------------------西湖区计生协举办“5.29会员活动日”2152113021525978 21525978 宁波市计生协召开六届二次理事会21525994 21525994 桐乡市召开计生家庭安康保险工作会议21525992 21525992 宁波市计生协改革理事会领导机构增设基层一线兼职副会长21521130 21521130 关爱新市民 真情暖万家 提升流动人口获得感和幸福感21523288 21523288 西湖区举办“5.29会员活动日”暨流动人口健康服务年启动仪式21526000 21526000 长兴县计生协开展“最美协会人”投票评选活动21525996 21525996 文成县计生协联合部门共同举办亲子运动会21525976 21525976 富阳区全面部署基层计生协换届选举工作21525020 21525020 南湖区举办“5.29”大型宣传服务活动21525018 21525018 南湖:“5.29”生育关怀温暖计生困难家庭21525988 21525988 嘉兴:关爱计生家庭 重视保险保障21522720 21522720 5.29宣传服务 西溪街道活动丰富接地气21523662 21523662 浙江省计生协启动“5.29会员活动日”暨流动人口健康服务年活动21524998 21524998 柯城区计生协举办文艺晚会纪念第19个“5.29”会员活动日21525008 21525008 西湖区计生协举办“5.29会员活动日”21525981 21525981 西湖区计生协会长卢华英走访慰问特殊家庭21525013 21525013 乐清市计生协创新“5.29”宣传服务月活动21525003 21525003 龙泉市计生协积极开展“5.29会员活动日”宣传服务活动21525025 21525025 桐乡市举办“5·29”计生宣传专场文艺演出21525009 21525009 洞头区举办“5.29”计生协会纪念日主题活动启动仪式21525031 21525031 镇海区举行“5.29会员活动日”主题宣传服务活动21525007 21525007 温州市:三级联动 共庆“5.29”会员活动日21523651 21523651 金禹社区开展计生宣传活动21524997 21524997 金华市举办“生育关怀 健康相伴”5.29大型宣传活动21525983 21525983 洞头区计生协开展计生协实务工作培训21525027 21525027 萧山区计生协举办5.29会员活动日宣传活动21525993 21525993 三门县计生协召开计生家庭保险工作会议21526001 21526001 镇海区成立计生特殊家庭健康关爱中心

完整代码

package com.zhaochaoimport org.ansj.splitWord.analysis.BaseAnalysisimport org.apache.spark.graphx.Graphimport org.apache.spark.mllib.feature.{HashingTF, IDF}import org.apache.spark.{SparkConf, SparkContext}import org.apache.spark.mllib.linalg.{SparseVector => SV}object groupNews {  def main(args: Array[String]): Unit = {    //相似度阈值    val sim = 0.5    val sparkconfig = new SparkConf().setAppName("DocSim").setMaster("local[2]")    val ctx = new SparkContext(sparkconfig)    val path = "E:\\te.txt"    //加载过滤数据(id,title,content)    val input = ctx.textFile(path).map(x => x.split("====").toSeq).filter(x => x.length == 3 && x(1).length > 1 && x(2).length > 1)    //分词(id,title,[words])    val splitWord = input.map(x => (x(0), x(1), BaseAnalysis.parse(x(2)).toStringWithOutNature(" ").split(" ").toSeq))    //聚类初始化 计算文章向量(id,(id,content,title))    val init_rdd = input.map(a => {      (a(0).toLong, a)    })    init_rdd.cache()    //计算TF-IDF特征值    val hashingTF = new HashingTF(Math.pow(2, 18).toInt)    //计算TF    val newSTF = splitWord.map(x => (x._1, hashingTF.transform(x._3))    )    newSTF.cache()    //构建idf model    val idf = new IDF().fit(newSTF.values)    //将tf向量转换成tf-idf向量    val newsIDF = newSTF.mapValues(v => idf.transform(v)).map(a => (a._1, a._2.toSparse))    newsIDF.take(10).foreach(x => println(x))    //构建hashmap索引 ,特征排序取前10个    val indexArray_pairs = newsIDF.map(a => {      val indices = a._2.indices      val values = a._2.values      val result = indices.zip(values).sortBy(-_._2).take(10).map(_._1)      (a._1, result)    })    //(id,[特征ID])    indexArray_pairs.cache()    indexArray_pairs.take(10).foreach(x => println(x._1 + " " + x._2.toSeq))    //倒排序索引 (词ID,[文章ID])    val index_idf_pairs = indexArray_pairs.flatMap(a => a._2.map(x => (x, a._1))).groupByKey()    index_idf_pairs.take(10).foreach(x => println(x._1 + " " + x._2.toSeq))    //倒排序    val b_content = index_idf_pairs.collect.toMap    //广播全局变量    val b_index_idf_pairs = ctx.broadcast(b_content)    //广播TF-IDF特征    val b_idf_parirs = ctx.broadcast(newsIDF.collect.toMap)    //相似度计算 indexArray_pairs(id,[特征ID]) b_index_idf_pairs( 124971 CompactBuffer(21520885, 21520803, 21521903, 21521361, 21524603))    val docSims = indexArray_pairs.flatMap(a => {      //将包含特征的所有文章ID      var ids: List[Long] = List()      //存放文章对应的特征      var idfs: List[(Long, SV)] = List()      //遍历特征,通过倒排序索引取包含特征的所有文章,除去自身      a._2.foreach(b => {        ids = ids ++ b_index_idf_pairs.value.get(b).get.filter(x => (!x.equals(a._1))).map(x => x.toLong).toList      })      //b_idf_parirs(tf-idf特征),遍边文章,获取对应的TF-IDF特征      ids.foreach(b => {        idfs = idfs ++ List((b, b_idf_parirs.value.get(b.toString).get))      })      //获取当前文章TF-IDF特征      val sv1 = b_idf_parirs.value.get(a._1).get      import breeze.linalg._      //构建当前文章TF-IDF特征向量      val bsv1 = new SparseVector[Double](sv1.indices, sv1.values, sv1.size)      //遍历相关文章      val result = idfs.map {        case (id2, idf2) =>          val sv2 = idf2.asInstanceOf[SV]          //对应相关文章的特征向量          val bsv2 = new SparseVector[Double](sv2.indices, sv2.values, sv2.size)          //计算余弦值          val cosSim = bsv1.dot(bsv2) / (norm(bsv1) * norm(bsv2))          (a._1, id2, cosSim)      }      // 文章1,文章2,相似度      result.filter(a => a._3 >= sim)    })    docSims.take(10).foreach(x => println(x))    //取出所有,有相似度的文章    val vertexrdd = docSims.map(a => {      (a._2.toLong, a._1.toLong)    })    //构建图    val graph = Graph.fromEdgeTuples(vertexrdd, 1)    val graphots = Graph.graphToGraphOps(graph).connectedComponents().vertices    //聚类初始化 计算文章向量  init_rdd(id,(id,content,title))    init_rdd.join(graphots).take(10).foreach(x => println(x))    val simrdd = init_rdd.join(graphots).map(a => {      (a._2._2, (a._2._1, a._1))    })    val simrddtop = simrdd.groupByKey().filter(a => a._2.size >= 6).sortBy(-_._2.size).take(50)    val simrdd2 = ctx.parallelize(simrddtop, 18)    simrdd2.take(10).foreach(x => {      val titles = x._2.map(x => x._1(1)).toArray      //选取事件主题名      val title = mostSimilartyTitle(titles)      println("事件---------------------" + title)      println(x._1)      x._2.foreach(x => println(x._2 + " " + x._1(0) + " " + x._1(1)))    })  }  /**    * 相似度比对 最短编辑距离    * @param s    * @param t    * @return    */  def ld(s: String, t: String): Int = {    var sLen: Int = s.length    var tLen: Int = t.length    var cost: Int = 0    var d = Array.ofDim[Int](sLen + 1, tLen + 1)    var ch1: Char = 0    var ch2: Char = 0    if (sLen == 0)      tLen    if (tLen == 0)      sLen    for (i <- 0 to sLen) {      d(i)(0) = i    }    for (i <- 0 to tLen) {      d(0)(i) = i    }    for (i <- 1 to sLen) {      ch1 = s.charAt(i - 1)      for (j <- 1 to tLen) {        ch2 = t.charAt(j - 1)        if (ch1 == ch2) {          cost = 0        } else {          cost = 1        }        d(i)(j) = Math.min(Math.min(d(i - 1)(j) + 1, d(i)(j - 1) + 1), d(i - 1)(j - 1) + cost)      }    }    return d(sLen)(tLen)  }  /**    *    * @param src    * @param tar    * @return    */  def similarity(src: String, tar: String): Double = {    val a: Int = ld(src, tar)    1 - a / (Math.max(src.length, tar.length) * 1.0)  }  /**    * 选出一组字符串 中相似度最高的    * @param strs    * @return    */  def mostSimilartyTitle(strs: Array[String]): String = {    var map: Map[String, Double] = Map()    for (i <- 0 until strs.length) {      for (j <- i + 1 until strs.length) {        var similar = similarity(strs(i), strs(j))        if (map.contains(strs(i)))          map += (strs(i) -> (map.get(strs(i)).get + similar))        else          map += (strs(i) -> similar)        if (map.contains(strs(j)))          map += (strs(j) -> (map.get(strs(j)).get + similar))        else          map += (strs(j) -> similar)      }    } //end of for    if (map.size > 0)      map.toSeq.sortWith(_._2 > _._2)(0)._1    else      ""  }}
原创粉丝点击