Использование Numpy для нахождения комбинации строк в массиве, так что каждый столбец суммирует одно и то же значение - PullRequest
0 голосов
/ 13 мая 2018

Я пытаюсь использовать numpy, чтобы найти конфигурации строк в матрице, чтобы при суммировании столбцов строк получалось одинаковое значение.Например, для матрицы / массива

[[0,0,0,1],
 [1,0,1,0],
 [1,1,0,0],
 [0,1,0,0]]

я хотел бы иметь первую, вторую и последнюю строки в качестве выходных данных, потому что

  0,0,0,1
  1,0,1,0
  0,1,0,0 +
  -------
= 1,1,1,1

Есть ли какие-либо инструменты, встроенные вnumpy что поможет мне в этом?

1 Ответ

0 голосов
/ 13 мая 2018

Одним из решений является перечисление набора мощностей строк, а затем проверка каждого возможного подмножества строк для условия суммирования. Для матриц с большим количеством строк это может быть довольно медленным.

Используйте стандартный рецепт itertools для блока питания:

from itertools import chain, combinations

def powerset(iterable):
    xs = list(iterable)
    return chain.from_iterable(combinations(xs, n) for n in range(len(xs) + 1))

тогда я показываю рабочий пример с некоторыми синтетическими данными:

In [79]: data
Out[79]: 
array([[0, 1, 1],
       [0, 0, 1],
       [1, 0, 1],
       [0, 1, 1],
       [0, 0, 0],
       [0, 1, 0],
       [1, 1, 1],
       [1, 1, 0],
       [1, 1, 1],
       [0, 1, 0]], dtype=int32)

In [80]: def is_constant(array):
    ...:     return (array == array[0]).all()
    ...: 

In [81]: solution = []

In [82]: for candidate in powerset(range(len(data))):
    ...:     if candidate and is_constant(data[candidate, :].sum(axis=0)):
    ...:         solution.append(candidate)
    ...: 

Который показывает, например:

In [83]: solution
Out[83]: 
[(4,),
 (6,),
 (8,),
 (1, 7),
 (2, 5),
 (2, 9),
 (4, 6),
 (4, 8),
 (6, 8),
 (0, 2, 7),
 (1, 4, 7),
 (1, 6, 7),
 (1, 7, 8),
 (2, 3, 7),
 (2, 4, 5),
 (2, 4, 9),
 (2, 5, 6),
 (2, 5, 8),
 (2, 6, 9),
 (2, 8, 9),
 (4, 6, 8),
 (0, 2, 4, 7),
 (0, 2, 6, 7),
 (0, 2, 7, 8),
 (1, 2, 5, 7),
 (1, 2, 7, 9),
 (1, 4, 6, 7),
 (1, 4, 7, 8),
 (1, 6, 7, 8),
 (2, 3, 4, 7),
 (2, 3, 6, 7),
 (2, 3, 7, 8),
 (2, 4, 5, 6),
 (2, 4, 5, 8),
 (2, 4, 6, 9),
 (2, 4, 8, 9),
 (2, 5, 6, 8),
 (2, 6, 8, 9),
 (0, 2, 4, 6, 7),
 (0, 2, 4, 7, 8),
 (0, 2, 6, 7, 8),
 (1, 2, 4, 5, 7),
 (1, 2, 4, 7, 9),
 (1, 2, 5, 6, 7),
 (1, 2, 5, 7, 8),
 (1, 2, 6, 7, 9),
 (1, 2, 7, 8, 9),
 (1, 4, 6, 7, 8),
 (2, 3, 4, 6, 7),
 (2, 3, 4, 7, 8),
 (2, 3, 6, 7, 8),
 (2, 4, 5, 6, 8),
 (2, 4, 6, 8, 9),
 (0, 2, 4, 6, 7, 8),
 (1, 2, 4, 5, 6, 7),
 (1, 2, 4, 5, 7, 8),
 (1, 2, 4, 6, 7, 9),
 (1, 2, 4, 7, 8, 9),
 (1, 2, 5, 6, 7, 8),
 (1, 2, 6, 7, 8, 9),
 (2, 3, 4, 6, 7, 8),
 (1, 2, 4, 5, 6, 7, 8),
 (1, 2, 4, 6, 7, 8, 9)]

и мы можем проверить решение для нескольких из этих случаев:

In [84]: data[(1, 2, 4, 6, 7, 8, 9), :].sum(axis=0)
Out[84]: array([4, 4, 4])

In [85]: data[(0, 2, 4, 6, 7), :].sum(axis=0)
Out[85]: array([3, 3, 3])

Чтобы расширить это для более конкретных случаев использования, вы можете использовать itertools.combinations для создания подмножеств только определенного размера, например, подмножеств ровно 2 строки или ровно 3 строки и т. Д.

Или вы можете просто отфильтровать нежелательные результаты (например, тривиальные решения, состоящие из одной строки за раз) из набора результатов, приведенного в моем примере.

Обратите внимание, что вы можете упростить определение функции powerset (та, которую я использую, буквально взята из документации Python о рецептах itertools). Вместо передачи итерируемой переменной, которая преобразуется в список, вы можете просто передать целое число и сразу пропустить, чтобы вернуть окончательный результат chain.from_iterable, а затем изменить, просто передав len(data) в качестве аргумента powerset в моем примере, как это:

from itertools import chain, combinations

def powerset(N):
    """Power set of integers {0, ..., N-1}."""
    xs = list(range(N))
    return chain.from_iterable(combinations(xs, n) for n in range(N + 1))

...
for candidate in powerset(len(data)):
    ...
...