【问题标题】:Merging arrays based on duplicate values on another array in python?基于python中另一个数组上的重复值合并数组?
【发布时间】:2020-11-21 20:45:12
【问题描述】:

我已将数据整理到 3 个列表中。第一个只包含浮点数,其中一些是重复的。第二个和第三个列表包含可变长度的一维数组。

第一个列表已排序,所有列表包含相同数量的元素。

整体格式是这样的:

a = [1.0, 1.5, 1.5, 2 , 2]
b = [arr([1 2 3 4 10]), arr([4 8 10 11 5 6 12]), arr([1 5 7]), arr([70 1 2]), arr([1])]
c = [arr([3 4 8]), arr([5 6 12]), arr([6 7 10 123 14]), arr([70 1 2]), arr([1 5 10 4])]

如果列表a 中对应的浮点数相同,我正在尝试找到一种方法来合并列表bc 中的数组。对于上面的示例,期望的结果是:

a = [1.0, 1.5, 2]
b = [arr([1 2 3 4 10]), arr([4 8 10 11 5 6 12 1 5 7]), arr([70 1 2 1])]
c = [arr([3 4 8]), arr([5 6 12 6 7 10 123 14]), arr([70 1 2 1 5 10 4]])]

我该怎么做呢?跟zip有关系吗?

【问题讨论】:

  • a 总是排序吗?
  • 是的。在添加过程之前,我添加了一个步骤,将数组压缩在一起并根据 a 元素的顺序对它们进行排序

标签: python arrays list numpy merge


【解决方案1】:

由于a 已排序,我将使用itertools.groupby。类似于@MadPhysicist 的答案,但遍历列表的zip

import numpy as np
from itertools import groupby

arr = np.array

a = [1.0, 1.5, 1.5, 2 , 2]
b = [arr([1, 2, 3, 4, 10]), arr([4, 8, 10, 11, 5, 6, 12]), arr([1, 5, 7]), arr([70, 1, 2]), arr([1])]
c = [arr([3, 4, 8]), arr([5, 6, 12]), arr([6, 7, 10, 123, 14]), arr([70, 1, 2]), arr([1, 5, 10, 4])]

res_a, res_b, res_c = [], [], []
for k, g in groupby(zip(a, b, c), key=lambda x: x[0]):
    g = list(g)
    res_a.append(k)
    res_b.append(np.concatenate([x[1] for x in g]))
    res_c.append(np.concatenate([x[2] for x in g]))

..输出res_ares_bres_c为:

[1.0, 1.5, 2]
[array([ 1,  2,  3,  4, 10]), array([ 4,  8, 10, 11,  5,  6, 12,  1,  5,  7]), array([70,  1,  2,  1])]
[array([3, 4, 8]), array([  5,   6,  12,   6,   7,  10, 123,  14]), array([70,  1,  2,  1,  5, 10,  4])]

如果a没有排序,你可以使用defaultdict

import numpy as np
from collections import defaultdict

arr = np.array

a = [1.0, 1.5, 1.5, 2 , 2]
b = [arr([1, 2, 3, 4, 10]), arr([4, 8, 10, 11, 5, 6, 12]), arr([1, 5, 7]), arr([70, 1, 2]), arr([1])]
c = [arr([3, 4, 8]), arr([5, 6, 12]), arr([6, 7, 10, 123, 14]), arr([70, 1, 2]), arr([1, 5, 10, 4])]

res_a, res_b, res_c = [], [], []

d = defaultdict(list)

for x, y, z in zip(a, b, c):
    d[x].append([y, z])

for k, v in d.items():
    res_a.append(k)
    res_b.append(np.concatenate([x[0] for x in v]))
    res_c.append(np.concatenate([x[1] for x in v]))

