Matplotlib प्लॉट पर निकटतम डेटा बिंदु के निर्देशांक प्राप्त करना


9

के matplotlibसाथ उपयोग कर रहा हूं NavigationToolbar2QT। टूलबार कर्सर की स्थिति दिखा रहा है। लेकिन मैं चाहूंगा कि कर्सर निकटतम डेटा बिंदु (जब पर्याप्त पास हो) या केवल निकटतम डेटा बिंदु के समन्वय को दिखाता है। क्या यह किसी तरह व्यवस्थित किया जा सकता है?


कृपया नीचे दिए गए लिंक को देखें और देखें कि क्या यह आपकी समस्या का समाधान करता है। लिंक एक फ़ंक्शन स्नैप्टोकोर्स प्रदान करता है जो आपके लिए देख रहे हैं के समान है। matplotlib.org/3.1.1/gallery/misc/cursor_demo_sgskip.html
अनुपम चपलोत

@AnupamChaplot "यह कर्सर खींचने के लिए Matplotlib का उपयोग करता है और यह धीमा हो सकता है क्योंकि इसके लिए हर माउस चाल के साथ आकृति को फिर से तैयार करना आवश्यक है।" मेरे पास ग्राफ पर 10000 अंक के साथ लगभग 16 भूखंड हैं, इसलिए इसे फिर से तैयार करने के बजाय धीमा होगा।
पैगामलियन

यदि आप नेत्रहीन कुछ भी नहीं निकालना चाहते हैं (तो इसके लिए क्यों पूछें?), आप टूलबार में दिखाए गए अनुसार हेरफेर कर सकते हैं जैसा कि matplotlib.org/3.1.1/gallery/images_contours_and_fields/…
ImportanceOfBeingErnest

@ImportanceOfBeingErnest मुझे आपका सुझाव समझ नहीं आया। लेकिन इसकी कल्पना करें: आपके पास 16 लाइन भूखंड हैं और उनमें से प्रत्येक में एक अलग चोटी है। आप डेटा में झांकने के बिना एक भूखंड के शिखर के सटीक निर्देशांक जानना चाहते हैं। आप कर्सर को कभी भी बिंदु पर नहीं डाल सकते हैं, इसलिए यह अत्यधिक प्रभाव है। इसलिए उत्पत्ति जैसे कार्यक्रमों में वर्तमान कर्सर स्थिति के निकटतम बिंदु के सटीक निर्देशांक दिखाने का विकल्प होता है।
पैगामलियन

1
हां, यह वही है जो कर्सर_demo_sgskip करता है। लेकिन अगर आप कर्सर नहीं खींचना चाहते हैं, तो आप उस उदाहरण से गणना का उपयोग कर सकते हैं और इसके बजाय टूलबार में परिणामी संख्या प्रदर्शित कर सकते हैं, जैसा कि image_zcoord
ImportanceOfBeingErnest

जवाबों:


6

यदि आप बड़े सेट के साथ काम कर रहे हैं, तो मैं आपको उपयोग करने की सलाह देता हूं CKDtrees:

import matplotlib.pyplot as plt
import numpy as np
import scipy.spatial

points = np.column_stack([np.random.rand(50), np.random.rand(50)])
fig, ax = plt.subplots()
coll = ax.scatter(points[:,0], points[:,1])
ckdtree = scipy.spatial.cKDTree(points)

मैंने kpie'sउत्तर को थोड़ा-थोड़ा करके उत्तर दिया। एक बार ckdtreeबन जाने के बाद , आप थोड़े प्रयास से निकटतम बिंदुओं को तुरंत पहचान सकते हैं और उनके बारे में विभिन्न प्रकार की जानकारी प्राप्त कर सकते हैं:

def closest_point_distance(ckdtree, x, y):
    #returns distance to closest point
    return ckdtree.query([x, y])[0]

def closest_point_id(ckdtree, x, y):
    #returns index of closest point
    return ckdtree.query([x, y])[1]

def closest_point_coords(ckdtree, x, y):
    # returns coordinates of closest point
    return ckdtree.data[closest_point_id(ckdtree, x, y)]
    # ckdtree.data is the same as points

कर्सर की स्थिति का इंटरएक्टिव प्रदर्शन। यदि आप चाहते हैं कि नेविगेशन टूलबार पर प्रदर्शित होने वाले निकटतम बिंदु का निर्देशांक:

def val_shower(ckdtree):
    #formatter of coordinates displayed on Navigation Bar
    return lambda x, y: '[x = {}, y = {}]'.format(*closest_point_coords(ckdtree, x, y))

plt.gca().format_coord = val_shower(ckdtree)
plt.show()

घटनाओं का उपयोग करना। यदि आप अन्य प्रकार की अन्तरक्रियाशीलता चाहते हैं, तो आप घटनाओं का उपयोग कर सकते हैं:

def onclick(event):
    if event.inaxes is not None:
        print(closest_point_coords(ckdtree, event.xdata, event.ydata))

fig.canvas.mpl_connect('motion_notify_event', onclick)
plt.show()

यह निश्चित रूप से केवल त्रुटिपूर्ण रूप से काम करेगा अगर x: y विज़ुअल स्केल बराबर हो। 1. समस्या के इस भाग के बारे में कोई भी विचार, pointsहर बार प्लॉट को फिर से स्केल करने के अलावा ज़ूम किया जाता है?
Pygmalion

