Python+MapReduce实现矩阵相乘

算法原理

map阶段

在map阶段,需要做的是进行数据准备。把来自矩阵A的元素aij,标识成p条<key, value>的形式,key="i,k",(其中k=1,2,...,p),value="a:j,aij";把来自矩阵B的元素bij,标识成m条<key, value>形式,key="k,j"(其中k=1,2,...,m),value="b:i,bij"。

经过处理,用于计算cij需要的a、b就转变为有相同key("i,j")的数据对,通过value中"a:"、"b:"能区分元素是来自矩阵A还是矩阵B,以及具体的位置(在矩阵A的第几列,在矩阵B的第几行)。

shuffle阶段  

这个阶段是Hadoop自动完成的阶段,具有相同key的value被分到同一个Iterable中,形成<key,Iterable(value)>对,再传递给reduce。

reduce阶段

通过map数据预处理和shuffle数据分组两个阶段,reduce阶段只需要知道两件事就行:

<key,Iterable(value)>对经过计算得到的是矩阵C的哪个元素?因为map阶段对数据的处理,key(i,j)中的数据对,就是其在矩阵C中的位置,第i行j列。
Iterable中的每个value来自于矩阵A和矩阵B的哪个位置?这个也在map阶段进行了标记,对于value(x:y,z),只需要找到y相同的来自不同矩阵(即x分别为a和b)的两个元素,取z相乘,然后加和即可。

过程如下图所示:

算法实现

mapper.py

#!/usr/bin/env python3
import sys

flag = 0       # 0表示输入A、B矩阵信息,1表示处理A矩阵,2表示处理B矩阵
row_a, col_a, row_b, col_b = 0, 0, 0, 0  # A、B矩阵shape
current_row = 1  # 记录现在处理矩阵的第几行


def read_input():
    for lines in sys.stdin:
        yield lines


if __name__ == '__main__':
    for line in read_input():
        if line.count('\n') == len(line):    # 去空行
            pass
        data = line.strip().split('\t')

        if flag == 0:
            flag = 1
            row_a = int(data[0])
            col_a = int(data[1])
            row_b = int(data[2])
            col_b = int(data[3])
            if row_a == 0 or row_b == 0 or col_a == 0 or col_b ==0 or col_a != row_b:
                print("矩阵输入错误!")
                break

        elif flag == 1:
            for i in range(col_b):
                for j in range(col_a):
                    print("%s,%s\tA:%s,%s" % (current_row, i+1, j+1, data[j]))
            current_row += 1
            if current_row > row_a:
                flag = 2
                current_row = 1

        elif flag == 2:
            for i in range(row_a):
                for j in range(col_b):
                    print("%s,%s\tB:%s,%s" % (i+1, j+1, current_row, data[j]))
            current_row += 1

reducer.py

这是我一开始所写的版本。

#!/usr/bin/env python3
import sys


last, now = None, None
s = 0.0
count = 0
matrix_a, matrix_b = {}, {}


def read_input():
    for lines in sys.stdin:
        yield lines


if __name__ == '__main__':
    for line in read_input():
        if line.count('\n') == len(line):    # 去空行
            pass
        data = line.strip().split('\t')
        now = data[0]
        if last is None:
            last = now
            count = 0
        elif last != now:
            for key in matrix_a:
                s += float(matrix_a[key])*float(matrix_b[key])
            print("%s\t%s" % (last, s))
            s = 0.0
            count = 0
            last = now

        value1 = data[1][0]
        value2 = data[1].split(':')[1].split(',')[0]
        value3 = data[1].split(',')[1]
        if value1 == 'A':
            count += 1
            matrix_a[value2] = value3
        else:
            matrix_b[value2] = value3

    for key in matrix_a:
        s += float(matrix_a[key])*float(matrix_b[key])
    print("%s\t%s" % (last, s))

 后来借鉴参考了别人的代码后,学习了groupby,下面的代码就简洁多了。

#!/usr/bin/env python3
import sys
from itertools import groupby
from operator import itemgetter


def read_input(splitstr):
    for line in sys.stdin:
        line = line.strip()
        if len(line) == 0:
            continue
        yield line.split(splitstr)


if __name__ == '__main__':
    data = read_input('\t')
    lstg = (groupby(data, itemgetter(0)))
    try:
        for flag, group in lstg:
            matrix_a, matrix_b = {}, {}
            total = 0.0
            for element, g in group:
                matrix = g.split(':')[0]
                pos = g.split(':')[1].split(',')[0]
                value = g.split(',')[1]
                if matrix == 'A':
                    matrix_a[pos] = value
                else:
                    matrix_b[pos] = value
            for key in matrix_a:
                total += float(matrix_a[key]) * float(matrix_b[key])
            print("%s\t%s" % (flag, total))
    except Exception:
        pass

  

算法运行

执行结果为:

 

 

参考:

[1] 用MapReduce实现矩阵乘法

[2] python版mapreduce矩阵相乘

[3] MapReduce实现矩阵乘法

posted @ 2019-03-23 23:34  Kayden_Cheung  阅读(2030)  评论(0编辑  收藏  举报
//目录