Измените цвет точек данных при выделении и удалите их нажатием клавиши на графике рассеяния Matplotlib

используйте

ob_start ();

blockquote>

в самой верхней части вашего скрипта и

ob_end_flush ( );

blockquote>

внизу вашего скрипта. Этот wil включит буферизацию вывода и ваши заголовки будут созданы после того, как страница будет bufferd.

ОБЩИЕ ПРОБЛЕМЫ:

=============== =====

(скопированный ответ из: source )

1) не должно быть никакого выхода (т.е. echo.. или HTML-коды) перед командой header(.......);.

2) удалите любое белое пространство (или новую строку) до и после ?> тегов.

3) ЗОЛОТОЕ ПРАВИЛО! - проверьте, поддерживает ли этот файл php (а также, если вы include другие файлы) UTF8 без кодировки спецификации (а не только UTF-8). Это проблема во многих случаях (потому что кодированный файл UTF8 имеет что-то особенное в начале файла php, которое ваш текстовый редактор не показывает) !!!!!!!!!!!

4) После header(...); вы должны использовать exit;

5) всегда используйте ссылку 301 или 302:

header("location: http://example.com",  true,  301 );  exit;

6) Включить ошибку составление отчетов. И сообщать об ошибке.

7) Если ни одно из вышеизложенных не помогает, используйте перенаправление JAVSCRIPT (однако, сильно не рекомендуемый метод), может быть последним шансом в пользовательских случаях ...:

echo ""; exit;

2
задан Carl 17 January 2019 в 10:10
поделиться

1 ответ

Хорошо, у меня есть хотя бы частичное решение для вас, без выделения прямоугольника, но вы можете выбрать несколько точек и удалить их одним key_event.

Чтобы изменить цвет, вам нужно изменить graph._facecolor3d, подсказка была в в этом отчете об ошибке set_facecolor, не устанавливающем _facecolor3d.

Также может быть хорошей идеей переписать вашу функцию как класс, чтобы избавиться от любых необходимых global переменных.

В моем решении есть части, которые не совсем красивые, мне нужно перерисовать фигуру после удаления точек данных, я не могу заставить ее работать и обновляться. Также (см. РЕДАКТИРОВАТЬ 2 ниже). Я еще не реализовал, что произойдет, если последняя точка данных будет удалена.

Причина, по которой ваша функция on_key(event) не работала, была простой: вы забыли подключить ее.

Так что это решение, которое должно удовлетворять целям (i) и (ii):

import matplotlib.pyplot as plt, numpy as np
from mpl_toolkits.mplot3d import proj3d

