暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Hadoop/Spark处理技巧【算法】系列---反转排序

考拉苑 2017-02-17
233

    其实在实际的应用中在数据分析阶段可能会需要有序的数据,同时在hadoop和spark中值到reduce的顺序是未定义的,换句话就是没有明确的顺序,除非我们利用mapreduce的排序阶段(sort)将所需要的数据送达归约器(reduce),进而我们引入了反转排序【Order Inversion,OI】,用来控制mapreduce中的归约器值的顺序,OI模式比较适合成对模式,适当地确定提供reduce的数据顺序。来个栗子阐述下:

  比如:计算给定文档集中单词的相对频度,为了更好理解我们通过矩阵来描述该问题:通过建一个 N x N的矩阵,其中N为所有给定文档的单词量,每个单元Mij则代表一个特定的邻域里单词Wi和Wj共同出现的次数【邻域可以简单理解为以某个单词为中心前后跨度单词的范围】,现有W1,W2,W3,W4,W5,W6 那么基于这个单词集合形成的邻域表如下,同时设定跨度为2

W1       W2,W3

W2       W1,W3,W4

W3       W1,W2, W4, W5

W4       W2,W3, W5, W6

W5       W3, W4, W6

W6       W4,W5

对应的计算方式:单词集合 tokens  变量i:代表单词集合元素下班  变量neighborWindow:每个单词的邻域跨度

// 循环遍历单词集合

for(int i=0; i<tokens.length;i++){

    String word = tokens[i];

    因为每一个单词的邻域范围[0,tokens.length)【主要因为数组下标从0开始故而上限不能超过tokens.length】

   // 计算邻域范围的起点:通过将当前元素下标与跨度的差值是否>0;大于0说明该单词左侧存在其他单词,故而需要获取对应的下标( i-neighborWindow)

 //计算邻域范围的终点则比较当前单词的下标和跨度的和值是否>tokens.length;若是小于则说明该单词右边还有其他单词,故而获取对应的值(i+neighborWindow)

    int start =  (i-neighborWindow < 0) ? 0 :  i-neighborWindow; 跨度起点

   int end = (i+neighborWindow > tokens.length) ? tokens.length -1 : i+neighborWindow 

}

上面的内容总结的结果:计算相对频度是需要得到边缘计算【领域范围的起点和终点】,在完成前面的需求,我们在得到所有的计算前,是无法完成边缘计算的,所以需要边缘计数在联合计数之前达到归约器【reduce】,需要注意一点计算相对频度尽量不要使用绝对单词数【绝对计算没有考虑某些单词可能比另外一些单词出现次数频繁,比如W1和W2经常出现,原因是W1比较常用】

                 N(Wi,Wj) 【单词对(Wi,Wj)出现的次数】

f(Wi|Wj) = -----------------

                  ∑N(Wi,w)【Wi与所有的其他单词出现的总次数】

接下来我们将分别使用Hadoop和Spark分别实现反转排序

一、MapReduce实现

(1)、Driver

import org.apache.hadoop.conf.Configured;

import org.apache.hadoop.conf.Configuration;

import org.apache.hadoop.fs.Path;

import org.apache.hadoop.fs.FileSystem;

import org.apache.hadoop.io.IntWritable;

import org.apache.hadoop.io.DoubleWritable;

import org.apache.hadoop.mapreduce.Job;

import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;

import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;

import org.apache.hadoop.util.Tool;

import org.apache.hadoop.util.ToolRunner;

//

import org.apache.log4j.Logger;


public class RelativeFrequencyDriver

        extends Configured implements Tool {


    private static final Logger THE_LOGGER = Logger.getLogger(RelativeFrequencyDriver.class);


    public static void main(String[] args) throws Exception {

        if (args.length != 3) {

            THE_LOGGER.warn("usage: <window> <input> <output>");

            System.exit(-1);

        }

       

        int status = ToolRunner.run(new RelativeFrequencyDriver(), args);

        System.exit(status);

    }


    @Override

    public int run(String[] args) throws Exception {

        int neighborWindow = Integer.parseInt(args[0]);

        Path inputPath = new Path(args[1]);

        Path outputPath = new Path(args[2]);


        Job job = new Job(new Configuration(), "RelativeFrequencyDriver");

        job.setJarByClass(RelativeFrequencyDriver.class);

        job.setJobName("RelativeFrequencyDriver");


        Delete the output directory if it exists already

        FileSystem.get(getConf()).delete(outputPath, true);


        job.getConfiguration().setInt("neighbor.window", neighborWindow);


        FileInputFormat.setInputPaths(job, inputPath);

        FileOutputFormat.setOutputPath(job, outputPath);


        (key,value) generated by map()

        job.setMapOutputKeyClass(PairOfWords.class);

        job.setMapOutputValueClass(IntWritable.class);


        (key,value) generated by reduce()

        job.setOutputKeyClass(PairOfWords.class);

        job.setOutputValueClass(DoubleWritable.class);


        job.setMapperClass(RelativeFrequencyMapper.class);

        job.setReducerClass(RelativeFrequencyReducer.class);

        job.setCombinerClass(RelativeFrequencyCombiner.class);

        job.setPartitionerClass(OrderInversionPartitioner.class);

        job.setNumReduceTasks(3);


        long startTime = System.currentTimeMillis();

        job.waitForCompletion(true);

        THE_LOGGER.info("Job Finished in milliseconds: " + (System.currentTimeMillis() - startTime));

        return 0;

    }


}

  辅助参数: PairOfWords 组合键

