计算行中参数的Map Reduce,并计数第二个参数

0 投票
1 回答
840 浏览
提问于 2025-04-30 21:28

想象一下,我有一个日志文件,里面满是这样的行:

"a,b,c",其中这些是变量,可以有任何值,但这些值可能会重复出现,这就是我们要分析的内容。

第一步

找出所有'c'的链接,前提是'a'等于一个特定的域名,比如 "stackoverflow.com",而'c'则是像 "stackoverflow.com/test/user/" 这样的链接。我已经写了一个正则表达式来完成这个任务。

第二步

统计所有的c(链接),这样我就能得到每个链接的总数。这一步进行得很顺利。

第三步

(这一步还没实现,也是我提问的主题)

查找每个在第二步中统计到的链接对应的所有b(浏览器名称)。然后返回一个关系列表,比如字典或JSON,格式如下:

[
   {
    "url":Stackoverflow.com/login, 
    "count": 200.654, 
    "browsers":[
      Firefox 33, 
      IE 7, 
      Opera
    ]
   },
   {..},
   {..}
 ],

我在考虑在我的代码中引入一个组合器(见下文),或者把东西串联起来。但真正的问题是,我该如何优化我的工作流程,以便只需遍历所有日志行一次?

MapReduce作业 (mrjob)

FULL_URL_WHERE_DOMAIN_EQUALS = mySuperCoolRegex

class MRReferralAnalysis(MRJob):

    def mapper(self, _, line):

        for group in FULL_URL_WHERE_DOMAIN_EQUALS.findall(line):
            yield (group, 1)

    def reducer(self, itemOfInterest, counts):
        yield (sum(counts), itemOfInterest)

    def steps(self):
        return [
            MRStep( mapper=self.mapper,
                    reducer=self.reducer)
        ]

if __name__ == '__main__':
    MRReferralAnalysis.run()

总结

这就是我想要的伪代码:

LOGS_1 -> MAPREDUCE OVER SOME_CRITERIA -> LIST_1

FOR EVERY ITEM IN LIST_1:
    LOGS_1 -> MAPREDUCE OVER ITEM_CRITERIA -> LIST_2
暂无标签

1 个回答

1

这里有一个非MRJob,非mapreduce的解决方案。它只需遍历日志文件一次。这个方法和你想要的输出格式有点不同,browsers是一个包含(浏览器,计数)元组的列表,而它生成的字典是无序的。你可以用collections.OrderedDict来替代。

假设文件的内容是这样的:

domain,browser,url
wonderful.edu,IE,wonderful.edu/pix
odd.org,Firefox,odd.org/login
wonderful.edu,Opera,wonderful.edu/pix

读取文件后,按域名、网址和浏览器进行排序,以便使用itertools.groupby

import collections, itertools, operator
with open('fake.log') as f:
    lines = [tuple(line.strip().split(',')) for line in f]

lines.sort(key = operator.itemgetter(0,2,1))

这里有一些有用的可调用对象:

domain = operator.itemgetter(0)
browser = operator.itemgetter(1)
url = operator.itemgetter(2)

使用collections.Counter来统计每个唯一网址的浏览器数量。网址的总计数是所有浏览器计数的总和。

results = list()
FULL_URL_WHERE_DOMAIN_EQUALS = re.compile('.*\.(edu|org|com)')
for d, group in itertools.groupby(lines, domain):
    # this outer loop only needed if filtering by domain
    if not FULL_URL_WHERE_DOMAIN_EQUALS.match(d):
        print d
        continue    
    for u, group2 in itertools.groupby(group, url):
        browsers = itertools.imap(browser, group2)
        browser_count = collections.Counter(browsers)
        results.append({'url' : u,
                        'count' : sum(browser_count.viewvalues()),
                        'browsers' : browser_count.items()}
                       )

生成的结果是:

[{'browsers': [('Chrome', 2), ('IE', 4), ('Opera', 7), ('Firefox', 6)],
  'count': 19,
  'url': 'odd.org/foo'},
  {...},
  {...}]

撰写回答