Sign Up

Sign Up to our social questions and Answers Engine to ask questions, answer people’s questions, and connect with other people.

Have an account? Sign In

Have an account? Sign In Now

Sign In

Login to our social questions & Answers Engine to ask questions answer people’s questions & connect with other people.

Sign Up Here

Forgot Password?

Don't have account, Sign Up Here

Forgot Password

Lost your password? Please enter your email address. You will receive a link and will create a new password via email.

Have an account? Sign In Now

You must login to ask a question.

Forgot Password?

Need An Account, Sign Up Here

Please briefly explain why you feel this question should be reported.

Please briefly explain why you feel this answer should be reported.

Please briefly explain why you feel this user should be reported.

Sign InSign Up

The Archive Base

The Archive Base Logo The Archive Base Logo

The Archive Base Navigation

  • Home
  • SEARCH
  • About Us
  • Blog
  • Contact Us
Search
Ask A Question

Mobile menu

Close
Ask a Question
  • Home
  • Add group
  • Groups page
  • Feed
  • User Profile
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Buy Points
  • Users
  • Help
  • Buy Theme
  • SEARCH
Home/ Questions/Q 9165775
In Process

The Archive Base Latest Questions

Editorial Team
  • 0
Editorial Team
Asked: June 17, 20262026-06-17T14:59:07+00:00 2026-06-17T14:59:07+00:00

I have a plot of 3 data sets that have datetime objetcs on the

  • 0

I have a plot of 3 data sets that have datetime objetcs on the x axis.
I want to have a cursor that snaps to the data and shows the precise x and y value.

I already have a “snap to cursor”, but that only works for scalar x axes.
Can anyone help me to modify the snap to cursor so that it works for datetime x axes as well?

Here are my data plots:
enter image description here

import numpy as np
import matplotlib.pyplot as plot
import matplotlib.ticker as mticker
import matplotlib.dates as dates
import datetime
import Helpers

fig = plot.figure(1)
DAU = (  2,  20,  25,  60, 190, 210,  18, 196, 212)
WAU = ( 50, 160, 412, 403, 308, 379, 345, 299, 258)
MAU = (760, 620, 487, 751, 612, 601, 546, 409, 457)

firstDay = datetime.datetime(2012,1,15)

#create an array with len(DAU) entries from given starting day
dayArray = [firstDay + datetime.timedelta(days = i) for i in xrange(len(DAU))]

line1 = plot.plot(dayArray, DAU, 'o-', color = '#336699')
line2 = plot.plot(dayArray, WAU, 'o-', color = '#993333')
line3 = plot.plot(dayArray, MAU, 'o-', color = '#89a54e')

ax = plot.subplot(111)
dateLocator   = mticker.MultipleLocator(2)
dateFormatter = dates.DateFormatter('%d.%m.%Y')
ax.xaxis.set_major_locator(dateLocator)
ax.xaxis.set_major_formatter(dateFormatter)
fig.autofmt_xdate(rotation = 90, ha = 'center')

yMax = max(np.max(DAU), np.max(WAU), np.max(MAU))
yLimit = 100 - (yMax % 100) + yMax
plot.yticks(np.arange(0, yLimit + 1, 100))

plot.title('Active users', weight = 'bold')
plot.grid(True, axis = 'both')
plot.subplots_adjust(bottom = 0.2)
plot.subplots_adjust(right = 0.82)

legend = plot.legend((line1[0], line2[0], line3[0]),
                 ('DAU',
                 'WAU',
                 'MAU'),
                 'upper left',
                 bbox_to_anchor = [1, 1],
                 shadow = True)

frame = legend.get_frame()
frame.set_facecolor('0.80')
for t in legend.get_texts():
    t.set_fontsize('small')

#THIS DOES NOT WORK
cursor = Helpers.SnaptoCursor(ax, dayArray, DAU, 'euro daily')
plot.connect('motion_notify_event', cursor.mouse_move)

plot.show()

And this is my module “Helper” that contains the “SnaptoCursor” class:
(I got the basic SnaptoCursor class from somewhere else and modified it a little bit)

from __future__ import print_function
import numpy as np
import matplotlib.pyplot as plot

