比较两个字典列表中值的Pythonic方式

3

我是Python的新手,仍在努力摆脱C++编程技术,因此如果这是一个微不足道的问题,请原谅我。我似乎找不到最Pythonic的方法来做到这一点。

我有两个字典列表。两个列表中的每个字典都可能包含嵌套的字典。(如果您感兴趣,实际上是一些Yelp数据。)第一个字典列表包含以下条目:

{business_id': 'JwUE5GmEO-sH1FuwJgKBlQ',
 'categories': ['Restaurants'],
 'type': 'business'
 ...}

第二个字典列表包含以下条目:
{'business_id': 'vcNAWiLM4dR7D2nwwJ7nCA',
 'date': '2010-03-22',
 'review_id': 'RF6UnRTtG7tWMcrO2GEoAg',
 'stars': 2,
 'text': "This is a basic review",
 ...}

我想要做的是从第二个列表中提取出与第一个列表中特定类别匹配的所有条目。例如,如果我对餐馆感兴趣,我只想要第二个列表中的条目,其中business_id与第一个列表中的business_id匹配,并且单词Restaurants出现在categories值列表中。
如果我在SQL中将这两个列表作为表格,我会在business_id属性上执行连接,然后只需简单过滤以获取所需行(其中Restaurants IN categories或类似内容)。
这两个列表非常大,因此我遇到了效率和内存空间问题。在将所有内容放入SQL数据库之前,有人能给我一些指导吗?我已经使用Pandas进行了一些尝试,因此我在这方面有一些有限的经验。我在合并过程中遇到了一些问题。
6个回答

2
假设您的列表分别称为l1l2

l1中的所有元素:

[each for each in l1]

所有属于 Restaurant 类别的 l1 元素:

[each for each in l1
      if 'Restaurants' in each['categories']]

l2中匹配与l1中类别为餐厅的元素相同id的所有元素:

[x for each in l1 for x in l2 
   if 'Restaurants' in each['categories']
   and x['business_id'] == each['business_id'] ]

谢谢!我真的很喜欢你如何分解列表理解式。这是在Python中需要我一段时间才能完全理解的事情之一。 - TheOriginalBMan
@TheOriginalBMan,很高兴能帮忙! - elyase

2
让我们定义一些字典列表示例:
first = [
        {'business_id':100, 'categories':['Restaurants']},
        {'business_id':101, 'categories':['Printer']},
        {'business_id':102, 'categories':['Restaurants']},
        ]

second = [
        {'business_id':100, 'stars':5},
        {'business_id':101, 'stars':4},
        {'business_id':102, 'stars':3},
        ]

我们可以分两步提取感兴趣的项目。第一步是收集属于餐厅的业务id列表:
ids = [d['business_id'] for d in first if 'Restaurants' in d['categories']]

第二步是获取与这些ID相对应的字典:
[d for d in second if d['business_id'] in ids]

这将导致:
[{'business_id': 100, 'stars': 5}, {'business_id': 102, 'stars': 3}]

1

这很棘手,但我很喜欢它。以下是我的做法:

def match_fields(business, review):
    return business['business_id'] == review['business_id'] and 'Restaurants' in business['categories']

def search_businesses(review):
    # the lambda binds the given review as an argument to match_fields
    return any(lambda business: match_fields(business, review), business_list)

answer = filter(search_businesses, review_list)

这是我找到的最易读的方法。我不是特别喜欢超过一行的列表推导式,而三行则有些太多了。如果你希望看起来更简洁,只需使用更短的变量名称。我偏爱长名称以便于理解。
我定义了一个函数,如果能在列表之间匹配条目,则返回true;还定义了第二个函数,帮助我在评论列表中进行搜索。然后我可以说:摆脱任何没有在商家列表中找到匹配条目的评论。对于列表之间的任意检查,这种模式都很有效。

我也喜欢这个。作为面向对象编程和函数式编程背景的人,这绝对容易理解。谢谢你! - TheOriginalBMan
@TheOriginalBMan,你需要知道在Python中,列表推导式被认为是首选而非map函数。有人可能会说这是实现函数式编程的Pythonic方式。当然这只是主观看法,在某些情况下使用map也是合理的。 - elyase
@elyase 在Python中,没有一种语言特性适用于所有情况。在这种情况下,列表推导需要执行大量逻辑,并且不太易读,正如我们在上面的答案中所看到的那样。“实用性胜过纯粹,可读性至上。”-禅。 - jack
@jack,我同意你的第一点,这就是为什么我写“在某些情况下可能是合理的”的原因。我不同意我的解决方案有更多逻辑,以及什么更易读,这是主观的,我尊重你看法不同,但我刚向我的女友(非程序员,文学背景)展示了两种方案,她告诉我她不知道你的解决方案做了什么,而我的则像一句话一样可读。这正是我所看到的。 - elyase
@elyase 我并不是说你的解决方案比我的需要更多的逻辑,只是把同样的逻辑放在列表推导式中会变得非常拥挤。此外:向非开发人员展示您的代码意义不大,因为您实质上只是比较了两个语言特性的外观,而不考虑应用程序。'filter'和'any'之类的内置函数存在有其合理的原因,可以合理地期望其他开发人员知道它们。 - jack

1
Python程序员喜欢使用列表推导式作为同时进行逻辑和设计的方法。
列表推导式可以使表达更加简洁紧凑。您可以将其视为查询语言。
x = [comparison(a, b) for (a, b) in zip(A, B)] 
x = [comparison(a, b) for (a, b) in itertools.product(A, B)] 
x = [comparison(a, b) for a in A for b in B if test(a, b)]
x = [comparison(a, b) for (a, b) in X for X in Y if test(a, b, X)]

...这些都是我使用的模式。


1
作为对列表推导式的变化,使用set和生成器推导式可能更有效。特别是如果您的第一个列表非常大或餐厅的总数非常大。
restaurant_ids = set(biz for biz in first if 'Restaurants' in biz['categories'])
restaurant_data = [rest for rest in second if rest['id'] in restaurant_ids]

请注意,暴力列表推导式的时间复杂度为O(len(first)*len(second)),但它不使用额外的内存存储,而这种方法的时间复杂度为O(len(first)+len(second)),并且对于set使用了O(number_of_restaurants)额外的内存。

0
你可以这样做: restaurant_ids = [biz['id'] for biz in list1 if 'Restaurants' in biz['categories']] restaurant_data = [rest for rest in list2 if rest['id'] in restaurant_ids] 然后,restaurant_data 将包含所有包含餐厅数据的 list2 字典。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接