Объединение массивов на основе повторяющихся значений в другом массиве в python? - PullRequest
2 голосов
/ 01 августа 2020

Я организовал свои данные в 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])]

Я пытаюсь найти способ объединить массивы в списках b и c, если их соответствующее число с плавающей запятой совпадает в списке a. Для приведенного выше примера желаемый результат будет:

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

Как мне go это сделать? Это как-то связано с zip?

Ответы [ 3 ]

3 голосов
/ 01 августа 2020

Поскольку 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_a, res_b и res_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]))
1 голос
/ 01 августа 2020

Так как 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 is итератор, поэтому вам нужно использовать его, чтобы получить фактические индексы, которые он представляет. Каждый 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:]))
1 голос
/ 01 августа 2020

EDIT: вышеупомянутые решения от @Austin и @Mad Physicist лучше, поэтому лучше их использовать. Я изобретаю велосипед, который отличается от pythoni c.

Я думаю, что изменение исходных массивов опасно, несмотря на то, что этот подход использует вдвое больше памяти, но безопасно повторять и выполнять операции таким образом. Что происходит:

  1. итерация по a и поиск вхождений индекса в остальной части a (мы исключаем текущее значение remove(i)
  2. , если нет дубликатов, просто скопируйте b и c как обычно
  3. , если есть, затем объедините их во временные списки, затем добавьте его в a1, b1 и c1. Заблокируйте значение, чтобы повторяющееся значение не запускало другое слияние . Используя if вначале, мы можем проверить, заблокировано ли значение.
  4. возвращать новые списки. Я не стал беспокоиться о массивах np, хотя использовал np.where, поскольку это немного быстрее, чем использование списков. для редактирования форматов данных и т.д. c, мои просто для демонстрационных целей.
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]]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...