import java.io.DataInput;

import java.io.DataOutput;

import java.io.IOException;

//

import org.apache.hadoop.io.Text;

import org.apache.hadoop.io.WritableComparable;

import org.apache.hadoop.io.WritableComparator;

import org.apache.hadoop.io.WritableUtils;


public class PairOfWords implements WritableComparable<PairOfWords> {


    private String leftElement;

    private String rightElement;


    **

     * Creates a pair.

     */

    public PairOfWords() {

    }


    **

     * Creates a pair.

     *

     * @param left the left element

     * @param right the right element

     */

    public PairOfWords(String left, String right) {

        set(left, right);

    }


    **

     * Deserializes the pair.

     *

     * @param in source for raw byte representation

     */

    @Override

    public void readFields(DataInput in) throws IOException {

        leftElement = Text.readString(in);

        rightElement = Text.readString(in);

    }


    **

     * Serializes this pair.

     *

     * @param out where to write the raw byte representation

     */

    @Override

    public void write(DataOutput out) throws IOException {

        Text.writeString(out, leftElement);

        Text.writeString(out, rightElement);

    }


    public void setLeftElement(String leftElement) {

        this.leftElement = leftElement;

    }


    public void setWord(String leftElement) {

        setLeftElement(leftElement);

    }


    **

     * Returns the left element.

     *

     * @return the left element

     */

    public String getWord() {

        return leftElement;

    }


    **

     * Returns the left element.

     *

     * @return the left element

     */

    public String getLeftElement() {

        return leftElement;

    }


    public void setRightElement(String rightElement) {

        this.rightElement = rightElement;

    }


    public void setNeighbor(String rightElement) {

        setRightElement(rightElement);

    }


    **

     * Returns the right element.

     *

     * @return the right element

     */

    public String getRightElement() {

        return rightElement;

    }


    public String getNeighbor() {

        return rightElement;

    }


    **

     * Returns the key (left element).

     *

     * @return the key

     */

    public String getKey() {

        return leftElement;

    }


    **

     * Returns the value (right element).

     *

     * @return the value

     */

    public String getValue() {

        return rightElement;

    }


    **

     * Sets the right and left elements of this pair.

     *

     * @param left the left element

     * @param right the right element

     */

    public void set(String left, String right) {

        leftElement = left;

        rightElement = right;

    }


    /**

     * Checks two pairs for equality.

     *

     * @param obj object for comparison

     * @return <code>true</code> if <code>obj</code> is equal to this object, <code>false</code> otherwise

     */

    @Override

    public boolean equals(Object obj) {

        if (obj == null) {

            return false;

        }

        //

        if (!(obj instanceof PairOfWords)) {

            return false;

        }

        //

        PairOfWords pair = (PairOfWords) obj;

        return leftElement.equals(pair.getLeftElement())

                && rightElement.equals(pair.getRightElement());

    }


    /**

     * Defines a natural sort order for pairs. Pairs are sorted first by the left element, and then by the right

     * element.

     *

     * @return a value less than zero, a value greater than zero, or zero if this pair should be sorted before, sorted

     * after, or is equal to <code>obj</code>.

     */

    @Override

    public int compareTo(PairOfWords pair) {

        String pl = pair.getLeftElement();

        String pr = pair.getRightElement();


        if (leftElement.equals(pl)) {

            return rightElement.compareTo(pr);

        }


        return leftElement.compareTo(pl);

    }


    /**

     * Returns a hash code value for the pair.

     *

     * @return hash code for the pair

     */

    @Override

    public int hashCode() {

        return leftElement.hashCode() + rightElement.hashCode();

    }


    /**

     * Generates human-readable String representation of this pair.

     *

     * @return human-readable String representation of this pair

     */

    @Override

    public String toString() {

        return "(" + leftElement + ", " + rightElement + ")";

    }


    /**

     * Clones this object.

     *

     * @return clone of this object

     */

    @Override

    public PairOfWords clone() {

        return new PairOfWords(this.leftElement, this.rightElement);

    }


