क्या मेटप्लोटलिब में यह जांचने का एक तरीका है कि कुल्हाड़ियों के वर्तमान में प्रदर्शित क्षेत्र में कौन से कलाकार हैं?


9

मेरा एक इंटरेक्टिव फिगर वाला कार्यक्रम है जहां कभी-कभार कई कलाकार खींचे जाते हैं। इस आंकड़े में, आप माउस का उपयोग करके ज़ूम और पैन भी कर सकते हैं। हालांकि, एक पैनिंग को ज़ूम करने के दौरान प्रदर्शन बहुत अच्छा नहीं होता है क्योंकि हर कलाकार हमेशा रिडर्न होता है। क्या यह जांचने का एक तरीका है कि वर्तमान में प्रदर्शित किए गए क्षेत्र में कौन से कलाकार हैं और केवल उन्हीं को फिर से बनाना है? (परफ्यूम के नीचे के उदाहरण में अभी भी अपेक्षाकृत अच्छा है, लेकिन इसे अधिक या अधिक जटिल कलाकारों का उपयोग करके मनमाने ढंग से बदतर बनाया जा सकता है)

मुझे इस hoverपद्धति के साथ एक समान प्रदर्शन समस्या थी कि जब भी इसे बुलाया गया तो इसे canvas.draw()अंत में चलाया गया । लेकिन जैसा कि आप देख सकते हैं मैं कैशिंग का इस्तेमाल कर रही और कुल्हाड़ियों की पृष्ठभूमि (के आधार पर बहाल करके उस के लिए एक साफ वैकल्पिक हल मिल गया इस )। इसने प्रदर्शन में काफी सुधार किया और अब भी कई कलाकारों के साथ यह बहुत सहजता से चलता है। हो सकता है कि ऐसा करने का एक तरीका है, लेकिन विधि panऔर zoomविधि के लिए?

लंबे कोड के नमूने के लिए क्षमा करें, यह अधिकांश प्रश्न के लिए सीधे प्रासंगिक नहीं है, लेकिन समस्या को उजागर करने के लिए एक कार्यशील उदाहरण के लिए आवश्यक है।

संपादित करें

मैंने एमडब्ल्यूई को कुछ ऐसा अपडेट किया जो मेरे वास्तविक कोड का अधिक प्रतिनिधि है।

import numpy as np
import numpy as np
import sys
import matplotlib.pyplot as plt
from matplotlib.backends.backend_qt5agg import \
    FigureCanvasQTAgg
import matplotlib.patheffects as PathEffects
from matplotlib.text import Annotation
from matplotlib.collections import LineCollection

from PyQt5.QtWidgets import QApplication, QVBoxLayout, QDialog


def check_limits(base_xlim, base_ylim, new_xlim, new_ylim):
    if new_xlim[0] < base_xlim[0]:
        overlap = base_xlim[0] - new_xlim[0]
        new_xlim[0] = base_xlim[0]
        if new_xlim[1] + overlap > base_xlim[1]:
            new_xlim[1] = base_xlim[1]
        else:
            new_xlim[1] += overlap
    if new_xlim[1] > base_xlim[1]:
        overlap = new_xlim[1] - base_xlim[1]
        new_xlim[1] = base_xlim[1]
        if new_xlim[0] - overlap < base_xlim[0]:
            new_xlim[0] = base_xlim[0]
        else:
            new_xlim[0] -= overlap
    if new_ylim[1] < base_ylim[1]:
        overlap = base_ylim[1] - new_ylim[1]
        new_ylim[1] = base_ylim[1]
        if new_ylim[0] + overlap > base_ylim[0]:
            new_ylim[0] = base_ylim[0]
        else:
            new_ylim[0] += overlap
    if new_ylim[0] > base_ylim[0]:
        overlap = new_ylim[0] - base_ylim[0]
        new_ylim[0] = base_ylim[0]
        if new_ylim[1] - overlap < base_ylim[1]:
            new_ylim[1] = base_ylim[1]
        else:
            new_ylim[1] -= overlap

    return new_xlim, new_ylim


class FigureCanvas(FigureCanvasQTAgg):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.bg_cache = None

    def draw(self):
        ax = self.figure.axes[0]
        hid_annotation = False
        if ax.annot.get_visible():
            ax.annot.set_visible(False)
            hid_annotation = True
        hid_highlight = False
        if ax.last_artist:
            ax.last_artist.set_path_effects([PathEffects.Normal()])
            hid_highlight = True
        super().draw()
        self.bg_cache = self.copy_from_bbox(self.figure.bbox)
        if hid_highlight:
            ax.last_artist.set_path_effects(
                [PathEffects.withStroke(
                    linewidth=7, foreground="c", alpha=0.4
                )]
            )
            ax.draw_artist(ax.last_artist)
        if hid_annotation:
            ax.annot.set_visible(True)
            ax.draw_artist(ax.annot)

        if hid_highlight:
            self.update()


