【问题标题】:In a list of dicts, flag a dict if combination of key/value pairs is identical in another dict在字典列表中,如果键/值对的组合在另一个字典中相同,则标记一个字典
【发布时间】:2022-02-18 20:26:51
【问题描述】:

我有一个字典列表,其中包含 streetnumbersome_flag 键。

我的目标是在字典中搜索键 streetnumber 中的重复项。如果对于两个或多个字典,这两个键/值对是相同的,我想将值 1 分配给它们的 some_flag 键。

请参阅下面的可重现示例。

字典起始列表:

a = [
    {'street': 'ocean drive', 'number': '1', 'some_flag': 0},
    {'street': 'ocean drive', 'number': '3', 'some_flag': 0},
    {'street': 'ocean drive', 'number': '4', 'some_flag': 0}, # duplicate street / number keys
    {'street': 'ocean drive', 'number': '4', 'some_flag': 0}, # duplicate street / number keys
    {'street': 'apple tree rd.', 'number': '3', 'some_flag': 0},
]

预期输出:

a_checked = [
    {'street': 'ocean drive', 'number': '1', 'some_flag': 0},
    {'street': 'ocean drive', 'number': '3', 'some_flag': 0},
    {'street': 'ocean drive', 'number': '4', 'some_flag': 1}, # duplicate street / number keys
    {'street': 'ocean drive', 'number': '4', 'some_flag': 1}, # duplicate street / number keys
    {'street': 'apple tree rd.', 'number': '3', 'some_flag': 0},
]

我的最大努力:

到目前为止,我得到的代码来自 Aarons 的回答 (here) 和社区 wiki 的回答 (here)

from collections import defaultdict, Counter

items = defaultdict(list) # create defaultdict 

for row in a:
    items[row['street']].append(row['number'])  # make a list of 'number' values for each 'street' key


for key in items.keys():
    if checkIfDuplicates(items[key]):  #if there is more than one 'number' --> function definition see below  
        duplicate_dict = {}
        duplicate_dict['numbers'] =  [item for item, count in Counter(items[key]).items() if count > 1] # storing duplicate numbers in dict
        duplicate_dict['street'] = key # storing street name in same dict

检查给定列表是否包含任何重复项的功能(来自here):

def checkIfDuplicates(listOfElems): 
    if len(listOfElems) == len(set(listOfElems)):
        return False
    else:
        return True
        

当前输出:

print(duplicate_dict)
{'numbers': ['4'], 'street': 'ocean drive'}

使用我的方法,我现在必须将duplicate_dict 与原始列表a 进行匹配,这似乎效率不高。

有没有更直接的方法来解决这个问题?