【讨论】:

    【解决方案2】:

    编辑:@Austin 和@Mad Physicist 的上述解决方案更好,所以最好使用它们。我正在重塑自行车,这不是 Python 的方式。

    我认为修改原始数组是危险的,尽管这种方法使用了两倍的内存,但以这种方式迭代和执行操作是安全的。 发生了什么:

    1. 遍历a 并在a 的其余部分搜索索引出现(我们 通过remove(i)排除当前值
    2. 如果没有重复,则照常复制bc
    3. 如果有,则合并到临时列表中,然后将其附加到a1b1c1。阻止值,以便重复值不会触发另一个值 合并。在开头使用 if 我们可以检查 value 是否被阻止
    4. 返回新列表 虽然我使用了np.where,但我没有打扰 np 数组,因为它比使用列表推导要快一点。随意编辑数据格式等,我的很简单用于演示目的。
    import numpy as np
    a = [1.0, 1.5, 1.5, 2, 2]
    b = [[1, 2, 3, 4, 10], [4, 8, 10, 11, 5, 6, 12], [1, 5, 7], [70, 1, 2], [1]]
    c = [[3, 4, 8], [5, 6, 12], [6, 7, 10, 123, 14], [70, 1, 2], [1, 5, 10, 4]]
    def function(list1, list2, list3):
        a1 = []
        b1 = []
        c1 = []
        merged_list = []
        # to preserve original index we use enumerate
        for i, item in enumerate(list1):
            # to aboid merging twice we just exclude values from a we already checked
            if item not in merged_list:
                list_without_elem = np.array(list1)
                ixs = np.where(list_without_elem == item)[0].tolist() # removing our original index
                ixs.remove(i)
                # if empty append to new list as usual since we don't need merge
                if not ixs:
                    a1.append(item)
                    b1.append(list2[i])
                    c1.append(list3[i])
                    merged_list.append(item)
                else:
                    temp1 = [*list2[i]] # temp b and c prefilled with first b and c
                    temp2 = [*list3[i]]
                    for ix in ixs:
                        [temp1.append(item) for item in list2[ix]]
                        [temp2.append(item) for item in list3[ix]]
                    a1.append(item)
                    b1.append(temp1)
                    c1.append(temp2)
                    merged_list.append(item)
        print(a1)
        print(b1)
        print(c1)
    
    # example output
    # [1.0, 1.5, 2]
    # [[1, 2, 3, 4, 10], [4, 8, 10, 11, 5, 6, 12, 1, 5, 7], [70, 1, 2, 1]]
    # [[3, 4, 8], [5, 6, 12, 6, 7, 10, 123, 14], [70, 1, 2, 1, 5, 10, 4]]
    

    【讨论】:

      【解决方案3】:

      由于a 已排序,您可以在列表中的索引范围上使用itertools.groupby,以a 为键:

      from itertools import groupby
      
      result_a = []
      result_b = []
      result_c = []
      
      for _, group in groupby(range(len(a)), key=a.__getitem__):
          group = list(group)
          index = slice(group[0], group[-1] + 1)
          result_a.append(k)
          result_b.append(np.concatenate(b[index]))
          result_c.append(np.concatenate(c[index]))
      

      group 是一个迭代器,因此您需要使用它来获取它所代表的实际索引。每个group 包含与list_a 中相同值对应的所有索引。

      slice(...) 是在索引表达式中有: 时传递给list.__getitem__ 的内容。 index 等价于 group[0]:group[-1] + 1]。这会切出列表中与 list_a 中的每个键对应的部分。

      最后,np.concatenate 只是将您的数组分批合并在一起。

      如果您想在不使用list(group) 的情况下执行此操作,则可以以其他方式使用迭代器,而无需保留值。例如,您可以让groupby 为您做这件事:

      from itertools import groupby
      
      result_a = []
      result_b = []
      result_c = []
      
      prev = None
      
      for _, group in groupby(range(len(a)), key=a.__getitem__):
          index = next(group)
          result_a.append(k)
          if prev is not None:
              result_b.append(np.concatenate(b[prev:index]))
              result_c.append(np.concatenate(c[prev:index]))
          prev = index
      
      if prev is not None:
          result_b.append(np.concatenate(b[prev:]))
          result_c.append(np.concatenate(c[prev:]))
      

      那时,您甚至都不需要使用groupby,因为自己跟踪所有内容不会有太多工作:

      result_a = []
      result_b = []
      result_c = []
      
      k = None
      
      for i, n in enumerate(a):
          if n == k:
              continue
          result_a.append(n)
          if k is not None:
              result_b.append(np.concatenate(b[prev:i]))
              result_c.append(np.concatenate(c[prev:i]))
          k = n
          prev = index
      
      if k is not None:
          result_b.append(np.concatenate(b[prev:]))
          result_c.append(np.concatenate(c[prev:]))
      

      【讨论】:

      • 如果不是groupby,我想defaultdict 会是更好的选择。你怎么看?
      • @Austin。你打算如何使用defaultdict?我添加了一个不使用groupby 的解决方案。这只是一个正常簿记的循环......
      • 我刚刚对我的答案进行了编辑以包括defaultdict。对于我能想到的大多数情况,如果我们可以使用groupby,则有defaultdict 方式。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-06-19
      • 2019-04-08
      • 2018-02-28
      • 2019-04-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多