MapReduce问题与解答第2部分

4文本检索的反向索引

本章包含有关整数编码和压缩的许多详细信息。 由于这些主题并非直接与MapReduce有关,因此我对它们没有任何疑问。

4.4反向索引:修订的实现

说明反向索引检索算法。 您可以假定每个文档都适合内存。 假设还有大量的文档。 应该通过整数编码和压缩来优化哪一部分?

Input: text documents
key: document id

输出:键/值对,其中
键:字
值:list [documentId,numberOfOccurences]列表元素必须按numberOfOccurences排序

答案是:

映射器计算每个单词在文档中出现的次数。 由于整个文档都适合存储在内存中,因此我们可以将部分结果保存在地图中。

Intermediate key/values:
key: word, numberOfOccurences

自定义分区程序按单词对中间键/值进行分组。 自定义排序按单词对它们进行排序,按出现次数对它们进行排序。

Reducer使用initialize方法初始化所有发布的列表。 Reduce方法处理两种情况:

  • 当前单词等于上一个单词–将documentId和numberOfOccurences添加到发布列表中。
  • 当前单词等于前一个单词–发出前一个单词并发布列表; 初始化发布列表。

reducer中的发布列表应该被压缩。

class MAPPER
  method INITIALIZE
    H = new hash map    

  method MAP(docid, doc d)
    H = new hash map
    for all term w in doc d do
        H(w) = H(w) + 1

    for all term w in H do
        emit(pair(u, w), count 1)

  method CLOSE 
    for all term w in H
      emit(pair(w, H(w)), docid)    

