【问题标题】:Pythonic Way to Compare Values in Two Lists of Dictionaries比较两个字典列表中的值的 Pythonic 方法
【发布时间】:2015-03-20 17:42:47
【问题描述】:

我是 Python 新手,在 Python 中仍然试图摆脱 C++ 编码技术,所以如果这是一个微不足道的问题,请原谅我。我似乎找不到最符合 Python 风格的方法。

我有两个字典列表。两个列表中的单个字典可能包含嵌套字典。 (如果你好奇的话,它实际上是一些 Yelp 数据。)第一个 dicts 列表包含如下条目:

{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 数据库之前,谁能给我一些指点?我已经搞砸了一些熊猫,所以我确实有一些有限的经验。我在合并过程中遇到了问题。

【问题讨论】:

    标签: python list python-2.7 dictionary


    【解决方案1】:

    假设您的列表名为l1l2

    来自l1的所有元素:

    [each for each in l1]
    

    来自l1 的所有元素与Restaurant 类别:

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

    来自l2 的所有元素与来自l1 的元素匹配idRestaurant 类别:

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

    【讨论】:

    • 谢谢!我真的很喜欢你如何分解列表理解。这是我需要一段时间才能完全理解 Python 的一件事。
    • @TheOriginalBMan,很高兴为您提供帮助!
    【解决方案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对应的dicts:

    [d for d in second if d['business_id'] in ids]
    

    这会导致:

    [{'business_id': 100, 'stars': 5}, {'business_id': 102, 'stars': 3}]
    

    【讨论】:

      【解决方案3】:

      你可以这样做: 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 中包含餐厅数据的所有字典。

      【讨论】:

        【解决方案4】:

        这很棘手,我玩得很开心。这就是我要做的:

        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,第二个函数可以帮助我搜索评论列表。然后我可以说:删除任何在企业列表中没有匹配条目的评论。这种模式适用于列表之间的任意检查。

        【讨论】:

        • 我也喜欢这个。来自 OOP 和函数式编程背景,这绝对容易理解。谢谢!
        • @TheOriginalBMan,就像你在 Python 列表推导中知道 are preferred 来映射一样,可以说这是 Python 风格的函数式风格。当然,这是主观的,在某些情况下可能是合理的。
        • @elyase Python 中没有任何一种语言特性在所有情况下都是首选的。在这种情况下,列表推导需要做很多逻辑,并且最终不会变得非常可读,正如我们在上面的答案中看到的那样。 '实用胜过纯洁。可读性很重要。 - 禅
        • @jack,同意你的第一点,这就是我写“在某些情况下可能是合理的”的原因。我不同意我的解决方案有更多的逻辑和更具可读性,这是主观的,我尊重你的不同看法,但我刚刚向我的 GF(非开发人员,文学背景)展示了这两种解决方案,她只是告诉我她不知道你的解决方案是做什么的,而我的则是句子。这正是我的看法。
        • @elyase 我并不是说您的解决方案需要比我更多的逻辑,只是将相同的逻辑放入列表理解中会变得非常狭窄。另外:向非开发人员展示您的代码意义不大,因为您实际上只是在比较两种语言功能的外观,而与应用程序无关。 'filter' 和 'any' 是内置的,有充分的理由,可以合理地期望其他开发人员知道它们。
        【解决方案5】:

        Python 程序员喜欢使用list comprehensions 来进行逻辑和设计。

        列表推导导致更简洁和更紧凑的表达。您认为它很像查询语言是对的。

        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)]
        

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

        【讨论】:

          【解决方案6】:

          作为仅列表解析的变体,使用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) 额外内存。

          【讨论】:

            猜你喜欢
            • 2018-07-09
            • 2017-08-21
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2017-12-20
            • 2021-11-08
            • 2023-01-07
            相关资源
            最近更新 更多