def position(t_, coeff, var=0.1):
    x_ = np.random.normal(np.polyval(coeff[:, 0], t_), var)
    y_ = np.random.normal(np.polyval(coeff[:, 1], t_), var)

    return x_, y_


class Data:
    def __init__(self, times):
        self.length = np.random.randint(1, 20)
        self.t = np.sort(
            np.random.choice(times, size=self.length, replace=False)
        )
        self.vel = [np.random.uniform(-2, 2), np.random.uniform(-2, 2)]
        self.accel = [np.random.uniform(-0.01, 0.01), np.random.uniform(-0.01,
                                                                      0.01)]
        x0, y0 = np.random.uniform(0, 1000, 2)
        self.x, self.y = position(
            self.t, np.array([self.accel, self.vel, [x0, y0]])
        )


class Test(QDialog):
    def __init__(self):
        super().__init__()
        self.fig, self.ax = plt.subplots()
        self.canvas = FigureCanvas(self.fig)
        self.artists = []
        self.zoom_factor = 1.5
        self.x_press = None
        self.y_press = None
        self.annot = Annotation(
            "", xy=(0, 0), xytext=(-20, 20), textcoords="offset points",
            bbox=dict(boxstyle="round", fc="w", alpha=0.7), color='black',
            arrowprops=dict(arrowstyle="->"), zorder=6, visible=False,
            annotation_clip=False, in_layout=False,
        )
        self.annot.set_clip_on(False)
        setattr(self.ax, 'annot', self.annot)
        self.ax.add_artist(self.annot)
        self.last_artist = None
        setattr(self.ax, 'last_artist', self.last_artist)

        self.image = np.random.uniform(0, 100, 1000000).reshape((1000, 1000))
        self.ax.imshow(self.image, cmap='gray', interpolation='nearest')
        self.times = np.linspace(0, 20)
        for i in range(1000):
            data = Data(self.times)
            points = np.array([data.x, data.y]).T.reshape(-1, 1, 2)
            segments = np.concatenate([points[:-1], points[1:]], axis=1)
            z = np.linspace(0, 1, data.length)
            norm = plt.Normalize(z.min(), z.max())
            lc = LineCollection(
                segments, cmap='autumn', norm=norm, alpha=1,
                linewidths=2, picker=8, capstyle='round',
                joinstyle='round'
            )
            setattr(lc, 'data_id', i)
            lc.set_array(z)
            self.ax.add_artist(lc)
            self.artists.append(lc)
        self.default_xlim = self.ax.get_xlim()
        self.default_ylim = self.ax.get_ylim()

        self.canvas.draw()

        self.cid_motion = self.fig.canvas.mpl_connect(
            'motion_notify_event', self.motion_event
        )
        self.cid_button = self.fig.canvas.mpl_connect(
            'button_press_event', self.pan_press
        )
        self.cid_zoom = self.fig.canvas.mpl_connect(
            'scroll_event', self.zoom
        )

        layout = QVBoxLayout()
        layout.addWidget(self.canvas)
        self.setLayout(layout)

    def zoom(self, event):
        if event.inaxes == self.ax:
            scale_factor = np.power(self.zoom_factor, -event.step)
            xdata = event.xdata
            ydata = event.ydata
            cur_xlim = self.ax.get_xlim()
            cur_ylim = self.ax.get_ylim()
            x_left = xdata - cur_xlim[0]
            x_right = cur_xlim[1] - xdata
            y_top = ydata - cur_ylim[0]
            y_bottom = cur_ylim[1] - ydata

            new_xlim = [
                xdata - x_left * scale_factor, xdata + x_right * scale_factor
            ]
            new_ylim = [
                ydata - y_top * scale_factor, ydata + y_bottom * scale_factor
            ]
            # intercept new plot parameters if they are out of bounds
            new_xlim, new_ylim = check_limits(
                self.default_xlim, self.default_ylim, new_xlim, new_ylim
            )

            if cur_xlim != tuple(new_xlim) or cur_ylim != tuple(new_ylim):
                self.ax.set_xlim(new_xlim)
                self.ax.set_ylim(new_ylim)

                self.canvas.draw_idle()

    def motion_event(self, event):
        if event.button == 1:
            self.pan_move(event)
        else:
            self.hover(event)

    def pan_press(self, event):
        if event.inaxes == self.ax:
            self.x_press = event.xdata
            self.y_press = event.ydata

    def pan_move(self, event):
        if event.inaxes == self.ax:
            xdata = event.xdata
            ydata = event.ydata
            cur_xlim = self.ax.get_xlim()
            cur_ylim = self.ax.get_ylim()
            dx = xdata - self.x_press
            dy = ydata - self.y_press
            new_xlim = [cur_xlim[0] - dx, cur_xlim[1] - dx]
            new_ylim = [cur_ylim[0] - dy, cur_ylim[1] - dy]

            # intercept new plot parameters that are out of bound
            new_xlim, new_ylim = check_limits(
                self.default_xlim, self.default_ylim, new_xlim, new_ylim
            )

            if cur_xlim != tuple(new_xlim) or cur_ylim != tuple(new_ylim):
                self.ax.set_xlim(new_xlim)
                self.ax.set_ylim(new_ylim)

                self.canvas.draw_idle()

    def update_annot(self, event, artist):
        self.ax.annot.xy = (event.xdata, event.ydata)
        text = f'Data #{artist.data_id}'
        self.ax.annot.set_text(text)
        self.ax.annot.set_visible(True)
        self.ax.draw_artist(self.ax.annot)

    def hover(self, event):
        vis = self.ax.annot.get_visible()
        if event.inaxes == self.ax:
            ind = 0
            cont = None
            while (
                ind in range(len(self.artists))
                and not cont
            ):
                artist = self.artists[ind]
                cont, _ = artist.contains(event)
                if cont and artist is not self.ax.last_artist:
                    if self.ax.last_artist is not None:
                        self.canvas.restore_region(self.canvas.bg_cache)
                        self.ax.last_artist.set_path_effects(
                            [PathEffects.Normal()]
                        )
                        self.ax.last_artist = None
                    artist.set_path_effects(
                        [PathEffects.withStroke(
                            linewidth=7, foreground="c", alpha=0.4
                        )]
                    )
                    self.ax.last_artist = artist
                    self.ax.draw_artist(self.ax.last_artist)
                    self.update_annot(event, self.ax.last_artist)
                ind += 1

            if vis and not cont and self.ax.last_artist:
                self.canvas.restore_region(self.canvas.bg_cache)
                self.ax.last_artist.set_path_effects([PathEffects.Normal()])
                self.ax.last_artist = None
                self.ax.annot.set_visible(False)
        elif vis:
            self.canvas.restore_region(self.canvas.bg_cache)
            self.ax.last_artist.set_path_effects([PathEffects.Normal()])
            self.ax.last_artist = None
            self.ax.annot.set_visible(False)
        self.canvas.update()
        self.canvas.flush_events()