    /**

     * Comparator optimized for <code>PairOfWords</code>.

     */

    public static class Comparator extends WritableComparator {


        /**

         * Creates a new Comparator optimized for <code>PairOfWords</code>.

         */

        public Comparator() {

            super(PairOfWords.class);

        }


        /**

         * Optimization hook.

         */

        @Override

        public int compare(byte[] b1, int s1, int l1, byte[] b2, int s2, int l2) {

            try {

                int firstVIntL1 = WritableUtils.decodeVIntSize(b1[s1]);

                int firstVIntL2 = WritableUtils.decodeVIntSize(b2[s2]);

                int firstStrL1 = readVInt(b1, s1);

                int firstStrL2 = readVInt(b2, s2);

                int cmp = compareBytes(b1, s1 + firstVIntL1, firstStrL1, b2, s2 + firstVIntL2, firstStrL2);

                if (cmp != 0) {

                    return cmp;

                }


                int secondVIntL1 = WritableUtils.decodeVIntSize(b1[s1 + firstVIntL1 + firstStrL1]);

                int secondVIntL2 = WritableUtils.decodeVIntSize(b2[s2 + firstVIntL2 + firstStrL2]);

                int secondStrL1 = readVInt(b1, s1 + firstVIntL1 + firstStrL1);

                int secondStrL2 = readVInt(b2, s2 + firstVIntL2 + firstStrL2);

                return compareBytes(b1, s1 + firstVIntL1 + firstStrL1 + secondVIntL1, secondStrL1, b2,

                        s2 + firstVIntL2 + firstStrL2 + secondVIntL2, secondStrL2);

            } catch (IOException e) {

                throw new IllegalArgumentException(e);

            }

        }

    }


    static { // register this comparator

        WritableComparator.define(PairOfWords.class, new Comparator());

    }

}


(2)、自定义partition :由于这里面我们需要使用组合键的方式(W,Wj)【代表和单词W公共出现的单词组合,一般W是不变的】,为了能够使组合键(W,Wi)左边的W投递到同一个reduce上面,需要定制分区器

import org.apache.hadoop.io.IntWritable;

import org.apache.hadoop.mapreduce.Partitioner;


public class OrderInversionPartitioner 

   extends Partitioner<PairOfWords, IntWritable> {


    @Override

    public int getPartition(PairOfWords key, 

                            IntWritable value, 

                            int numberOfPartitions) {

        // key = (leftWord, rightWord) = (word, neighbor)

        String leftWord = key.getLeftElement();

        return Math.abs( ((int) hash(leftWord)) % numberOfPartitions);

    }

    

    /**

     * 重载hashcode的计算

     */

    private static long hash(String str) {

       long h = 1125899906842597L; // prime

       int length = str.length();

       for (int i = 0; i < length; i++) {

          h = 31*h + str.charAt(i);

       }

       return h;

    }

    

}

(3)、Mapper实现

import org.apache.hadoop.io.IntWritable;

import org.apache.hadoop.io.LongWritable;

import org.apache.hadoop.io.Text;

import org.apache.hadoop.mapreduce.Mapper;

//

import org.apache.commons.lang.StringUtils;

//

import java.io.IOException;


public class RelativeFrequencyMapper

        extends Mapper<LongWritable, Text, PairOfWords, IntWritable> {

    private int neighborWindow = 2;

    // pair = (leftElement, rightElement)

    private final PairOfWords pair = new PairOfWords();

    private final IntWritable totalCount = new IntWritable();

    private static final IntWritable ONE = new IntWritable(1);


    @Override

    public void setup(Context context) {

        this.neighborWindow = context.getConfiguration().getInt("neighbor.window", 2);

    }


    @Override

    protected void map(LongWritable key, Text value, Context context)

            throws IOException, InterruptedException {


        String[] tokens = StringUtils.split(value.toString(), " ");

        //String[] tokens = StringUtils.split(value.toString(), "\\s+");

        if ((tokens == null) || (tokens.length < 2)) {

            return;

        }


        for (int i = 0; i < tokens.length; i++) {

            tokens[i] = tokens[i].replaceAll("\\W+", "");


            if (tokens[i].equals("")) {

                continue;

            }


            pair.setWord(tokens[i]);

            // 计算邻域的边缘计数

            int start = (i - neighborWindow < 0) ? 0 : i - neighborWindow;

            int end = (i + neighborWindow >= tokens.length) ? tokens.length - 1 : i + neighborWindow;

            for (int j = start; j <= end; j++) {

                if (j == i) {

                    continue;

                }

                pair.setNeighbor(tokens[j].replaceAll("\\W", ""));

                context.write(pair, ONE);

            }

            //

            pair.setNeighbor("*");

            totalCount.set(end - start);

            context.write(pair, totalCount);

        }

    }

}


文章转载自考拉苑,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论