Визуальное представление 2D-массива объектов - PullRequest
1 голос
/ 09 мая 2020

Предположим, я получаю массив объектов, например,

array([[ 0 ,  0 , 'g', 'g', 'g'],
       [ 0 ,  0 ,  0 , 'g', 'g'],
       ['d',  0 ,  1 ,  1 , 'g'],
       ['d', 'd',  1 ,  1 , 'g'],
       ['d', 'd', 'd', 'd', 'g']], dtype=object)

Я хочу иметь возможность визуально видеть это, как если бы я использовал plt.imshow на numpy array. В частности, я хотел бы увидеть изображение, на котором область с g имеет какой-то цвет (возможно, желтый), область с d - какой-то цвет (возможно, зеленый) и, возможно, цветовую карту, которая изменяется для значений numeri c 0, 1 ,2 , ..., как показано на изображении ниже.

Sample

РЕДАКТИРОВАТЬ: Возможно, что числовые значения c не целые числа, просто с плавающей точкой.

Ответы [ 2 ]

2 голосов
/ 09 мая 2020

Я думаю, что в этом типе приложений абсолютно необходима хорошая информативная палитра, даже если ОП не упоминает палитру в своем Q…

enter image description here


Приведенный ниже код основан на предположении, что целые числа, используемые в массиве, представляют собой только нули и единицы

import numpy as np
import matplotlib.pyplot as plt
from itertools import count

a = np.array([[ 0 ,  0 , 'g', 'g', 'g'], 
              [ 0 ,  0 ,  0 , 'g', 'g'], 
              ['d',  0 ,  1 ,  1 , 'g'], 
              ['d', 'd',  1 ,  1 , 'g'], 
              ['d', 'd', 'd', 'd', 'g']], dtype=object)

# we want to build a dictionary mapping objects to integers
seq2 = count(2) # we don't know in advance how many different objects we'll see
d = {0:0, 1:1}  # but we know that the integers are either 0 or 1
for o in a.flatten():  
    if o not in d: d[o] = next(seq2)

# with the help of the dictionary, here it is a plottable matrix
b = np.array([d[x] for x in a.flatten()]).reshape(a.shape)

N = len(d)
# to avoid a continuous colorbar, we sample the needed colors
cmap = plt.cm.get_cmap('viridis', N) 

# eventually,
# we can plot the matrix, the colorbar and fix the colorbar labelling
plt.imshow(b, cmap=cmap)
cb = plt.colorbar(drawedges=True)
dc = (N-1)/N
cb.set_ticks([dc*(n+1/2) for n in range(N)])
cb.set_ticklabels([v for k, v in sorted((v,k) for k,v in d.items())])

plt.show()

enter image description here



Post Scriptum

В другом ответе упоминается, что (курсив мой)

Из документов imshow соответствует исключительно массивы с типом float

, но ① единственные требования касаются скалярных данных, потому что «непрерывное» отображение на цветовую карту делегировано классу matplotlib.colors.Normalize и ② с использованием специализированного подкласса matplotlib.colors.Normalize можно напрямую используйте отдельные целые числа в матрице для индексации списка цветов, связанных с каждой цветовой картой, например,

In [34]: import matplotlib.pyplot as plt 
    ...: import numpy as np 
    ...: from matplotlib.colors import NoNorm 
    ...: %matplotlib 
    ...:  
    ...: mat_r, mat_i = (np.array(np.arange(6), dtype=float)[None,:], 
    ...:                 np.array(np.arange(6), dtype=int)[None,:]) 
    ...:  
    ...: def show(ax, mat, title, norm=None): 
    ...:     ax.imshow(mat, norm=norm) 
    ...:     ax.set_title(title) 
    ...:     ax.set_yticks([]) 
    ...:  
    ...: fig, axes = plt.subplots(6,1, figsize=(3, 6)) 
    ...:  
    ...: for ax, mat, norm, title in zip( 
    ...:   axes, 
    ...:   (mat_r, mat_i, mat_i, 30*mat_r, 30*mat_i, 30*mat_i), 
    ...:   (None, None, NoNorm(), None, None, NoNorm()), 
    ...:   ('mat_r','mat_i','mat_i NoNorm','30×mat_r','30×mat_i','30×mat_i NoNorm')): 
    ...:      show(ax, mat, title, norm) 
    ...: fig.tight_layout()                                                               

enter image description here

In [35]: plt.cm.viridis.colors[:151:30]                                                   
Out[35]: 
[[0.267004, 0.004874, 0.329415],
 [0.280255, 0.165693, 0.476498],
 [0.237441, 0.305202, 0.541921],
 [0.182256, 0.426184, 0.55712],
 [0.13777, 0.537492, 0.554906],
 [0.128087, 0.647749, 0.523491]]

In [36]:                                                                                  
2 голосов
/ 09 мая 2020

Сначала давайте посмотрим на imshow из документов: https://matplotlib.org/3.1.1/api/_as_gen/matplotlib.pyplot.imshow.html

Из документов:

Показать изображение, т.е. данные на обычном 2D-растре.

, тогда как документы для ввода изображения X состояние:

X : изображение в виде массива или PIL

Данные изображения. Поддерживаемые формы массивов:

(M, N): изображение со скалярными данными. Данные визуализируются с помощью цветовой карты.

(M, N, 3): изображение со значениями RGB (0-1 с плавающей точкой или 0-255 int).

(M, N, 4): изображение со значениями RGBA (0-1 с плавающей запятой или 0-255 int), то есть включая прозрачность.

Из документации, imshow соответствует исключительно массивам с типом float и, следовательно, нужно найти способ преобразовать dtype из объекта в float для массива, который вы разместили.

Итак, как указано в моем комментарии: я думал о чем-то вроде этого.

import numpy as np
import matplotlib.pyplot as plt

foo = np.array([[ 0 ,  0 , 'g', 'g', 'g'], 
                [ 0 ,  0 ,  0 , 'g', 'g'],
                ['d',  0 ,  1 ,  1 , 'g'],
                ['d', 'd',  1 ,  1 , 'g'],
                ['d', 'd', 'd', 'd', 'g']], dtype=object)

# replace chars by reserved numbers
np.place(foo, foo=='g', -1)
np.place(foo, foo=='d', -2)

# change dtype from object to float
foo = foo.astype(np.float32)

# sanity check
print(foo)

# plot
plt.imshow(foo)

где я не настраивал cmap, потому что это действительно сахар на вершине. :-) Результат выглядит следующим образом:

в результате imshow

Проблема:

  • Объявление imshow ожидает, что входной массив будет dtype.float, отсюда и подход преобразования.

Бонус:

  • Зарезервированные числа для каждого символа могут быть сопоставлены с выделенным цветом в вашем cmap по вашему запросу.

Недостатки:

  • Не очень хорошо масштабируется для произвольных символов.
  • Использует зарезервированные числа в качестве замены и, следовательно, набор чисел c значения для остальных должны быть известны и исправлены.

Я все еще надеюсь, что этот ответ протянет вам руку помощи. Ура!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...