def minsec(sec, unused):
    """
    Returns a string of the input seconds formatted as mm'ss''.
    """
    minutes = sec // 60
    sec = sec - minutes * 60
    return '{0:02d}\'{1:02d}\'\''.format(int(minutes), int(sec))

class SnaptoCursor():
    """
    A cursor with crosshair snaps to the nearest x point.
    For simplicity, I'm assuming x is sorted.
    """
    def __init__(self, ax, x, y, formatting, z = None):
        """
        ax: plot axis
        x: plot spacing
        y: plot data
        formatting: string flag for desired formatting
        z: optional second plot data
        """
        self.ax = ax
        self.lx = ax.axhline(color = 'k')  #the horiz line
        self.ly = ax.axvline(color = 'k')  #the vert line
        self.x = x
        self.y = y
        self.z = z
        # text location in axes coords
        self.txt = ax.text(0.6, 0.9, '', transform = ax.transAxes)
        self.formatting = formatting

    def format(self, x, y):
        if self.formatting == 'minsec':
            return 'x={0:d}, y='.format(x) + minsec(y, 0)

        elif self.formatting == 'daily euro':
            return u'day {0:d}: {1:.2f}€'.format(x, y)

    def mouse_move(self, event):
        if not event.inaxes: return

        mouseX, mouseY = event.xdata, event.ydata

        #searchsorted: returns an index or indices that suggest where x should be inserted
        #so that the order of the list self.x would be preserved
        indx = np.searchsorted(self.x, [mouseX])[0]

        mouseX = self.x[indx]
        #if z wasn't defined
        if self.z == None:
            mouseY = self.y[indx]
        #if z was defined: compare the distance between mouse and the two plots y and z
        #and use the nearest one
        elif abs(mouseY - self.y[indx]) < abs(mouseY - self.z[indx]):
            mouseY = self.y[indx]
        else:
            mouseY = self.z[indx]

        #update the line positions
        self.lx.set_ydata(mouseY)
        self.ly.set_xdata(mouseX)

        self.txt.set_text(self.format(mouseX, mouseY))
        plot.draw()

Of course this does not work since I am calling the SnaptoCursor with the datetime array “dayArray”, which is supposed to be compared to the mouse coordinates later on. And these data types are not comparable.

  • 1 1 Answer
  • 0 Views
  • 0 Followers
  • 0
Share
  • Facebook
  • Report

Leave an answer
Cancel reply

You must login to add an answer.

Forgot Password?

Need An Account, Sign Up Here