पहलू अनुपात को बदलने के लिए ckdtrees में दूरी को कैसे मापा जाता है, इसके मैट्रिक्स को बदलना पड़ता है। ऐसा लगता है कि ckdtrees पर कस्टम मीट्रिक का उपयोग करना समर्थित नहीं है। इसलिए आपको ckdtree.dataस्केल = 1. के साथ यथार्थवादी बिंदुओं को रखना चाहिए । आपका pointsरिस्क हो सकता है और कोई समस्या नहीं है यदि आपको केवल उनके सूचकांक तक पहुंचने की आवश्यकता है।
मैथ्यूक्स

धन्यवाद। क्या आप जानते हैं, किसी भी संयोग से, यदि कुल्हाड़ियों के लिए reuw स्केल अनुपात को आसानी से एक्सेस करने का कोई तरीका है matplotlib? मुझे वेब पर जो मिला वह बेहद जटिल था।
पैगामलियन

IMHO मेरी समस्या का सबसे अच्छा समाधान है कि इसे matplotlibपुस्तकालय में एक विकल्प के रूप में शामिल किया जाएगा । आखिरकार, लाइब्रेरी ने कहीं-कहीं पॉइंट पोज़िशन को बचा लिया है - आखिरकार, यह उन्हें प्लॉट में बना रहा है।
पैगामलियन

आप कोशिश करना पसंद कर सकते हैं set_aspect: matplotlib.org/3.1.3/api/_as_gen/…
mathfux

0

जब आप क्लिक करेंगे तो निम्नलिखित कोड माउस के निकटतम डॉट के निर्देशांक को प्रिंट करेगा।

import matplotlib.pyplot as plt
import numpy as np
np.random.seed(19680801)
N = 50
x = np.random.rand(N)
y = np.random.rand(N)
fig,ax = plt.subplots()
plt.scatter(x, y)
points = list(zip(x,y))
def distance(a,b):
    return(sum([(k[0]-k[1])**2 for k in zip(a,b)])**0.5)
def onclick(event):
    dists = [distance([event.xdata, event.ydata],k) for k in points]
    print(points[dists.index(min(dists))])
fig.canvas.mpl_connect('button_press_event', onclick)
plt.show()

मैं शायद अपनी स्थिति के लिए कोड को अनुकूलित करने में सक्षम हो सकता है (प्रत्येक 10000 अंक के साथ 16 भूखंड), लेकिन विचार यह था कि डॉट के निर्देशांक, नेविगेशन टूलबार पर कहते हैं। क्या यह संभव है?
Pygmalion

0

आप उपसंहार NavigationToolbar2QTऔर mouse_moveहैंडलर को ओवरराइड कर सकते हैं । xdataऔर ydataविशेषताओं साजिश निर्देशांक में वर्तमान माउस स्थिति में होते हैं। आप बेस क्लास mouse_moveहैंडलर को इवेंट पास करने से पहले उसे निकटतम डेटा बिंदु पर स्नैप कर सकते हैं ।

पूर्ण उदाहरण, बोनस के रूप में भूखंड में निकटतम बिंदु को उजागर करने के साथ:

import sys

import numpy as np

from matplotlib.backends.qt_compat import QtWidgets
from matplotlib.backends.backend_qt5agg import FigureCanvas, NavigationToolbar2QT
from matplotlib.figure import Figure


class Snapper:
    """Snaps to data points"""

    def __init__(self, data, callback):
        self.data = data
        self.callback = callback

    def snap(self, x, y):
        pos = np.array([x, y])
        distances = np.linalg.norm(self.data - pos, axis=1)
        dataidx = np.argmin(distances)
        datapos = self.data[dataidx,:]
        self.callback(datapos[0], datapos[1])
        return datapos


class SnappingNavigationToolbar(NavigationToolbar2QT):
    """Navigation toolbar with data snapping"""

    def __init__(self, canvas, parent, coordinates=True):
        super().__init__(canvas, parent, coordinates)
        self.snapper = None

    def set_snapper(self, snapper):
        self.snapper = snapper

    def mouse_move(self, event):
        if self.snapper and event.xdata and event.ydata:
            event.xdata, event.ydata = self.snapper.snap(event.xdata, event.ydata)
        super().mouse_move(event)


class Highlighter:
    def __init__(self, ax):
        self.ax = ax
        self.marker = None
        self.markerpos = None

    def draw(self, x, y):
        """draws a marker at plot position (x,y)"""
        if (x, y) != self.markerpos:
            if self.marker:
                self.marker.remove()
                del self.marker
            self.marker = self.ax.scatter(x, y, color='yellow')
            self.markerpos = (x, y)
            self.ax.figure.canvas.draw()


class ApplicationWindow(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()
        self._main = QtWidgets.QWidget()
        self.setCentralWidget(self._main)
        layout = QtWidgets.QVBoxLayout(self._main)
        canvas = FigureCanvas(Figure(figsize=(5,3)))
        layout.addWidget(canvas)
        toolbar = SnappingNavigationToolbar(canvas, self)
        self.addToolBar(toolbar)

        data = np.random.randn(100, 2)
        ax = canvas.figure.subplots()
        ax.scatter(data[:,0], data[:,1])

        self.highlighter = Highlighter(ax)
        snapper = Snapper(data, self.highlighter.draw)
        toolbar.set_snapper(snapper)


if __name__ == "__main__":
    qapp = QtWidgets.QApplication(sys.argv)
    app = ApplicationWindow()
    app.show()
    qapp.exec_()
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.