class Class3DDataVisualizer:    
    def __init__(self, X, ids, subindus, drawNew = True):

        self.X = X;
        self.ids = ids
        self.subindus = subindus

        self.disconnect = False
        self.ind = []
        self.label = None

        if drawNew:        
            self.fig = plt.figure(figsize = (7,5))
        else:
            self.fig.delaxes(self.ax)
        self.ax = self.fig.add_subplot(111, projection = '3d')
        self.graph  = self.ax.scatter(self.X[:, 0], self.X[:, 1], self.X[:, 2], depthshade = False, picker = True, facecolors=np.repeat([[0,0,1,1]],X.shape[0], axis=0) )         
        if drawNew and not self.disconnect:
            self.fig.canvas.mpl_connect('motion_notify_event', lambda event: self.onMouseMotion(event))  # on mouse motion    
            self.fig.canvas.mpl_connect('pick_event', lambda event: self.onpick(event))
            self.fig.canvas.mpl_connect('key_press_event', lambda event: self.on_key(event))

        self.fig.tight_layout()
        self.fig.show()


    def distance(self, point, event):
        """Return distance between mouse position and given data point

        Args:
            point (np.array): np.array of shape (3,), with x,y,z in data coords
            event (MouseEvent): mouse event (which contains mouse position in .x and .xdata)
        Returns:
            distance (np.float64): distance (in screen coords) between mouse pos and data point
        """
        assert point.shape == (3,), "distance: point.shape is wrong: %s, must be (3,)" % point.shape

        # Project 3d data space to 2d data space
        x2, y2, _ = proj3d.proj_transform(point[0], point[1], point[2], plt.gca().get_proj())
        # Convert 2d data space to 2d screen space
        x3, y3 = self.ax.transData.transform((x2, y2))

        return np.sqrt ((x3 - event.x)**2 + (y3 - event.y)**2)


    def calcClosestDatapoint(self, event):
        """"Calculate which data point is closest to the mouse position.

        Args:
            X (np.array) - array of points, of shape (numPoints, 3)
            event (MouseEvent) - mouse event (containing mouse position)
        Returns:
            smallestIndex (int) - the index (into the array of points X) of the element closest to the mouse position
        """
        distances = [self.distance (self.X[i, 0:3], event) for i in range(self.X.shape[0])]
        return np.argmin(distances)


    def annotatePlot(self, index):
        """Create popover label in 3d chart

        Args:
            X (np.array) - array of points, of shape (numPoints, 3)
            index (int) - index (into points array X) of item which should be printed
        Returns:
            None
        """
        # If we have previously displayed another label, remove it first
        if self.label is not None:
            self.label.remove()
        # Get data point from array of points X, at position index
        x2, y2, _ = proj3d.proj_transform(self.X[index, 0], self.X[index, 1], self.X[index, 2], self.ax.get_proj())
        self.label = plt.annotate( self.ids[index],
            xy = (x2, y2), xytext = (-20, 20), textcoords = 'offset points', ha = 'right', va = 'bottom',
            bbox = dict(boxstyle = 'round,pad=0.5', fc = 'yellow', alpha = 0.5),
            arrowprops = dict(arrowstyle = '->', connectionstyle = 'arc3,rad=0'))
        self.fig.canvas.draw()


    def onMouseMotion(self, event):
        """Event that is triggered when mouse is moved. Shows text annotation over data point closest to mouse."""
        closestIndex = self.calcClosestDatapoint(event)
        self.annotatePlot (closestIndex) 


    def on_key(self, event):
        """
        Function to be bound to the key press event
        If the key pressed is delete and there is a picked object,
        remove that object from the canvas
        """
        if event.key == u'delete':
            if self.ind:
                self.X = np.delete(self.X, self.ind, axis=0)
                self.ids = np.delete(ids, self.ind, axis=0)
                self.__init__(self.X, self.ids, self.subindus, False)
            else:
                print('nothing selected')

    def onpick(self, event):
        self.ind.append(event.ind)
        self.graph._facecolor3d[event.ind] = [1,0,0,1]



#Datapoints
Y = np.array([[ 4.82250000e+01,  1.20276889e-03,  9.14501289e-01], [ 6.17564688e+01,  5.95020883e-02, -1.56770827e+00], [ 4.55139000e+01,  9.13454423e-02, -8.12277299e+00], [3,  8, -8.12277299e+00]])
#Annotations
ids = ['a', 'b', 'c', 'd']

subindustries =  'example'

Class3DDataVisualizer(Y, ids, subindustries)

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

Затем используйте proj3d.proj_transform, чтобы найти, какие данные находятся внутри этого прямоугольника, найти индекс упомянутых данных и перекрасить его, используя функцию self.graph._facecolor3d[idx], и заполнить self.ind этими индексами, после чего нажатие кнопки «Удалить» позаботится о удаляя все данные, указанные в self.ind.

РЕДАКТИРОВАТЬ: Я добавил две строки в __init__, которые удаляют топор / подзаговор, прежде чем добавить новую после удаления точек данных. Я заметил, что взаимодействие на графике становилось медленным после того, как несколько точек данных были удалены, поскольку фигура просто строила графики на каждом участке.

РЕДАКТИРОВАТЬ 2: Я узнал, как вы можете изменить свои данные вместо перерисовки всего графика. Как упомянуто в этом ответе вам придется изменить _offsets3d, что странно вернуть кортеж для x и y, но массив для z.

Вы можете изменить его, используя

(x,y,z) = self.graph._offsets3d # or event.artist._offsets3d
xNew = x[:int(idx)] + x[int(idx)+1:]
yNew = y[:int(idx)] + y[int(idx)+1:]
z = np.delete(z, int(idx))
self.graph._offsets3d = (xNew,yNew,z) # or event.artist._offsets3d

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

0
ответ дан Freya W 17 January 2019 в 10:10
поделиться
Другие вопросы по тегам:

Похожие вопросы: