《Data Algorithm
》读书笔记五之 — 反转排序
1. 什么是反转排序
Hadoop
中的反转排序并不是反过来排序。【即使是反过来排序,也是一种排序,就不用搞一个反转排序 的名字出来了】那么MapReduce
中的反转排序到底是什么意思呢?
从事java 开发的相信都知道IOC
,即控制反转(Inversion of Control
)。
以下是百度百科中对IOC
的介绍,我这里不再赘述了。
IOC 是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。其中最常见的方式叫做依赖注入(Dependency Injection,简称DI)…
同样,如果想要在 MapReduce
job中,让开发者自定义排序规则,即反转排序。【我理解的定义】
2. 反转排序能干啥?
反转排序是MapReduce job
中的一种设计模式!
反转排序是MapReduce job
中的一种设计模式!
同java 工厂模式
,策略模式
等等一样,它们只不过都是一种抽象的定义,具体的实现则是要通过代码展示出来。反转排序主要提供以下功能:
- 用于控制
MapReduce
框架中归约器值的顺序 - 使用
OI
模式的唯一目的是: 适当地确定提供给归约器的数据的顺序 - 定制一个分区器,它只关注组合键(K1,K2)左边的部分(K1,即自然归约器键)。定制分区器只根据左键(K1)的散列进行分区
3. 简单示例
- 在本博客中,通过计算一个文档中单词的相对频度来展示 OI模式(只是用这个例子去展示OI模式,但是不能说这个例子就是OI模式) 。
- 例如,针对文本:
W1,W2,W5,W4,W5,W6
。如果定义一个单词的邻域为这个单词的前后两个单词,那么这6个单词的邻域表示如下:
<W1,W2>
<W1,W5>
<W2,W1>
<W2,W5>
<W2,W4>
<W5,W1>
<W5,W2>
<W5,W4>
<W5,W5>
<W4,W2>
<W4,W5>
<W4,W5>
<W4,W6>
<W5,W5>
<W5,W4>
<W5,W6>
<W6,W4>
<W6,W5>
因为计算相对频度需要得到边缘计数(所谓边缘计数就是频度总数)。在得到所有计数之前,将无法计算边缘计数。因此,需要让边缘计数在联合计数之前到达归约器(这一点可以通过使用二次排序实现)。
计算一个给定文档集的相对频度。需要生成两个数据序列。
- 第一个序列是单词的总邻域计数(一个单词的总共现次数),用组合键
(w,*)
表示,其中w表示单词; - 第二个序列是这个单词与其他特定单词的共现次数,用组合键
(w,w2)
表示。
KEY VALUE
(W,*) A1,A2,A3
(W,W1) B1,
...
(W,Wn) N1,N2,N3,N4...
在Mapper
类中,不仅向Reducer
发送(W,W1)
,也会发(W,*)
。但是需要保证的是:(W,*)
比 (W,W1)
要先到达reducer
。然后在 reducer
中进行一个数量的统计,这样就可以保证每个(W,*)
会被先统计,然后就知道有多少个数量了。
针对上述的实例:得到的如下的键值对:
(<W1,W2> , 1)
(<W1,W5> , 1)
(<W2,W1> , 1)
(<W2,W5> , 1)
(<W2,W4> , 1)
(<W4,W2> , 1)
(<W4,W5> , 1)
(<W4,W5> , 1)
(<W4,W6> , 1)
(<W5,W1> , 1)
(<W5,W2> , 1)
(<W5,W4> , <1,1>)
(<W5,W5> , <1,1>)
(<W5,W6> , 1)
(<W6,W4> , 1)
(<W6,W5> , 1)
例如,key.leftStr = W5
的都会被分到同一个分区中。在发送上述的键值对之外,还会再发送<W5,*>,<1,1,1,1,1,1,1>
4. 如何实现反转排序?
反转排序的实现主要是通过 二次排序,自定义分区方法等实现。
- 其中对二次排序的介绍可见我的博客 — 《Data Algorithm》读书笔记 — 使用MapReduce实现二次排序。
- 关于自定义分区方法的实现可以通过我的
MapReduce
代码查看其具体实现。
5.实现代码
5.1 GroupPair
package data_algorithm.chapter_5;
import org.apache.hadoop.io.WritableComparable;
import org.apache.hadoop.io.WritableComparator;
public class GroupPair extends WritableComparator {
public GroupPair(){
super(PairOfWord.class,true);
}
@Override
public int compare(WritableComparable a, WritableComparable b) {
System.out.println("");
PairOfWord p1 = (PairOfWord) a;
PairOfWord p2 = (PairOfWord) b;
int status = p1.getLeftStr().compareTo(p2.getLeftStr());
if (status == 0) {
status = p1.getRightStr().compareTo(p2.getRightStr());
}
return status;
}
}
5.2 PairOfWord
类
package data_algorithm.chapter_5;
import org.apache.hadoop.io.Writable;
import org.apache.hadoop.io.WritableComparable;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
/**
* 01.这个PairOfWord 是一个<key-value>对中的 key。 这个key由两个部分组成:分别是leftStr 和 rightStr
*/
public class PairOfWord implements Writable,WritableComparable<PairOfWord>{
private String leftStr;
private String rightStr;
public PairOfWord() {
}
public PairOfWord(String leftStr, String rightStr) {
this.leftStr =leftStr;
this.rightStr = rightStr;
}
public String getLeftStr() {
return leftStr;
}
public void setLeftStr(String leftStr) {
this.leftStr = leftStr;
}
public String getRightStr() {
return rightStr;
}
public void setRightStr(String rightStr) {
this.rightStr = rightStr;
}
@Override
public void write(DataOutput out) throws IOException {
out.writeUTF(this.leftStr);
out.writeUTF(this.rightStr);
}
@Override
public void readFields(DataInput in) throws IOException {
this.leftStr = in.readUTF();
this.rightStr = in.readUTF();
}
@Override
public int compareTo(PairOfWord o) {
//以PairOfWord 的leftStr作为比较对象
int compareValue = this.leftStr.compareTo(o.leftStr);
if (compareValue == 0) {
compareValue = this.rightStr.compareTo(o.getRightStr());
}
return compareValue;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
PairOfWord that = (PairOfWord) o;
if (leftStr != null ? !leftStr.equals(that.leftStr) : that.leftStr != null) return false;
return rightStr != null ? rightStr.equals(that.rightStr) : that.rightStr == null;
}
@Override
public int hashCode() {
int result = leftStr != null ? leftStr.hashCode() : 0;
//result = 31 * result + (rightStr != null ? rightStr.hashCode() : 0);
result = 31 * result ;
return result;
}
@Override
public String toString() {
return "<" + leftStr + ","+ rightStr + '>';
}
}
5.3 其它代码
其它的代码诸如Mapper
,Reducer
等我在这里就不介绍了,详情可以通过我的 github 查看。
6. 实现结果
6.1 输入文件
[root@server4 hadoop]# hdfs dfs -cat /input/orderInversion.txt
W1,W2,W5,W4,W5,W6
6.2 输出文件
[root@server4 hadoop]# hdfs dfs -cat /output/orderInversion/part*
<W5,W1> 0.14285714285714285
<W5,W2> 0.14285714285714285
<W5,W4> 0.2857142857142857
<W5,W5> 0.2857142857142857
<W5,W6> 0.14285714285714285
<W1,W2> 0.5
<W1,W5> 0.5
<W6,W4> 0.25
<W6,W5> 0.25
<W2,W1> 0.3333333333333333
<W2,W4> 0.3333333333333333
<W2,W5> 0.3333333333333333
<W4,W2> 0.25
<W4,W5> 0.5
<W4,W6> 0.25
6.3 其它测试
java is a great language
java is a programming language
java is green fun language
java is great
programming with java is fun
现在来看 great 的频率,如下:
(great,is)
(great,a)
(great,language)
(great,is)
(great,java)
=>
(great,is) 0.2
(great,is) 0.2
(great,a) 0.2
(great,language) 0.2
(great,java) 0.2
=>
(great,is) 0.4
(great,a) 0.2
(great,language) 0.2
(great,java) 0.2
得到的输出结果如下: