ম্যাটপ্ল্লোলিব প্লটের নিকটতম ডেটা পয়েন্টের স্থানাঙ্কগুলি প্রাপ্ত


9

আমি matplotlibসাথে ব্যবহার করছি NavigationToolbar2QT। টুলবারটি কার্সারের অবস্থান দেখাচ্ছে। তবে আমি চাই যে কার্সারটি নিকটতম ডেটা পয়েন্টে স্ন্যাপ করে (যখন যথেষ্ট কাছে থাকে) বা কেবল নিকটবর্তী ডেটা পয়েন্টের স্থানাঙ্ক প্রদর্শন করে show তা কি কোনওভাবে সাজানো যায়?


দয়া করে নীচের লিঙ্কটি দেখুন এবং দেখুন এটি আপনার সমস্যার সমাধান করে কিনা। লিঙ্কটি একটি ফাংশন স্নাপটকার্সার সরবরাহ করে যা আপনি যা খুঁজছেন তার মতো দেখতে। matplotlib.org/3.1.1/gallery/misc/cursor_demo_sgskip.html
অনুপম চ্যাপলট

@ অনুপম চ্যাপলট "এটি কার্সার আঁকার জন্য ম্যাটপ্লটলিব ব্যবহার করে এবং এটি একটি ধীর গতি হতে পারে যেহেতু এটির জন্য প্রতিটি মাউস পদক্ষেপের সাহায্যে চিত্রটি আবার অঙ্কন করা প্রয়োজন" " আমার গ্রাফটিতে প্রতি 10000 পয়েন্ট সহ প্রায় 16 টি প্লট রয়েছে, তাই পুনরায় আঁকানো সহ এটি বরং ধীর হবে।
পিগমালিয়ন

আপনি যদি দৃষ্টিভঙ্গিভাবে কোনও কিছুই পুনরায় আঁকতে না চান (তবে কেন এটির জন্য জিজ্ঞাসা করবেন?), টুলপ্লেতে ম্যাটপ্লটলিব.আর.৩.৩.১.১
ImportanceOfBeingErnest

@ ইমপোর্টান্সঅফবিজিংআরনেস্ট আমি আপনার পরামর্শটি বুঝতে পারি না। তবে এটি কল্পনা করুন: আপনার কাছে 16 টি লাইন প্লট রয়েছে এবং তাদের প্রত্যেকের একটি আলাদা শিখর রয়েছে। আপনি ডেটাতে খোঁজ না করেই একটি প্লটের শিখরের সঠিক স্থানাঙ্কগুলি জানতে চান। আপনি কখনই কার্সারটিকে ঠিক বিন্দুতে রাখতে পারবেন না, সুতরাং এটি অত্যন্ত অনর্থক। সুতরাং অরিজিনের মতো প্রোগ্রামগুলিতে বর্তমান কার্সার অবস্থানের নিকটতম বিন্দুর সঠিক স্থানাঙ্ক প্রদর্শন করার বিকল্প রয়েছে।
পিগমালিয়ন

1
হ্যাঁ, এটিই কর্সার_ডেমো_সেস্কিপ করে। তবে আপনি যদি কার্সার আঁকতে না চান, আপনি সেই উদাহরণটি থেকে গণনাগুলি ব্যবহার করতে পারেন এবং এর পরিবর্তে সরঞ্জামদণ্ডে ফলাফলটি চিত্র প্রদর্শন করতে পারেন যেমন চিত্র_জকর্ড
ImportanceOfBeingEnnest

উত্তর:


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প্রতিটি সময় প্লটকে পুনরায় স্কেলিং ব্যতীত জুম করা হবে?
পিগমালিয়ন

দিক অনুপাত পরিবর্তনের জন্য সি কেডিটিরিজে দূরত্ব কীভাবে পরিমাপ করা হয় তার মেট্রিক পরিবর্তন করা প্রয়োজন। দেখে মনে হচ্ছে ckdtree তে কাস্টম মেট্রিক ব্যবহার করা সমর্থিত নয়। অতএব আপনাকে অবশ্যই ckdtree.dataস্কেল = 1 দিয়ে বাস্তবসম্মত পয়েন্ট হিসাবে রাখতে হবে Your pointsআপনার উদ্ধার করা যেতে পারে এবং কেবলমাত্র তাদের সূচকগুলিতে অ্যাক্সেস করার প্রয়োজন হলে কোনও সমস্যা নেই।
mathfux

ধন্যবাদ। আপনি কি জানেন যে কোনও সুযোগেই, যদি অক্ষগুলির জন্য সহজেই পুনরায় স্কেল অনুপাত অ্যাক্সেস করার কোনও উপায় থাকে matplotlib? ওয়েবে আমি যা পেয়েছি তা অত্যন্ত জটিল ছিল।
পিগমালিয়ন

আইএমএইচও আমার সমস্যার সর্বোত্তম সমাধান হ'ল 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 টি প্লট) তবে ধারণাটি ছিল যে বিন্দুর স্থানাঙ্কগুলি নেভিগেশন সরঞ্জামদণ্ডে মুদ্রিত রয়েছে। এটা কি সম্ভব?
পিগমালিয়ন

0

আপনি হ্যান্ডলারকে সাবক্লাস NavigationToolbar2QTএবং ওভাররাইড করতে পারেন mouse_movexdataএবং 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_()
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.