使用hadoop+中文分词统计小说里的用词频率

事情是这样的, 某小说贴吧吧友开玩笑说 某作者最常使用的词语是xxxx , 然后就突发奇想地想用工具分析一下

环境

系统: ArchLinux

软件: hadoop 2.7

准备

下载hadoop: 下载地址

下载分词插件: 下载地址

1.解压hadoop-2.7.0.tar.gz: tar cxf hadoop-2.7.0.tar.gz

我的解压到/home/ystyle/Applications/hadoop-2.7.0下面了(我下载时是hadoop-2.7.0.tar.gz)

2.设置环境变量
vim ~/.bashrc

1
2
export HADOOP_INSTALL=/home/ystyle/Applications/hadoop-2.7.0
export PATH=$PATH:$HADOOP_HOME/bin:$HADOOP_HOME/sbin

然后再执行 source ~/.bashrc

PS: 这里使用hadoop的单机模式

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
package net.ystyle.hadoop;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.Reducer;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import org.apache.hadoop.util.GenericOptionsParser;
import org.wltea.analyzer.core.IKSegmenter;
import org.wltea.analyzer.core.Lexeme;

import java.io.IOException;
import java.io.StringReader;

public class WordCount {

public static class TokenizerMapper
extends Mapper<Object, Text, Text, IntWritable>{

private final static IntWritable one = new IntWritable(1);
private Text word = new Text();

public void map(Object key, Text value, Context context
) throws IOException, InterruptedException {
IKSegmenter iks = new IKSegmenter(new StringReader(value.toString()), true);
Lexeme t;
while ((t = iks.next()) != null) {
word.set(t.getLexemeText());
context.write(word, one);
}
}
}

public static class IntSumReducer
extends Reducer<Text,IntWritable,Text,IntWritable> {
private IntWritable result = new IntWritable();

public void reduce(Text key, Iterable<IntWritable> values,
Context context
) throws IOException, InterruptedException {
int sum = 0;
for (IntWritable val : values) {
sum += val.get();
}
result.set(sum);
context.write(key, result);
}
}

public static void main(String[] args) throws Exception {
Configuration conf = new Configuration();
String[] otherArgs = new GenericOptionsParser(conf, args).getRemainingArgs();
if (otherArgs.length < 2) {
System.err.println("Usage: wordcount <in> [<in>...] <out>");
System.exit(2);
}
Job job = Job.getInstance(conf, "word count");
job.setJarByClass(WordCount.class);
job.setMapperClass(TokenizerMapper.class);
job.setCombinerClass(IntSumReducer.class);
job.setReducerClass(IntSumReducer.class);
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(IntWritable.class);
for (int i = 0; i < otherArgs.length - 1; ++i) {
FileInputFormat.addInputPath(job, new Path(otherArgs[i]));
}
FileOutputFormat.setOutputPath(job,
new Path(otherArgs[otherArgs.length - 1]));
System.exit(job.waitForCompletion(true) ? 0 : 1);
}
}

本代码改自hadoop自带的wordcount: 源码在hadoop/share/hadoop/mapreduce/sources/hadoop-mapreduce-examples-2.7.2-sources.jar 类名为org.apache.hadoop.examples.WordCount 这里只改了map方法添加了IK分词

在IDEA里创建项目

  1. 创建普通项目
  2. shift + alt + ctrl + s 进入项目设置, 选择Modules-Dependencies添加hadoop/share/hadoop/目录下的common hdfs mapreduce yarn common/lib 到Dependencies下
  3. 把下载的ik分词jar放hadoop/share/hadoop/mapreduce目录下

如图:

运行

  1. 把下载的小说放 /home/ystyle/hadoop/input 下面
  2. 在IDEA的Run/Debug Configurations里的Program arguments 里填上
    /home/ystyle/hadoop/input /home/ystyle/hadoop/output
  3. 近按Shift + F10 运行, 等待结果

结果分析

我分析的小说为<<我欲封天>>

  1. cd /home/ystyle/hadoop/output 结果在文件part-r-00000
  2. 先把结果按使用次数排序并存在0.txt里: sort -k2rn part-r-00000> 0.txt
  3. 分析两字词语的使用次数: awk '{if(length($1)==2) print $0}' 0.txt > 2.txt
  4. 分析四字词语的使用次数: awk '{if(length($1)==4) print $0}' 0.txt > 4.txt

部分结果如下

两字词语

1
2
3
4
5
6
7
8
9
10
在这	9889
此刻 9000
一个 8819
立刻 8676
身体 8338
到了 7906
出现 7504
他们 7382
修士 7350
四周 7104

四字词语

1
2
3
4
5
6
7
8
9
10
与此同时	1443
中年男子 1089
轰的一声 1048
未完待续 893
惊天动地 853
面色苍白 752
前所未有 481
兄弟姐妹 421
无法形容 408
毫不迟疑 390