上一篇文章主要讲的是spark如何进行词频统计,数据集为自己创建的,本篇主要使用上一篇的逻辑在实际应用中进行扩展—计算广州美食中最受欢迎的美食类别,然后把数据画成词云。
本篇是上一篇spark的学习(1)之使用spark进行WordCount词数统计的扩展,所以本篇没有涉及到更多的API,主要基于原本的API。重点在于思路,至于spark的后续知识点会在后续博文中说明,(^_−)☆ 关注博主不走丢
如果进行大数据处理,首先我们需要先明白数据,本文中的数据集是从和鲸社区中下载的,下载地址如下
https://www.heywhale.com/mw/dataset/5ee30becb772f5002d75a965/content
把数据集下载下来后,解压到idea中data目录下
读取数据
创建FoodsCount类,注意是Object类型,然后创建main方法,方法中编写spark,编写的方式和之前类似
第一步:创建Spark上下文
第二步:读取数据
第三步:转换数据
第四步:计算数据格式
这里我们先开始写第一步,读取数据
object FoodsCount {
def main(args: Array[String]): Unit = {
// 创建spark上下文
val spark = new SparkContext("local",
"food")
// 读取数据,A.csv就是刚下载的文件,我这里之前对文件进行重命名,自己用的时候注意改下
val data = spark.textFile("data/A.csv")
// 打印数据看下效果
// take(10) 返回数据中10条记录
// foreach(println(_)) 遍历数据,输出每一条
data.take(10).foreach(println(_))
}
}
输出结果如下
名称,评论数,人均价格,类别,商圈,地址,推荐1,推荐2,推荐3,口味评分,环境评分,服务评分,星级,店铺ID,网址
极炙·台灣精致炭火烤肉,3551,211元,日本料理,天河城/体育中心,天河路178-188号新天河宾馆院内综合楼3楼,极上和牛套餐,特选牛舌,元贝,9.1,8.5,9.2,五星商户,66250176,http://www.dianping.com/shop/66250176
大滷爺(正佳店),151,54元,粤菜,天河城/体育中心,体育东路正佳广场五楼广正街,滷爺鹅肉饭,生蚝沙锅粥,卤水鹅肝饭,9.3,9.1,9.1,五星商户,id110266597,http://www.dianping.com/shop/110266597
白天鹅宾馆·玉堂春暖餐厅,2018,312元,粤菜,沙面,沙面南街1号白天鹅宾馆3楼,沙琪玛,葵花鸡,招牌虾饺,8.9,9.3,9,五星商户,id520094,http://www.dianping.com/shop/520094
Mr.Fish鱼鲜生海鲜放题(高德置地冬广场店),7703,354元,自助餐,高德置地/花城汇,珠江新城花城大道85号高德置地冬广场5楼507-512铺,刺身新鲜,新西兰鳌虾,燕窝哈根达斯雪糕,8.8,9,8.7,准五星商户,id32501719,http://www.dianping.com/shop/32501719
漫活堂·健康有机西餐厅(高志店),1174,148元,西餐,兴盛路/跑马场,黄埔大道西120号高志大厦首层107铺,低温慢煮牛小排,青苹果焦糖核桃沙拉,鲜虾墨鱼汁意大利面配参巴海鲜酱,9.1,9,9,五星商户,id76972044,http://www.dianping.com/shop/76972044
点都德(花城店),5424,88元,茶餐厅,珠江新城,花城大道16号,金沙红米肠,金牌鲜虾饺皇,百合蒸酱凤爪,9,8.9,8.4,准五星商户,id56985236,http://www.dianping.com/shop/56985236
点都德(骏和楼店),1393,79元,茶餐厅,市桥,市桥街捷进二路1号骏和广场三楼,金沙红米肠,金牌鲜虾饺皇,沙爹金钱肚,9.1,9,8.8,五星商户,id69100250,http://www.dianping.com/shop/69100250
松月自慢料理,1348,382元,日本料理,珠江新城,珠江西路15号珠江城大厦商业4层,生烤牛肉,刺身拼盘,牛油果虾手卷,9,9.3,9.1,五星商户,id92411666,http://www.dianping.com/shop/92411666
串八·炉端烧,70,178元,日本料理,兴盛路/跑马场,马场西路黄埔大道西668号马会酒店大堂内,牛舌,牛肉鹅肝卷,豚肉芦笋卷,9.3,9,9,五星商户,id98948970,http://www.dianping.com/shop/98948970
从打印的数据中可以看出,目前程序正常,然后查看数据集,数据集本身是CSV文件,该类型文件,一般第一行作为标题,每一个数据之间使用逗号作为间隔,从打印数据的第一行中我们可以得出数据集主要涵盖饭店的名称,类别,人均等信息。
明白数据后,我们开始回归到本身的问题:计算广州美食中最受欢迎的美食类别
当我们拿到一个问题后,需要把问题转换成使用数字可以解决的问题,在这个问题中我们可以简单算为,餐厅类型最多的就是最受人喜欢的,因为喜欢,客户量大,才有老板开这个类型的饭店,所谓我们可以把原问题转换为 广州美食中餐厅类别数量
理清思路后,我们就需要把数据集中的数据,转换成便于计算的格式
转换&计算数据
此时数据的格式还是全有逗号隔开
极炙·台灣精致炭火烤肉,3551,211元,日本料理,天河城/体育中心,天河路178-188号新天河宾馆院内综合楼3楼,极上和牛套餐,特选牛舌,元贝,9.1,8.5,9.2,五星商户,66250176,http://www.dianping.com/shop/66250176
在这里面我们只需要类别这一个值,并且计算最后多出现的数量,这其实有点类似于之前词频统计了,不同的是统计的是类别中的数据,并且数据集接近于实际数据,但是道理是一样的。
为方便计算,我们需要把数据转换成
(日本料理,1),(粤菜,1)....
最后统计相同key值就好了,具体代码如下
...
val foodTypes = data.map(
line => {
// 针对数据进行切分
val arr = line.split(",")
// 因为只需要类型,所以取切分后数组下标为3的数据
(arr(3),1)
}
)
// 还是和之前统计词频一样的方式,countByKey()统计相同key的数量
val result = foodTypes.countByKey()
result.foreach(println(_))
结果输出如下
(新疆菜,1)
(咖啡厅,12)
(西北菜,1)
(东南亚菜,7)
......
此时绘画成词云,更便于查看
绘画词云
词云是文字组件的一种,支持自定义文本的内容、颜色、绘制形状等,支持多系列颜色配置,支持根据权重值映射文本大小,能够以词云的形式在可视化应用中展示较多数量的文本。
scala本身不支持词云,在这里我们使用java的kumo去生成词云。
首先在当下项目pom文件下,导入依赖
<dependency>
<groupId>com.kennycason</groupId>
<artifactId>kumo-core</artifactId>
<version>1.13</version>
<exclusions>
<exclusion>
<artifactId>commons-lang3</artifactId>
<groupId>org.apache.commons</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.kennycason</groupId>
<artifactId>kumo-tokenizers</artifactId>
<version>1.12</version>
</dependency>
在项目下创建utils包,用于存放相关类。
创建MyAngleGenerator类继承AngleGenerator,这里主要是为了自定义词云排列的横纵比,避免大多数单词纵状排列,直接复制下就好
public class MyAngleGenerator extends AngleGenerator {
private static final Random RANDOM = new Random();
private final int steps;
private final double[] thetas;
private int next;
public MyAngleGenerator() {
this.steps = 3;
this.thetas = this.calculateThetas(-90.0D, 90.0D);
}
@Override
public double randomNext() {
// 0.8 水平 0.2 垂直
int i = RANDOM.nextInt(10);
if (i < 2) {
// 这里修改概率
i = RANDOM.nextInt(2);
if (i < 1) {
return thetas[0];
} else {
return thetas[2];
}
} else {
return this.thetas[1];
}
}
private double[] calculateThetas(double to, double from) {
double stepSize = (to - from) / (double)(this.steps - 1);
double[] thetas = new double[this.steps];
for(int i = 0; i < this.steps; ++i) {
thetas[i] = this.degreesToRadians(from + (double)i * stepSize);
}
return thetas;
}
private double degreesToRadians(double degrees) {
return 3.141592653589793D * degrees / 180.0D;
}
}
创建词云生成的工具类WordCloudUtil,用于生成词云
public class WordCloudUtil {
/**
* 根据集合生成圆形词云
* @param wordFrequencies WordFrequency 的集合对象
* WordFrequency(单词,单词数量)
*/
public static void createWordCloud(List<WordFrequency> wordFrequencies){
// 设置图片大小
Dimension dimension = new Dimension(300, 300);
//设置图片相关的属性,这边是大小和形状,更多的形状属性,可以从CollisionMode源码里面查找
WordCloud wordCloud = new WordCloud(dimension, CollisionMode.PIXEL_PERFECT);
wordCloud.setPadding(2);
//这边要注意,是设置中文字体的,如果不设置,得到的将会是乱码,
java.awt.Font font = new java.awt.Font("STSong-Light", 2, 40);
wordCloud.setKumoFont(new KumoFont(font));
// 设置背景色
wordCloud.setBackgroundColor(new Color(255, 255, 255));
// 设置圆的半径
wordCloud.setBackground(new CircleBackground(150));
// 设置颜色
wordCloud.setColorPalette(new LinearGradientColorPalette(Color.RED, Color.BLUE, Color.GREEN, 30, 30));
wordCloud.setFontScalar(new SqrtFontScalar(12, 45));
// 自定义横纵比例
wordCloud.setAngleGenerator(new MyAngleGenerator());
//将文字写入图片
wordCloud.build(wordFrequencies);
//生成图片
wordCloud.writeToFile("output/foods.png");
}
}
然后接下来就是scala中的调用了,在原来scala main方法下
...
// 创建词云数据
var wordFrequencies = ArrayBuffer[WordFrequency]()
// 遍历result,把对应数据存入词云数据中
result.foreach{
case(str,num) => {
wordFrequencies = wordFrequencies :+ (new WordFrequency(str,num.toInt))
}
}
// 开始调用,注意提前导入import scala.collection.JavaConverters._
// 因为要使用asJava方法把scala对象转换为java对象
WordCloudUtil.createWordCloud(wordFrequencies.asJava)
...
运行完成后可以在当前项目下的output目录中看到生成图片如下
从词云中可以看出,目前最受欢迎的属于西餐,日本料理和粤菜。
以上就是文章所有内容,代码在以下地址中
https://gitee.com/lihao2/blog-code
本文主要是一些思路,spark简单处理,Spark中关于数据的操作还有很多,我们将在后续文章中去讲,( ̄︶ ̄)↗ 关注博主不走丢