【问题标题】:Split list of dictionaries in separate lists based primarily on list size but secondarily based on condition主要根据列表大小但其次根据条件在单独列表中拆分字典列表
【发布时间】:2022-11-12 18:10:34
【问题描述】:

我目前有一个看起来像这样的目录列表:

total_list = [{'email': 'usera@email.com',
               'id': 1,
               'country': 'UK',
               },
               {'email': 'userb@email.com',
                'id': 2,
                'country': 'UK',
               },
               {'email': 'usera@email.com',
                'id': 1,
                'country': 'Germany',
                },
               {'email': 'userc@email.com',
                'id': 3,
                'country': 'Italy',
                },
               {'email': 'userd@email.com',
                'id': 4,
                'country': 'France',
                },
               {'email': 'userc@email.com',
                'id': 3,
                'country': 'Netherland',
                },....
             ]

我想主要根据大小对其进行拆分,所以假设新的大小列表是每个列表 3 个项目,但我还想确保所有相同的用户都在同一个新的子列表中。

所以我试图创建的结果是:

list_a = [{'email': 'usera@email.com',
               'id': 1,
               'country': 'UK',
               },
               {'email': 'userb@email.com',
                'id': 2,
                'country': 'UK',
               },
               {'email': 'usera@email.com',
                'id': 1,
                'country': 'Germany',
                },
             ]

list_b = [{'email': 'userc@email.com',
           'id': 3,
           'country': 'Italy',
           },
           {'email': 'userd@email.com',
            'id': 4,
             'country': 'France',
            },
            {'email': 'userc@email.com',
             'id': 3,
             'country': 'Netherland',
             },....
          ]

显然,在我提供的示例中,用户在列表中的位置非常接近,但实际上他们可以分散得更多。 我正在考虑根据电子邮件对列表进行排序然后拆分它们,但我不确定如果应该组合在一起的项目恰好位于 主列表将被划分。

到目前为止,我尝试过的是:

def list_splitter(main_list, size):
    for i in range(0, len(main_list), size):
        yield main_list[i:i + size]

# calculating the needed number of sublists
max_per_batch = 3
number_of_sublists = ceil(len(total_list) / max_per_batch)

# sort the data by email
total_list.sort(key=lambda x: x['email'])

sublists = list(list_splitter(main_list=total_list, size=max_per_batch))

问题是用这个逻辑我不能 100%ensure如果有任何具有相同电子邮件值的项目,它们将最终出现在相同的子列表中。由于排序,很可能会发生这种情况,但不确定。 基本上我需要一种方法来确保具有相同email 的项目将始终位于同一子列表中,但拆分的主要条件是子列表大小。

有任何想法吗?

【问题讨论】:

  • 您忘记包含解决此问题的尝试。
  • 只是把它做得不好,然后改进它。查看如何创建minimal reproducible exampleedit 问题。
  • 我的错,现在添加
  • 每个子列表是否需要相同的大小?或者特定的子列表可以更小吗?据推测,email 用户的数量将始终小于子列表的大小。如果是这样,这听起来像是 Bin packing problem 的变体。另见:bin packing slides
  • 你说——I also want to make sure that all the same users will be in the same new sublist.——usera@email.comuserb@email.com有什么关系?

标签: python list directory


【解决方案1】:

我会考虑使用队列或 fifo 类型并弹出元素以供使用,而不是将字典保存在列表中。但是使用你所拥有的你可以先创建一个新的排序列表然后做你正在做的事情(有点),或者这是另一个解决方案,因为有很多解决方案可以以任何可以想象的方式组织数据(事实上,你的约束是不同的你想为每个输出对象分配一个变量名吗?我会忽略那部分):

  1. 创建一个 str:list 类型的字典 D,其中您的键是用户电子邮件,该列表是来自 total_list 的所有字典条目的列表,最初是空的 []。如果您有很多数据,排队/生成器会更好,但关键是您的过滤/格式化输入。
  2. 将您的total_list 解析为 D,因此每次点击相同的用户电子邮件时,您都会将该字典附加到该键的值列表中。 total_list 可以删除。
  3. 现在解析 D,用字典列表形成输出列表(或生成器),每个列表限制为 3 个字典。这可能是一个类似于您现在拥有的发电机。

【讨论】:

    【解决方案2】:

    该解决方案首先仅处理所有电子邮件的列表。然后根据邮件的频率和limit 根据组大小对电子邮件进行分组。稍后,剩余的数据,即idcountry,将重新加入电子邮件组。

    第一个函数create_groups 处理电子邮件列表。它计算每封电子邮件的出现次数并将它们分组。每个新组都从最常用的电子邮件开始。如果组中还有剩余空间,它会寻找最常见的也适合该组的空间。如果存在这样的项目,则将其添加到组中。

    重复此操作,直到组满为止;然后,一个新的组开始。

    from operator import itemgetter
    from itertools import groupby, chain
    from collections import Counter
    
    
    def create_groups(items, group_size_limit):
        # Count the frequency of all items and create a list of items 
        # sorted by descending frequency
        items_not_grouped = Counter(items).most_common()
        groups = []
    
        while items_not_grouped:
            # Start a new group with the most frequent ungrouped item
            item, count = items_not_grouped.pop(0)
            group, group_size = [item], count
            while group_size < group_size_limit:
                # If there is room left in the group, start looking for a new group member
                for index, (candidate, candidate_count) in enumerate(items_not_grouped):
                    if candidate_count <= group_size_limit - group_size:
                        # If the candidate fits, add it to the group
                        group.append(candidate)
                        group_size += candidate_count
                        # ... and remove it from the items not grouped
                        items_not_grouped.pop(index)
                        break
                else:
                    # If the for loop did not break, no items fit in the group
                    break
    
            groups.append(group)
    
        return groups
    

    这是在您的示例中使用该函数的结果:

    users = [
        {'email': 'usera@email.com', 'id': 1, 'country': 'UK',},
        {'email': 'userb@email.com', 'id': 2, 'country': 'UK'},
        {'email': 'usera@email.com', 'id': 1, 'country': 'Germany'},
        {'email': 'userc@email.com', 'id': 3, 'country': 'Italy'},
        {'email': 'userd@email.com', 'id': 4, 'country': 'France'},
        {'email': 'userc@email.com', 'id': 3, 'country': 'Netherland'}
    ]
    
    emails = [user["email"] for user in users]
    email_groups = create_groups(emails, 3)
    # -> [['usera@email.com', 'userb@email.com'], ['userc@email.com', 'userd@email.com']]
    
    

    最后,创建组后,join_data_on_groups 函数将原始用户字典分组。它以之前的电子邮件组和字典列表作为参数:

    def join_data_on_groups(groups, item_to_data):
        item_to_data = {item: list(data) for item, data in item_to_data}
    
        groups = [(item_to_data[item] for item in group) for group in groups]
        groups = [list(chain(*group)) for group in groups]
    
        return groups
    
    
    email_getter = itemgetter("email")
    users_grouped_by_email = groupby(sorted(users, key=email_getter), email_getter)
    
    user_groups = join_data_on_groups(email_groups, users_grouped_by_email)
    
    print(user_groups)
    

    【讨论】:

      猜你喜欢
      • 2021-05-21
      • 2021-07-24
      • 1970-01-01
      • 2010-10-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多