【问题讨论】:

    标签: python list dictionary defaultdict


    【解决方案1】:

    您可以使用dict.setdefault 首先存储列表的字典(其中键是“street”和“number”),然后遍历该字典的值以检查多个字典是否具有相同的“street”和"number" 并修改多个的 "some_flag":

    tmp = {}
    for d in a:
        tmp.setdefault((d['street'], d['number']), []).append(d)
    out = []
    for v in tmp.values():
        if len(v) > 1:
            for d in v:
                d['some_flag'] = 1
        out.extend(v)
    

    输出:

    [{'street': 'ocean drive', 'number': '1', 'some_flag': 0},
     {'street': 'ocean drive', 'number': '3', 'some_flag': 0},
     {'street': 'ocean drive', 'number': '4', 'some_flag': 1},
     {'street': 'ocean drive', 'number': '4', 'some_flag': 1},
     {'street': 'apple tree rd.', 'number': '3', 'some_flag': 0}]
    

    【讨论】:

      【解决方案2】:

      如果字典列表没有排序,那么我们必须检查每对可能的元素是否相等,这将花费O(n^2) 时间。 但是如果列表是排序的,那么我们可以检查每个元素和它的下一个元素,如果它们不相等,那么用下面的元素检查那个元素是没有意义的。另一方面,如果它们相等,我们继续检查下一个,依此类推。

      def is_duplicate(d_1, d_2):
          return d_1['street'] == d_2['street'] and d_1['number'] == d_2['number']
      
      
      def set_duplicate(d_1, d_2):
          d_1['some_flag'] = 1
          d_2['some_flag'] = 1
      
      a = sorted(a, key=lambda k: (k['street'].lower(), k['number']))
      
      cur_index = 0
      while cur_index < len(a):
      
          next_index = cur_index + 1
          while next_index < len(a) and is_duplicate(a[cur_index], a[next_index]):
              set_duplicate(a[cur_index], a[next_index])
              next_index += 1
      
          cur_index += 1
      
      print(a)
      

      【讨论】:

        【解决方案3】:

        可以通过以下方式完成:

        import pandas as pd
        import numpy as np
        # Your code
        a = [
            {'street': 'ocean drive', 'number': '1', 'some_flag': 0},
            {'street': 'ocean drive', 'number': '3', 'some_flag': 0},
            {'street': 'ocean drive', 'number': '4', 'some_flag': 0}, # duplicate street / number keys
            {'street': 'ocean drive', 'number': '4', 'some_flag': 0}, # duplicate street / number keys
            {'street': 'apple tree rd.', 'number': '3', 'some_flag': 0},
        ]
        # My code
        data = pd.DataFrame(a)
        data["some_flag"]= np.where(data.duplicated(keep=False), 1, data["some_flag"])
        data
        

        输出

        street number some_flag
        0 ocean drive 1 0
        1 ocean drive 3 0
        2 ocean drive 4 1
        3 ocean drive 4 1
        4 apple tree rd. 3 0

        如果您对使用字典而不是数据框感兴趣,可以尝试使用to_dict 函数将数据框更改为字典:

        data.to_dict(orient="list")
        

        导致:

        {'number': ['1', '3', '4', '4', '3'],
         'some_flag': [0, 0, 1, 1, 0],
         'street': ['ocean drive',
          'ocean drive',
          'ocean drive',
          'ocean drive',
          'apple tree rd.']}
        

        说明

        在数据帧上使用duplicated 函数并将False 分配给keep 参数,您可以找到重复的行。然后使用where这是一个numpy函数,您可以根据逻辑语句为数组赋值。

        【讨论】:

        • 感谢您分享您的方法,但我需要将结果采用我所写的格式。
        • 格式相同!字典和数据框!
        【解决方案4】:

        您也可以在没有 pandas 的情况下通过更多代码来实现这一点,例如像这样:

        import copy
        
        a = [
            {'street': 'ocean drive', 'number': '1', 'some_flag': 0},
            {'street': 'ocean drive', 'number': '3', 'some_flag': 0},
            {'street': 'ocean drive', 'number': '4', 'some_flag': 0}, # duplicate street / number keys
            {'street': 'ocean drive', 'number': '4', 'some_flag': 0}, # duplicate street / number keys
            {'street': 'ocean drive', 'number': '4', 'some_flag': 0},  # duplicate street / number keys
            {'street': 'ocean drive', 'number': '4', 'some_flag': 0},  # duplicate street / number keys
            {'street': 'apple tree rd.', 'number': '3', 'some_flag': 0},
        ]
        
        result = copy.deepcopy(a) #duplicate a to not override your input
        
        last_entry = ""
        
        for entry in result:
            if last_entry == "": #skip first iteration to have two entries to compare
                last_entry = entry 
                continue
            if last_entry["street"] == entry["street"] and last_entry["number"] == entry["number"]:
                last_entry["some_flag"] = 1 
                entry["some_flag"] = 1
            last_entry = entry
        
        print(result)
        
        

        【讨论】:

        • result = a 不会复制原始列表,而是对它进行新的引用。 (这个概念在 Java 中称为克隆)。另见stackoverflow.com/questions/2612802/…
        • 您正在检查每个项目及其下一个项目,但我认为 OP 应注意,如果字典列表未按其街道和数字值排序,则此方法需要一个排序步骤。
        • 您对我的副本问题完全正确。解决了这个问题。预计数组会像问题中那样排序。也许坚持另一种解决方案,而不是我的
        • 或者我们只是从您的帖子中添加排序解决方案;)
        【解决方案5】:
        du = {}
        for d in a:
            new_key = d['street'] + "_" + d['number']
            if new_key in du.keys():
                du[new_key] = du[new_key] + 1
            else:
                du[new_key] = 1
        
        print(du) 
        # {'ocean drive_1': 1, 'ocean drive_3': 1, 'ocean drive_4': 2, 'apple tree rd._3': 1}
        
        for k, v in du.items():
            if v > 1:
                k1 = k.split("_")[0]
                k2 = k.split("_")[1]
                for d in a:
                    if d['street'] == k1 and d['number'] == k2:
                        d['some_flag'] = 1
        print(a)
        # [{'street': 'ocean drive', 'number': '1', 'some_flag': 0}, {'street': 'ocean drive', 'number': '3', 'some_flag': 0}, {'street': 'ocean drive', 'number': '4', 'some_flag': 1}, {'street': 'ocean drive', 'number': '4', 'some_flag': 1}, {'street': 'apple tree rd.', 'number': '3', 'some_flag': 0}]
        
        

        【讨论】:

          猜你喜欢
          • 2022-11-22
          • 2022-06-23
          • 2021-09-03
          • 2017-07-06
          • 1970-01-01
          • 1970-01-01
          • 2017-08-05
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多