if __name__ == '__main__':
    app = QApplication(sys.argv)
    test = Test()
    test.show()
    sys.exit(app.exec_())

मैं समस्या को नहीं समझता। चूँकि कलाकार कुल्हाड़ियों के बाहर होते हैं, वैसे भी उन्हें नहीं खींचा जाता है, वे कुछ भी धीमा नहीं करेंगे।
महत्त्व

तो आप कह रहे हैं कि पहले से ही एक रूटीन है जो चेक करता है कि कौन से कलाकारों को देखा जा सकता है, केवल वही दिखाई दे रहा है जो वास्तव में खींचा हुआ है? शायद यह दिनचर्या है जो कम्प्यूटेशनल रूप से बहुत महंगी है? क्योंकि आप आसानी से प्रदर्शन में अंतर देख सकते हैं यदि आप निम्न प्रयास करें जैसे: मेरे 1000 कलाकार WME के ​​ऊपर, एकल कलाकार पर ज़ूम करें और चारों ओर पैन करें। आप एक महत्वपूर्ण देरी देखेंगे। अब ऐसा ही करें लेकिन केवल 1 (या यहां तक ​​कि 100) कलाकार की साजिश करें और आप देखेंगे कि लगभग कोई देरी नहीं हुई है।
मेफ

खैर, सवाल यह है कि क्या आप एक अधिक कुशल दिनचर्या लिखने में सक्षम हैं? एक साधारण मामले में, शायद। तो आप देख सकते हैं कि कौन से कलाकार दृश्य सीमा के भीतर हैं और अन्य सभी अदृश्य सेट करते हैं। यदि जाँच केवल बिंदुओं के केंद्र निर्देशांक की तुलना करती है, तो यह तेज़ है। लेकिन यह आपको डॉट को ढीला कर देगा यदि केवल इसका केंद्र बाहर है, लेकिन इसके आधे से थोड़ा कम अभी भी दृश्य के अंदर होगा। कहा जा रहा है, यहाँ मुख्य समस्या यह है कि कुल्हाड़ियों में 1000 कलाकार हैं। यदि इसके बजाय, आपने plotसभी बिंदुओं के साथ केवल एक एकल का उपयोग किया , तो समस्या नहीं होगी।
ImportanceOfBeingErnest