1 Answer

  • Voted
  • Oldest
  • Recent
  • Random
  1. Editorial Team
    Editorial Team
    2026-06-17T14:59:08+00:00Added an answer on June 17, 2026 at 2:59 pm

    I got it!!!

    The problems where these two lines in the init method of the SnaptoCursor class:

    self.lx = ax.axhline(color = 'k')  #the horiz line
    self.ly = ax.axvline(color = 'k')  #the vert line
    

    They were somehow messing up the datetime x axis (that has ordinals up to 730,000 e.g.), so you just have to initialize the lines’ coordinates:

    self.lx = ax.axhline(y = min(y), color = 'k')  #the horiz line
    self.ly = ax.axvline(x = min(x), color = 'k')  #the vert line
    

    Then it works just fine!

    I’ll be posting my complete SnaptoCursor class now that I have modified so it accepts individual formatting strings, and it can take up to 3 input data plots – that get snapped to according to your mouse position.

    def percent(x, unused):
        """
        Returns a string of the float number x formatted as %.
        """
        return '{0:1.2f}%'.format(x * 100)
    
    def minsec(sec, unused):
        """
        Returns a string of the input seconds formatted as mm'ss''.
        """
        minutes = sec // 60
        sec = sec - minutes * 60
        return '{0:02d}\'{1:02d}\'\''.format(int(minutes), int(sec))
    
    class SnaptoCursor():
        """
        A cursor with crosshair snaps to the nearest x point.
        For simplicity, I'm assuming x is sorted.
        """
        def __init__(self, ax, x, y, formatting, y2 = None, y3 = None):
            """
            ax: plot axis
            x: plot spacing
            y: plot data
            formatting: string flag for desired formatting
            y2: optional second plot data
            y3: optional third plot data
            """
            self.ax = ax
            self.lx = ax.axhline(y = min(y), color = 'k')  #the horiz line
            self.ly = ax.axvline(x = min(x), color = 'k')  #the vert line
            self.x = x
            self.y = y
            self.y2 = y2
            self.y3 = y3
            # text location in axes coords
            self.txt = ax.text(0.6, 0.9, '', transform = ax.transAxes)
            self.formatting = formatting
    
        def format(self, x, y):
            if self.formatting == 'minsec':
                return 'x={0:d}, y='.format(x) + minsec(y, 0)
    
            if self.formatting == 'decimal':
                return 'x={0:d}, y={1:d}'.format(x, int(y))
    
            elif self.formatting == 'date decimal':
                return 'x={0:%d.%m.%Y}, y={1:d}'.format(x, int(y))
    
            elif self.formatting == 'decimal percent':
                return 'x={0:d}, y={1:d}%'.format(x, int(y * 100))
    
            elif self.formatting == 'float':
                return 'x={0:d}, y={1:.2f}'.format(x, y)
    
            elif self.formatting == 'float percent':
                return 'x={0:d}, y='.format(x) + percent(y, 0)
    
            elif self.formatting == 'daily euro':
                return u'day {0:d}: {1:.2f}€'.format(x, y)
    
        def mouse_move(self, event):
            if not event.inaxes:
                return
    
            mouseX, mouseY = event.xdata, event.ydata
            if type(self.x[0]) == datetime.datetime:
                mouseX = dates.num2date(int(mouseX)).replace(tzinfo = None)
    
            #searchsorted: returns an index or indices that suggest where mouseX should be inserted
            #so that the order of the list self.x would be preserved
            indx = np.searchsorted(self.x, [mouseX])[0]
    
            #if indx is out of bounds
            if indx >= len(self.x):
                indx = len(self.x) - 1
    
            #if y2 wasn't defined
            if self.y2 == None:
                mouseY = self.y[indx]
    
            #if y2 was defined AND y3 wasn't defined
            elif self.y3 == None: 
                if abs(mouseY - self.y[indx]) < abs(mouseY - self.y2[indx]):
                    mouseY = self.y[indx]
                else:
                    mouseY = self.y2[indx]
    
            #if y2 AND y3 were defined
            elif abs(mouseY - self.y2[indx]) < abs(mouseY - self.y[indx]):
                if abs(mouseY - self.y2[indx]) < abs(mouseY - self.y3[indx]):
                    mouseY = self.y2[indx]
                else:
                    mouseY = self.y3[indx]
            #lastly, compare y with y3
            elif abs(mouseY - self.y[indx]) < abs(mouseY - self.y3[indx]):
                mouseY = self.y[indx]
            else:
                mouseY = self.y3[indx]
    
            #update the line positions
            self.lx.set_ydata(mouseY)
            self.ly.set_xdata(mouseX)
    
            self.txt.set_text(self.format(mouseX, mouseY))
            plot.draw()
    
    • 0
    • Reply
    • Share
      Share
      • Share on Facebook
      • Share on Twitter
      • Share on LinkedIn
      • Share on WhatsApp
      • Report

Sidebar

Related Questions

I have some data that I want to display as a box plot using
I have two sets of data, (Ax, Ay; Bx, By). I'd like to plot
I have a graph library that I use to plot some data. This data
I have a data which I want to plot in cumulative function. The R
I have the following data set that I am trying to plot with ggplot2,
I have the data in two files. I want to plot a graph the
I have a bunch of histograms to plot on data that is still coming.
I have a flot chart that calculates the max Y-axis value based on the
I have some data that I am using to plot a histogram. I also
I have three data sets of different lengths and I would like to plot

Explore

  • Home
  • Add group
  • Groups page
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Users
  • Help
  • SEARCH

Footer

© 2021 The Archive Base. All Rights Reserved
With Love by The Archive Base

Insert/edit link

Enter the destination URL

Or link to existing content

    No search term specified. Showing recent items. Search or use up and down arrow keys to select an item.