class REDUCER
  variable previous_word = 0
  variable PL = new list of postings

  method REDUCE(pair (w, #occurrences), docid)
    if w <> previous_word && previous_word <> 0 do
      emit(w, PL)
      PL = new list of postings
    
    PL.add(pair(#occurrences, docid))
    previous_word = w

  method compare(key (w1, o1), key (w2, o2))
    if w1 = w2 
      return keys are equal

    return keys are different

class SORTING_COMPARATOR
  method compare(key (w1, o1), key (w2, o2))
    if w1 = w2 do
      return compare(o1, o2)
         
    return compare(w1, w2)

5种图算法

本章包含两种算法:图形中的最短路径和页面排名算法。 问题很简单。

5.2并行广度优先搜索

查找从一个节点origin到所有其他节点的最短路径。 每个边缘都有关联的权重。 输入键/值对已经被预处理成舒适的形式。

Input: graph
key: node id

输出:键/值对,其中
键:节点ID
值:到原点的距离,列表[相邻节点,边长]

答案是:

该算法需要多次迭代。 停止迭代不会更改任何“到原点的距离”。 最坏的情况是会有O(n)次迭代,其中n是图中的节点数。

映射器将原始图形原样传递给下一个迭代。 另外,它为每个相邻节点生成键/值对。 如果路线将通过节点,则该值包含距原点的最小已知距离。

class MAPPER
  method MAP(node, pair(dist, adjacencylist))
    emit(node, pair(dist, adjacencylist))
    for all (closenode, nodedist) in adjacencylist do
      emit(closenode, pair(dist + nodedist, empty))

减速器找到距每个节点的最小已知距离。 它将距离与原始图形一起传递到下一个迭代。 每当与任何节点的最小已知距离发生变化时,它也会增加全局计数器。

class REDUCER
  method REDUCE(node, list(dist, adjacencylist))
    minimum = infinity
    previous_iteration_solution = infinity
    original_graph = empty
    for all (dist, adjacencylist) in list do
      if adjacencylist not empty do 
        original_graph = adjacencylist
        previous_iteration_solution = dist
      if minimum > dist
        minimum = dist
    
    if previous_iteration_solution <> minimum
      increment global counter
    emit(node, pair(minimum, original_graph))

如果全局计数器为0,则算法停止。 否则需要另一个迭代。

说明页面等级算法,假设alpha = 0。

答案是:

网页排名P(n)页面的n计算链接到它的所有页面的表单页面行列。

P(n) = sum_m (P(m)/C(m))

该总和经过链接到页面n所有页面mC(m)是页面m的传出链接数。

页面等级算法以迭代方式运行。 映射器将每个页面的页面排名贡献传递给相邻页面。 Reducer更新每个节点的页面等级。 当页面排名不再移动时,算法停止。

class MAPPER
  method MAP(page, (page_rank, adjacency_list))
    emit(page, (0, adjacency_list))
    contribution = page_rank/adjacency_list.length
    for all node in adjacency_list do
      emit(node, (contribution, empty))

class REDUCER
  method REDUCE(page, contributions[c1, c2, ..., cn])
    rank = 0
    adjacency_list = new list
    for all c in contributions do
      adjacency_list.addAll(c.adjacency_list)
      rank = rank + c.contribution 

    emit(page, (rank, adjacency_list))

6种用于文本处理的EM算法

在本章中,我没有提出任何疑问。

练习题
本章包含MapReduce的动手练习。 其中一些需要多次迭代。

暖身

计算文本集合中每个单词的出现次数。

Input:
key: document id,

输出:
关键:字,
值:发生的次数。

答案是:

Intermediate pairs:
key: word

class MAPPER
  method MAP(docid a, doc d)
    for all term w in doc d do
      emit(w, 1)

class COMBINER
  method COMBINE(word w, counts[c1, c2, ..., cn])
    s = 0
    for all c in counts[c1, c2, ..., cn] do 
      s = s + c

    emit(word w, s)

class REDUCER
  variable total_occurrences = 0

  method REDUCE(word w, counts[c1, c2, ..., cn])
    s = 0
    for all c in counts[c1, c2, ..., cn] do 
      s = s + c

    emit(word w, s)

替代解决方案将使用映射器内合并。  

网上商店

网站用户日志包含用户ID和每个会话的时长。 该网站的注册用户数量很少。 计算每个用户的平均会话长度。

Input:
key: user id,

输出:
密钥:用户ID,
值:平均会话长度。

答案是:

由于注册用户数量不多,我们可以使用映射器内合并。

class MAPPER
  variable total_time = new hash map 
  variable sessions_number = new hash map 

  method MAP(user_id, session_length)
    total_time(user_id) = total_time(user_id) + session_length
    sessions_number(user_id) = sessions_number(user_id) + 1

  method CLOSE 
    for all user_id in total_logged_in_time
      tt = total_time(user_id)
      sn = sessions_number(user_id)
      emit(user_id, pair(tt, sn))    

class REDUCER
  method REDUCE(user_id, [pairs(time, sessions_number)])
    total_time = 0
    total_sessions = 0
    for all pairs in [pairs(time, sessions_number)] do 
      total_time = total_time + time
      total_sessions = total_sessions + sessions_number

    emit(user_id, total_time/total_sessions)

网络商店日志包含每次销售的用户ID和购买的商品。 您需要实现“项目购买者也已购买”功能。 无论何时显示商品,商店都会建议商品买家最常购买的五种商品。

Input:
key: user id,

输出:
键:项目,
价值:五个最常见的“物品购买者也已购买”物品清单。

答案是:

我们的解决方案有两个迭代。 第一次迭代将生成同一用户带来的所有项目的列表。 分组是由框架完成的,映射器和化简器均执行标识功能。

Input:
key: user id,

输出:
密钥:用户ID,
价值:所有带来的物品清单。

class MAPPER
  method MAP(user_id, item)
    emit(user_id, item)

class REDUCER
  method REDUCE(user_id, items[i1, i2, ..., in])
    emit(user_id, items)

第二次迭代解决了列表项上的共现问题。 它使用条纹方法。 与标准解决方案的唯一区别是,我们仅发出了五个最常见的共现。

Input:
key: user id,

输出:
键:项目,
值:五个最常见的同时出现的列表。

class MAPPER
  method MAP(user_id, items[i1, i2, ..., in])
    for all item in items do
      H = new hash map
      for all item j in items do
        H(j) = H(j) + 1
      emit(item, H)

class REDUCER
  method REDUCE(item, stripes[H1, H2, ..., Hn])
    T = new hash map
    for all H in stripes do
      for all (key/value) in H do
        T(key) = T(key) + value
    emit(user_id, max_five(T))

网上商店日志包含每次销售的用户ID,时间戳,物品和带入件数。 商店正在寻找销售额同时上升或下降的商品。 寻找最多20个这样的月份的情侣。

Input:
key: user id,

输出:
键:项目,项目
价值:两个项目的销售额上升或下降的月数。 #:输出包含20个键/值对,最大值

答案是:

我们的解决方案需要多次MapReduce迭代。 我们必须:

  • 计算特定月份的商品销售量是上升还是下降,
  • 创建当月销售额变化相同的商品清单,
  • 在这些列表中找到并发次数,
  • 选择同时出现次数最多的项目。

第一次迭代计算任何给定月份的销售变化。 我们必须提供映射器,分区器,自定义排序和归约器。 映射器为每个输入键/值生成一个中间键/值对。 密钥由已售商品和销售月份组成。 价值包含已售件数。

分区程序将具有相同项目的所有键/值对发送到相同的化简器。 自定义排序按月对它们进行排序。 最后,reducer计算销售变化。

Input:
key: user id,

中间键/值:
键:项目,月份
值:计数。

输出:
键:月,上/下/等
值:项目。

class MAPPER
  method MAP(user_id, (timestamp, item, count))
    month = get_month(timestamp) 
    emit((item, month), count)

class PARTITIONING_COMPARATOR
  method compare(key (item1, month1), key (item2, month2))
    if item1 = item2 
      return keys are equal

    return keys are different

class SORTING_COMPARATOR
  method compare(key (item1, month1), key (item2, month2))
    if item1 = item2 do
      return compare(month1, month2)
         
    return compare(item1, item2)

class REDUCER
  method REDUCE((item, month), counts[c1, c2, ..., cn])
    c = sum([c1, c2, ..., cn])
    if last_item = item
      if last_month + 1 = month
        //emit correct up/down/equal flags
        if last_count < count
          emit((item, month), up)
        if last_count > count
          emit((item, month), down)
        if last_count = count
          emit((item, month), equal)
      else
        //no sales during some months
        emit((item, last_month + 1), down)
        emit((item, month), up)
    else 
      // new item
      emit((last_item, last_month + 1), down)
      emit((item, month), up)

    last_item = item
    last_count = count
    last_month = month

第二次迭代通过键将第一次迭代结果分组。 它生成当月具有相同销售变化的物料清单。 框架可以完成所有工作。 映射器和缩减器均执行标识功能。

Input:
key: month, up/down/equal

输出:
键:月,上/下/等
值:[项目]。 第三迭代执行标准的“成对共现”算法。

Input:
key: month, up/down/equal

中间键/值:
键:项目,项目
值:同时出现的部分次数。

输出:
键:项目,项目
价值:两个项目的销售额上升或下降的月数。 #:输出包含所有项目对

class MAPPER
  method MAP((month, change), items[i1, i2, ..., in])
    for each i in items do 
      for each j in items do
        if i != j 
          emit((i, j), 1) 

class COMBINER
  method COMBINE((item1, item2), co-occurrences[c1, c2, ..., cn])
    s = 0
    for all c in co-occurrences[c1, c2, ..., cn] do 
      s = s + c

    emit((item1, item2), s)

class REDUCER
  method REDUCE((item, item), co-occurrences[c1, c2, ..., cn])
    s = 0
    for all c in co-occurrences[c1, c2, ..., cn] do 
      s = s + c

    emit((item1, item2), s)

最后,我们必须选择20个具有最大值的键/值对。 每个映射器选择具有最大值的20个键/值对,并使用相同的键发出它们。 只有一个精简器可以选择最后的20个键/值对。

Input:
key: item, item
#: the output contains all items couples

中间键/值:
密钥:1
值:项目,项目,两个项目的销售额增加或减少的月数。 #:输出包含20个键/值对,每个映射器都有最大值

输出:
键:项目,项目
价值:两个项目的销售额上升或下降的月数。 #:输出包含20个键/值对,最大值

the code is very simple but long

刑事机构

本章所有练习的输入都使用相同的数据结构。

犯罪机构偷走了Facebook的友谊数据库,并希望分析新数据。 友谊以键/值对的形式存储,每个友谊对应于两个键/值对:

Friends:
key: first friend name

键:第二个朋友的名字
值:第一个朋友的名字
该机构还拥有所有公民的犯罪记录:

Criminal record:
key: citizen name

寻找处于危险中的年轻人。 如果一个人的朋友中有一半以上有犯罪记录,则该人被视为处于危险中的年轻人。

答案是:

我们的解决方案有两个迭代。 第一次迭代将连接两个集合,并使用has_record / law_abiding标志标记每个“有价值的朋友”。

Output:
key: first friend

映射器用数据集名称标记每个键。 分区程序根据密钥中的名称对数据进行分组,而排序程序则将犯罪记录放在友谊之前。 我们可以使用本地汇总来删除同一个人的多个犯罪记录。

class MAPPER
  method MAP(name, value)
    if value is name do
      emit(name, friendship, item)
    else
      emit(name, criminal, item)

class PARTITIONING_COMPARATOR
  method compare(key (name1, dataset1), key (name2, dataset2))
    if name1 = name2
      return keys are equal
 
    return keys are different
 
class SORTING_COMPARATOR
  method compare(key (name1, dataset1), key (name2, dataset2))
    if name1 = name2 AND dataset1 is criminal
      return key1 is lower

    if name1 = name2 AND dataset2 is criminal
      return key2 is lower

    return compare(name1, name2)

class REDUCER
  variable previous_name

  method REDUCE(pair(name, flag), items[i1, i2, ..., in])
    if flag is criminal do 
      previous_name = name
      has_record = criminal
      return 

    if previous_name <> name do 
      has_record = law_abiding
    else 
      has_record = criminal

    previous_name = name
    for all i in items do
      emit(i.name, pair(name, has_record))

第二次迭代同时统计朋友的总数和具有犯罪记录的朋友的数量。 Reducer仅针对有风险的年轻人发出键/值对。 同样,此迭代可以使用某种本地聚合。

Intermediate key/value:
key: name
# totals are relative only to in data sets subsets

输出:
键:名称
值:空 #只对有风险的年轻人

class MAPPER
  method MAP(name, pair(name, has_record))
    if has_record is law_abiding do
      emit(name, pair(0, 1))
    else
      emit(name, pair(1, 1))

class REDUCER
  method REDUCE(name, items[pair(total, criminals)])
    total = 0
    criminals = 0
    for all i in items do
      total = total + i.total
      criminals = criminals + i.criminals

    if criminals / total > 0.5 do
      emit(name, empty)

查找帮派。 帮派是一群人:

  • 有5位成员,
  • 每个成员都是其他成员的朋友,
  • 每两名成员共同犯下至少3项罪行。
答案是:

同样,我们需要三个迭代。 这个想法是首先清理所有无用边缘的图形,以便仅保留犯罪联系。 然后,我们将图分成较小的可管理子图。 我们将每个人之间的所有犯罪联系和边缘联系在一起:

Last iteration reducers input:
key: person

最终化简器采用每个键/值对中由值表示的较小图,并找到其中具有4个顶点的完整子图。 从其中的关键点添加一个人,您已经找到了具有5个顶点的完整子图。 约简器可以使用任何多项式算法。

第一次迭代使用对方法来清除图形。 我们省略了本地聚合和重复项的删除。 两者都会使算法更有效。

Intermediate key/values:
key: first friend, second friend, friendship/accomplice

输出:
关键:第一位朋友,第二位朋友
值:空 #拥有共同犯罪记录的唯一朋友

class MAPPER
  method MAP(name, value)
    if value is name do
      emit(triple(name, value, friendship), empty)
    else
      for all crime_accomplice in value.accomplices do
        emit(triple(name, crime_accomplice, accomplice), 1)

class PARTITIONING_COMPARATOR
  method compare(key (name1, accomplice1, flag1), key (name2, accomplice2, flag2))
    if name1 = name2 AND accomplice1 = accomplice2
      return keys are equal
 
    return keys are different
 
class SORTING_COMPARATOR
  method compare(key (name1, accomplice1, flag1), key (name2, accomplice2, flag2))
    if name1 = name2 AND accomplice1 AND flag1 is friendship
      return key1 is lower

    if name1 = name2 AND accomplice1 AND flag2 is friendship
      return key2 is lower

    return compare(pair(name1, accomplice1), pair(name2, accomplice2))

class REDUCER
  variable previous_name
  variable previous_accomplice

  method sameAsPrevious(name, accomplice) 
    if previous_name <> name
      return false

    if previous_accomplice <> accomplice
      return false

    return true

  method REDUCE(triple(name, accomplice, flag), items[i1, i2, ..., in])
    if sameAsPrevious(name, accomplice) do 
      if items.length > 2 do 
        emit(name, accomplice)
      return

    if flag is friendship do 
      previous_name = name
      previous_accomplice = accomplice

第二次迭代将所有“第二学位”朋友的列表附加到边缘:

Input
key: first friend, second friend
Intermediate key/values:

关键:第二个朋友
值:第一个朋友,第二个朋友

输出:
关键:第一位朋友,第二位朋友
值:第二个朋友的所有朋友

关键:第二个朋友,第一个朋友
值:第一个朋友的所有朋友

class MAPPER
  method MAP((first friend, second friend), empty)
    emit(first friend, (first friend, second friend))
    emit(second friend, (first friend, second friend))

class REDUCER
  method REDUCE(name, edges[e1, e2, ..., en])
    friends = new Set
    friends.add(name)

    for all edge in edges do
      friends.add(edge.v1, edge.v2)

    for all edge in edges do
       emit(edge, friends)

最后,mapper和shuffle以及sort阶段一起生成任何给定人员的所有朋友以及他们之间的关系的列表。

Input
key: friend 1, friend 2

中间键/值:
关键:朋友1
值:朋友2,朋友2的所有朋友

减速器输入(经过随机排序后):
关键人物
价值观:他所有的朋友和他们之间的关系。

输出:
关键:第一位朋友,第二位朋友,第三位朋友,第四位朋友,第五位朋友
值:帮派

class MAPPER
  method MAP((friend , friend 2), all friends of second friend)
    emit(friend 1, (friend 2, all friends of friend 2))

class REDUCER
  method REDUCE(name, graph attached to it)
    any polynomial algorithm will work

参考:我们的JCG合作伙伴 Maria Jurcovicova在This is Stuff博客上的MapReduce问答


翻译自: https://www.javacodegeeks.com/2012/05/mapreduce-questions-and-answers-part-2.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值