#python #arrays #numpy #matrix
#python #массивы #numpy #матрица
Вопрос:
У меня есть матрица numpy, такая как приведенная ниже. Что я хотел бы сделать, так это получить массивы, содержащие блоки, составляющие каждую строку / столбец здесь. Как это можно эффективно сделать в numpy?
Примеры
Итак, если, например, у нас есть массив [1 1 1 1 0 1 1 1 1 0]
(первая строка), то мы получим [4 4]
, поскольку у нас есть 2 блока по 4.
Для первого столбца мы получим, [3 1]
поскольку в начале у нас есть три 1
-s, за которыми следует ноль, затем один 1
, а затем больше нулей.
Упомянутая матрица
[[1 1 1 1 0 1 1 1 1 0]
[1 0 0 1 0 1 1 1 1 1]
[1 0 1 0 1 0 0 1 0 1]
[0 1 0 0 1 0 1 0 0 0]
[1 1 1 1 1 1 0 1 0 1]
[0 0 1 0 0 1 1 1 0 0]
[0 0 0 0 1 1 0 1 1 0]
[0 0 0 0 0 0 0 0 1 1]
[0 1 0 1 0 1 0 0 0 0]
[0 0 1 0 0 0 1 1 1 0]]
ПРИМЕЧАНИЕ: строки упорядочены слева направо, а столбцы — сверху вниз.
Комментарии:
1. Какой будет строка 2?
[1, 1, 5]
? И строка 3:[1, 1, 1, 1, 1]
?2. @S3DEV да, точно
3. @S3DEV и та же операция должна быть также применена к столбцам (строки упорядочены слева направо, столбцы сверху вниз)
4. ОК. И какова форма / структура данных для вывода, учитывая, что вы ищете результаты строк и столбцов. Возможно, вывод для каждого?
5. @S3DEV Один массив, содержащий несколько массивов, каждый массив имеет форму, описанную здесь (массивы упорядочены по их появлению в матрице, что означает, что массив, описывающий первую строку, находится в нулевой позиции (первой)). То же самое относится и к столбцам.
Ответ №1:
Вот некоторая магия numpy:
a = np.array([[1, 1, 1, 1, 0, 1, 1, 1, 1, 0],
[1, 0, 0, 1, 0, 1, 1, 1, 1, 1],
[1, 0, 1, 0, 1, 0, 0, 1, 0, 1],
[0, 1, 0, 0, 1, 0, 1, 0, 0, 0],
[1, 1, 1, 1, 1, 1, 0, 1, 0, 1],
[0, 0, 1, 0, 0, 1, 1, 1, 0, 0],
[0, 0, 0, 0, 1, 1, 0, 1, 1, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 1, 1],
[0, 1, 0, 1, 0, 1, 0, 0, 0, 0],
[0, 0, 1, 0, 0, 0, 1, 1, 1, 0]])
a_pad = np.zeros((a.shape[0] 2, a.shape[1] 2))
a_pad[1:-1, 1:-1] = a
cols = [np.diff(np.nonzero(c)[0].reshape(-1, 2), axis=1)[:, 0]
for c in np.diff(a_pad, axis=0).T[1:-1]]
# [array([3, 1]), array([1, 2, 1]), array([1, 1, 2, 1]), ...
rows = [np.diff(np.nonzero(r)[0].reshape(-1, 2), axis=1)[:, 0]
for r in np.diff(a_pad, axis=1)[1:-1]]
# [array([4, 4]), array([1, 1, 5]), array([1, 1, 1, 1, 1]), ...
Теперь давайте рассмотрим, что происходит на примере array ( a[5, :]
):
# a [0, 0, 1, 0, 0, 0, 1, 1, 1, 0,]
# pad [0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0]
# diff() [0, 0, 1, -1, 0, 0, 1, 0, 0, -1, 0]
# ^ ^ ^ ^
# nonzero() [2, 3, 6, 9]
# reshape() [[2, 3],
# [6, 9]]
# diff() [1, 3]
Идея заключается в том, что при заполнении двоичного массива нулями на обоих концах можно легко найти начало и конец каждой последовательности единиц, применив np.diff() ( 1
где 0->1
и -1
где 1->0
). Поэтому np.nonzero(np.diff())
дает индексы начальной и конечной точек каждой последовательности. Кроме того, мы знаем, что starts ( 1
) и ends ( -1
) всегда должны чередоваться. So np.reshape(-1, 2)
дает нам начальные точки в первом столбце и конечные точки во втором. np.diff()
Повторное применение к этому массиву дает длину каждой последовательности.
Комментарии:
1. Похоже, это отлично работает! Не могли бы вы объяснить процесс здесь?
2. Тот же метод в моем решении, но другое расположение аналогичных методов 😉
Ответ №2:
Это способ сделать это по строкам:
def get_blocks(a):
x = np.diff(a,prepend=0,append=0)
groups, starts = np.nonzero(x*x x)
groups, ends = np.nonzero(x*x-x)
values = ends - starts
markers = np.diff(groups, prepend=0)
marker_idx = np.nonzero(marker_idx)[0]
return np.split(values, marker_idx)
Пример запуска:
>>> a = np.array([[1, 1, 1, 1, 0, 1, 1, 1, 1, 0],
[1, 0, 0, 1, 0, 1, 1, 1, 1, 1],
[1, 0, 1, 0, 1, 0, 0, 1, 0, 1],
[0, 1, 0, 0, 1, 0, 1, 0, 0, 0],
[1, 1, 1, 1, 1, 1, 0, 1, 0, 1],
[0, 0, 1, 0, 0, 1, 1, 1, 0, 0],
[0, 0, 0, 0, 1, 1, 0, 1, 1, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 1, 1],
[0, 1, 0, 1, 0, 1, 0, 0, 0, 0],
[0, 0, 1, 0, 0, 0, 1, 1, 1, 0]])
>>> get_blocks(a)
[array([4, 4], dtype=int32),
array([1, 1, 5], dtype=int32),
array([1, 1, 1, 1, 1], dtype=int32),
array([1, 1, 1], dtype=int32),
array([6, 1, 1], dtype=int32),
array([1, 3], dtype=int32),
array([2, 2], dtype=int32),
array([2], dtype=int32),
array([1, 1, 1], dtype=int32),
array([1, 3], dtype=int32)]
Это работает и для столбцов, если вы вызываете его A.T
Комментарии:
1.
np.diff()
принимает аргумент perpend и аргумент append ?! Чувак, я должен чаще просматривать документацию.2. @sclaronomic Да, похоже, это новая функция с января 2019 года, когда был выпущен numpy 1.16.0.