हाँ बिल्कुल सच है। यह सिर्फ इतना है कि मेरा आधार गलत था। मुझे लगा कि खराब प्रदर्शन का कारण यह था कि सभी कलाकार इस तरह से स्वतंत्र हैं कि उन्हें देखा जा सकता है या नहीं। इस प्रकार मैंने सोचा कि एक स्मार्ट दिनचर्या जो केवल कलाकारों को दिखाई देती है, जो प्रदर्शन को बेहतर बनाती हैं, लेकिन जाहिर तौर पर इस तरह की दिनचर्या पहले से ही है, इसलिए मुझे लगता है कि बहुत कुछ ऐसा नहीं है जो यहां किया जा सकता है। मुझे पूरा यकीन है कि मैं कम से कम एक सामान्य मामले के लिए अधिक कुशल दिनचर्या नहीं लिख पाऊंगा।
mapf

हालांकि मेरे मामले में, मैं वास्तव में लाइनकोलिलेशन (पृष्ठभूमि में एक छवि) के साथ काम कर रहा हूं और जैसा कि आपने पहले ही कहा था, भले ही यह मेरे एमडब्ल्यूई के रूप में डॉट्स थे, बस यह जांचना कि क्या कुल्हाड़ियों के अंदर निर्देशांक पर्याप्त नहीं हैं। हो सकता है कि मुझे यह स्पष्ट करने के लिए MWE को तदनुसार अपडेट करना चाहिए।
mapf

जवाबों:


0

आप पा सकते हैं कि कलाकार कुल्हाड़ियों के वर्तमान क्षेत्र में हैं यदि आप उन आंकड़ों पर ध्यान केंद्रित करते हैं जो कलाकार साजिश रच रहे हैं।

उदाहरण के लिए यदि आप अपने अंक डेटा ( aऔर bसरणियों) को इस तरह एक संख्या में रखते हैं:

self.points = np.random.randint(0, 100, (1000, 2))

आप वर्तमान x और y सीमा के अंदर बिंदुओं की सूची प्राप्त कर सकते हैं:

xmin, xmax = self.ax.get_xlim()
ymin, ymax = self.ax.get_ylim()

p = self.points

indices_of_visible_points = (np.argwhere((p[:, 0] > xmin) & (p[:, 0] < xmax) & (p[:, 1] > ymin) &  (p[:, 1] < ymax))).flatten()

आप indices_of_visible_pointsअपनी संबंधित self.artistsसूची को अनुक्रमित करने के लिए उपयोग कर सकते हैं


आपके उत्तर के लिए धन्यवाद! दुर्भाग्य से, यह केवल तभी काम करता है जब कलाकार एकल बिंदु होते हैं। यह पहले से ही काम नहीं करता है अगर कलाकारों की लाइनें हैं। उदाहरण के लिए, केवल दो बिंदुओं द्वारा परिभाषित रेखा, जहां बिंदु कुल्हाड़ियों की सीमा के बाहर स्थित हैं, हालांकि बिंदुओं को जोड़ने वाली रेखा अक्षों के फ्रेम को काट रही है। हो सकता है कि मुझे उसके अनुसार MWE को संपादित करना चाहिए ताकि यह अधिक स्पष्ट हो।
mapf

मेरे लिए दृष्टिकोण समान है, डेटा पर ध्यान केंद्रित करें । यदि कलाकार ऐसी रेखाएँ हैं, जिन्हें आप दृश्य आयत के साथ चौराहे पर देख सकते हैं। यदि आप कर्व की साजिश रच रहे हैं, तो शायद आप उन्हें निश्चित अंतराल पर नमूना दे रहे हैं ताकि उन्हें लाइन सेगमेंट में कम किया जा सके। वैसे, क्या आप अधिक यथार्थवादी नमूना दे सकते हैं कि आप क्या साजिश रच रहे हैं?
गुगली

मैंने MWE के लिए अद्यतन किया
mapf
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.