pyspark:根据另一个RDD的某些列过滤一个RDD

时间:2017-01-31 22:07:19

标签: python apache-spark mapreduce pyspark

我在火花群集中有两个文件,foo.csvbar.csv,两个文件都有4列且具有相同的确切字段: time, user, url, category

我想按foo.csv的某些列过滤掉bar.csv。最后,我想要(用户,类别)的键/值对:[list,of,urls]。例如:

foo.csv:
11:50:00, 111, www.google.com, search
11:50:00, 222, www.espn.com, news
11:50:00, 333, www.reddit.com, news
11:50:00, 444, www.amazon.com, store
11:50:00, 111, www.bing.com, search
11:50:00, 222, www.cnn.com, news
11:50:00, 333, www.aol.com, news
11:50:00, 444, www.jet.com, store
11:50:00, 111, www.yahoo.com, search
11:50:00, 222, www.bbc.com, news
11:50:00, 333, www.nytimes.com, news
11:50:00, 444, www.macys.com, store

bar.csv:
11:50:00, 222, www.bbc.com, news
11:50:00, 444, www.yahoo.com, store

应该导致:

{
(111, search):[www.google.com, www.bing.com, www.yahoo.com],
(333, news): [www.reddit.com, www.aol.com, www.nytimes.com]
}

换句话说,如果bar.csv中存在(用户,类别)对,我想在foo.csv中过滤掉所有行,如果他们有相同的话确切的(用户,类别)对。因此,在上面的示例中,我想删除foo.csv(222, news)(444, store)中的所有行。最后,在删除我想要的行之后,我想要一个包含键/值对的字典,如:(user, category): [list, of, urls]

这是我的代码:

fooRdd = sc.textFile("file:///foo.txt/")
barRdd = sc.textFile("file:///bar.txt/")


parseFooRdd= fooRdd.map(lambda line: line.split(", "))
parseBarRdd = barRdd.map(lambda line: line.split(", "))



# (n[1] = user_id, n[3] = category_id) --> [n[2] = url]
fooGroupRdd = parseFooRdd.map(lambda n: ((n[1], n[3]), n[2])).groupByKey().map(lambda x: {x[0]: list(x[1])})
barGroupRdd = parseBarRdd.map(lambda n: ((n[1], n[3]), n[2])).groupByKey().map(lambda x: {x[0]: list(x[1])})

以上代码可以使用我想要的格式获取数据集:

(user_id, category): [all, urls, visited, by, user, in, that, category]

然而,几个问题:1)我认为它返回一个只有一个k / v对的字典列表和2)我坚持下一步该做什么。我知道用英语做什么:在barGroupRdd(元组)中获取密钥,并删除fooGroupRdd中具有相同密钥的所有行。但我是pyspark的新手,我觉得有些命令我没有利用。我认为我的代码可以优化。例如,我认为我不需要创建barGroupRdd行,因为bar.csv所需要的只是(user_id,category) - 我不需要创建字典。我还认为我应该首先过滤掉,然后然后从结果中创建字典。感谢任何帮助或建议,谢谢!

1 个答案:

答案 0 :(得分:2)

你真的很亲密。

而不是每个RDD:

fooGroupRdd = parseFooRdd.map(lambda n: ((n[1], n[3]),\
    n[2])).groupByKey().map(lambda x: {x[0]: list(x[1])})

这样做:

fooGroupRdd = parseFooRdd.map(lambda n: ((n[1], n[3]),\
    n[2])).groupByKey().map(lambda x: [(x[0]), list(x[1])])

这样您就可以使用rdd.keys()方法实际访问密钥并创建bar_keys列表。

bar_keys = barGroupRdd.keys().collect()

然后你可以完全按照你说的做。过滤fooGroupRdd中具有bar_keys中键的行。

dict(fooGroupRdd.filter(lambda x: x[0] not in bar_keys)\
    .map(lambda x: [x[0], x[1]]).collect())

最终结果如下:

{('111', 'search'): ['www.google.com', 'www.bing.com', 'www.yahoo.com'],
 ('333', 'news'): ['www.reddit.com', 'www.aol.com', 'www.nytimes.com']}

希望有所帮助。

根据你的评论,我也想知道这是否是最有效的方法。查看RDD的类方法,您会发现collectAsMap()的工作方式类似于collect,但返回字典而不是列表。但是,在对源代码进行调查后,该方法完全按照我的方式执行,因此看起来这是最佳选择。