I need to know how can I move the rectangle patch when I click anywhere with the mouse ?
in the code below the rectangle is fixed I just need to move it every time I click with the mouse somewhere ,
import matplotlib.pyplot as plt
import matplotlib.patches as patches
x=y=0.1
fig1 = plt.figure()
ax1 = fig1.add_subplot(111, aspect='equal')
patch= ax1.add_patch(patches.Rectangle((x, y), 0.5, 0.5,
alpha=1, fill=None,label='Label'))
plt.show()
maybe i need to use "motion_notify_event" to connect mouse to rectangle but id'ont know how i can use this function !
my second question is how to get this type of rectangle "selection icon" on the image with matplotlib or if possible to customize the rectangle patch !
thank you in advance
To move the rectangle around you can use a simple function that connects to a "button press event" via fig.canvas.mpl_connect('button_press_event', <function_name>) and re-defines the x, y origin coordinates of the rectangle. I have shifted those by half the width and height of the rectangle, so that the point you click on will be in its centre.
import matplotlib.pyplot as plt
import matplotlib.patches as patches
def on_press(event):
xpress, ypress = event.xdata, event.ydata
w = rect.get_width()
h = rect.get_height()
rect.set_xy((xpress-w/2, ypress-h/2))
ax.lines = []
ax.axvline(xpress, c='r')
ax.axhline(ypress, c='r')
fig.canvas.draw()
x = y = 0.1
fig = plt.figure()
ax = fig.add_subplot(111, aspect='equal')
fig.canvas.mpl_connect('button_press_event', on_press)
rect = patches.Rectangle((x, y), 0.1, 0.1, alpha=1, fill=None, label='Label')
ax.add_patch(rect)
plt.show()
As for the prettyfying of the rectangle, have a look at the
matplotlib patches or the gallery and see if you find something suitable. I have added a crosshair with red lines as an alternative.
Related
Is it possible to set ax.grid in such a way that lines will go just to bars?
Below the regular output("before") and expected("after"):
My code:
fig, ax = plt.subplots(figsize=(15,6))
ax.set_axisbelow(True)
ax = data_test.bar(fontsize=15, zorder=1, color=(174/255, 199/255, 232/255)) # 'zorder' is bar layaut order
for p in ax.patches:
ax.annotate(s=p.get_height(),
xy=(p.get_x()+p.get_width()/2., p.get_height()),
ha='center',
va='center',
xytext=(0, 10),
textcoords='offset points')
ax.spines["right"].set_visible(False)
ax.spines["left"].set_visible(False)
ax.spines["top"].set_visible(False)
ax.spines["bottom"].set_visible(False)
ax.set_xticklabels(
data_test.index,
rotation=34.56789,
fontsize='xx-large'
) # We will set xticklabels in angle to be easier to read)
# The labels are centred horizontally, so when we rotate them 34.56789°
ax.grid(axis='y', zorder=0) # 'zorder' is bar layaut order
plt.ylim([4500, 5300])
plt.show()
You could draw horizontal lines instead of using grid lines.
You forgot to add test data, making it quite unclear of what type data_test could be.
The code below supposes data_test is a pandas dataframe, and that data_test.plot.bar() is called to draw a bar plot. Note that since matplotlib 3.4 you can use ax.bar_label to label bars.
from matplotlib import pyplot as plt
import pandas as pd
import numpy as np
data_test = pd.DataFrame({'height': np.random.randint(1000, 2000, 7).cumsum()},
index=['Alkaid', 'Mizar', 'Alioth', 'Megrez', 'Phecda', 'Merak', 'Dubhe'])
fig, ax = plt.subplots(figsize=(15, 6))
ax.set_axisbelow(True)
data_test.plot.bar(fontsize=15, zorder=1, color=(174 / 255, 199 / 255, 232 / 255), ax=ax)
for container in ax.containers:
ax.bar_label(container, fmt='%.0f', fontsize=15)
for spine in ax.spines.values():
spine.set_visible(False)
ax.set_xticklabels(data_test.index, rotation=34.56789, fontsize='xx-large')
ax.tick_params(length=0) # remove tick marks
xmin, xmax = ax.get_xlim()
ticks = ax.get_yticks()
tick_extends = [xmax] * len(ticks)
# loop through the bars and the ticks; shorten the lines whenever a bar crosses it
for bar in ax.patches:
for j, tick in enumerate(ticks):
if tick <= bar.get_height():
tick_extends[j] = min(tick_extends[j], bar.get_x())
ax.hlines(ticks, xmin, tick_extends, color='grey', lw=0.8, ls=':', zorder=0)
plt.tight_layout()
plt.show()
The issue I would like you to figure out is about the coordinantes appearence on matplotlib graph with a double y axis. First of all a code on Jupyter Notebook which draws a graph with two lines and only one y axis (for some unknown reasons I have to run it two times in order to make it working correctly)
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.mlab as mlab
from IPython.display import display
from IPython.core.display import display, HTML #display multiple output on a cell
display(HTML("<style>.container { width:100% !important; }</style>")) # improve cells horizontal size
from IPython.core.interactiveshell import InteractiveShell # It saves you having to repeatedly type "Display"
InteractiveShell.ast_node_interactivity = "all"
%matplotlib notebook
x = np.arange(0, 10, 0.01)
y1 = np.sin(np.pi*x)/(np.pi*x)
y2 = abs(np.tan(0.1*np.pi*x))
plt.figure()
plt.plot(x, y1)
plt.plot(x, y2)
plt.ylim(0, 3)
plt.grid()
plt.show()
The present figure provides the two lines with cursor coordinates on the right bottom part of the graph.
The following code
import pandas as pd
import os
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.mlab as mlab
from IPython.display import display
from IPython.core.display import display, HTML #display multiple output on a cell
display(HTML("<style>.container { width:100% !important; }</style>")) # improve cells horizontal size
from IPython.core.interactiveshell import InteractiveShell # It saves you having to repeatedly type "Display"
InteractiveShell.ast_node_interactivity = "all"
%matplotlib notebook
x = np.arange(0, 10, 0.01)
y1 = np.sin(np.pi*x)/(np.pi*x)
y2 = abs(np.tan(0.1*np.pi*x))
# Create some mock data
fig, ax1 = plt.subplots()
plt.grid()
color = 'tab:red'
ax1.set_xlabel('Time (days from 24 February)')
ax1.set_ylabel('Death cases/Intensive care', color=color)
#ax1.set_xlim(0, 15)
#ax1.set_ylim(0, 900)
ax1.plot(x, y1, '-', color=color, label = 'Left hand scale')
ax1.tick_params(axis='y', labelcolor=color)
ax1.legend(loc = 'upper left')
ax2 = ax1.twinx()
color = 'tab:blue'
ax2.set_ylabel('Total cases/currently positive', color=color) # we already handled the x-label with ax1
ax2.plot(x, y2, '-', color=color, label = 'Right hand scale')
ax2.set_ylim(0, 20)
ax2.tick_params(axis='y', labelcolor=color)
ax2.legend(loc = 'lower right')
fig.tight_layout()
plt.show()
Shows the following graph
Which shows a graph with TWO y scales, one red on the left side and one blue on the right side. The problem here is that in the left bottom side of the picture there are the cursor coordinates related to the right scale and nothing about the left one. Is there a way to show up both the two scales?
Depending on your precise needs, mplcursors seems helpful. Mplcursors allows a lot of ways to customize, for example you can show both y-values together with the current x. Or you could suppress the annotation and only write in the status bar.
Setting hover=True constantly displays the plotted values when the mouse hovers over a curve. Default, the values would only be displayed when clicking.
import matplotlib.pyplot as plt
import numpy as np
import mplcursors
# Create some test data
x = np.arange(0, 10, 0.01)
y1 = np.sin(np.pi * x) / (np.pi * x)
y2 = abs(np.tan(0.1 * np.pi * x))
fig, ax1 = plt.subplots()
plt.grid()
color = 'tab:red'
ax1.set_xlabel('Time (days from 24 February)')
ax1.set_ylabel('Death cases/Intensive care', color=color)
lines1 = ax1.plot(x, y1, '-', color=color, label='Left hand scale')
ax1.tick_params(axis='y', labelcolor=color)
ax1.legend(loc='upper left')
ax2 = ax1.twinx()
color = 'tab:blue'
ax2.set_ylabel('Total cases/currently positive', color=color) # we already handled the x-label with ax1
lines2 = ax2.plot(x, y2, '-', color=color, label='Right hand scale')
ax2.set_ylim(0, 20)
ax2.tick_params(axis='y', labelcolor=color)
ax2.legend(loc='lower right')
cursor1 = mplcursors.cursor(lines1, hover=True)
cursor2 = mplcursors.cursor(lines2, hover=True)
fig.tight_layout()
plt.show()
I am trying to add a point on a surface displayed with the function plot_trisurf. The point is present, but seems to be drawn before the surface, so that the only way to see it is to make the surface transparent. I would like this point to appear on the surface even if the surface is not transparent. Do you have any idea of how to do that ?
An example of the problem would be the following :
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import pyplot as plt
Latitude = np.repeat(np.linspace(45.1525, 45.17, 1000), 15)
Longitude = np.array(list(np.linspace(5.9, 5.94, 1000))*15)
x = np.linspace(0,3,len(Latitude))
Altitude = np.sin(x)*np.cos(x**2)
fig = plt.figure(figsize=(15, 9))
ax = fig.gca(projection="3d")
ax.view_init(30, 250)
surf = ax.plot_trisurf(
Latitude,
Longitude,
Altitude,
cmap=plt.cm.jet,
linewidth=0,
alpha=0.2,
edgecolor=None,
antialiased=False,
)
s = ax.scatter(
Latitude[10600],
Longitude[10600],
Altitude[10600],
c="black",
marker="*",
s=100,
depthshade=False,
)
plt.title((Latitude[10600], Longitude[10600], Altitude[10600]))
plt.show()
I also tried with scatter3D, and by increasing a little the altitude of the point with respect to the surface but nothing worked.
Thanks a lot for your help !
Have tried to implement multiple plots on shared x axis with a common slider . On slider update , there is too much screen flicker . How can this be avoided . Here is the code sample i have used.
%matplotlib inline
from ipywidgets import interactive
import matplotlib.pyplot as plt
import numpy as np
''' 30% window size on the selected time on slider'''
data_size=round(M.Timestamp.size*0.30)
plt.close('all')
def f(m):
plt.figure(2)
x=M['Timestamp']
y1=M['Value']
'''define boundary limits for both axis'''
min_x=0 if m-data_size < 0 else m-data_size
max_x=M.Timestamp.size if m+data_size > M.Timestamp.size else m+data_size
f, (ax1, ax2, ax3) = plt.subplots(3, sharex=True, sharey=True)
ax1.plot(x[min_x:max_x],y1[min_x:max_x],color='r')
ax1.set_title('Sharing both axes')
ax2.plot(x[min_x:max_x],y1[min_x:max_x],color='b')
ax3.plot(x[min_x:max_x],y1[min_x:max_x],color='g')
plt.xticks(rotation=30)
interactive(f, m=(0, M.Timestamp.size))
When tried to update the xlimit on slider movement the graph is blank , hence used the subset of data to update on plots
Solved the issue with following settings.
Used a selection slider with continuous_update =False
on startup load the graph and manipulate only the xlim with plt.xlim(min_x,max_x) with the slider functionality
snippet of the implementation below.
selection_range_slider = widgets.SelectionRangeSlider(
options=options,
index=index,
description='Time slider',
orientation='horizontal',
layout={'width': '1000px'},
continuous_update=False
)
#selection_range_slider
def print_date_range(date_range):
print(date_range)
plt.figure(num=None, figsize=(15, 4), dpi=80, facecolor='w', edgecolor='k')
min_x=date_range[0]
max_x=date_range[1]
ax1 = plt.subplot(311)
plt.plot(Data_1.Timestamp,Data_1.value,'r')
plt.setp(ax1.get_xticklabels(), fontsize=6,visible=False)
plt.xlabel('Data_1')
ax1.xaxis.set_label_coords(1.05, 0.5)
# share x only
ax2 = plt.subplot(312, sharex=ax1)
plt.plot(Data_2.Timestamp,Data_2.value,'b')
# make these tick labels invisible
plt.setp(ax2.get_xticklabels(), visible=False)
plt.xlabel('Data_2')
ax2.xaxis.set_label_coords(1.05, 0.5)
# share x and y
ax3 = plt.subplot(313, sharex=ax1)
plt.plot(Data_3.Timestamp,Data_3.value,'g')
ax3.xaxis.set_label_coords(1.05, 0.5)
#plt.xlim(0.01, 5.0)
plt.xlim(min_x,max_x)
plt.show()
#plt.xlabel('Data_3')
widgets.interact(
print_date_range,
date_range=selection_range_slider
);
I'm trying to draw a box on a map in relative coordinates (i.e. 0 to 1). The reason is I have a colorbar on my map, but cannot see it clearly. I want a transparent box behind it. I've looked at adding patch Rectangles (see Draw rectangle (add_patch) in pylab mode), but that is in data coordinates, which is not easy to determine on this map. I also found axhspan, which uses relative coordinates for the x span, but data coordinates for the y span.
Is there a way to draw a box in a matplotlib axes object using relative coordinates?
Here's a way to add a boxed text to a relative coordinates:
#!/usr/bin/python3
from matplotlib import pyplot as plt
x = range(5)
y = range(5)
fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(x, y)
ax.text(0.5, 0.5,
"Relative coords!",
horizontalalignment = 'center',
backgroundcolor = "white",
verticalalignment = 'center',
bbox=dict(facecolor='white', edgecolor='green', alpha=0.65),
transform = ax.transAxes,
)
fig.savefig("mwe.png")
Result:
Edit:
To draw just a box given it's relative coordinates/dimensions with no text in it:
#!/usr/bin/python3
from matplotlib import pyplot as plt
from matplotlib.patches import Rectangle
x = range(5)
y = range(5)
fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(x, y, zorder=1)
plt.gca().add_patch(Rectangle(
(0.4, 0.4), # lower left point of rectangle
0.2, 0.2, # width/height of rectangle
transform=ax.transAxes,
facecolor="white",
edgecolor='green',
alpha=0.65,
zorder=2,
))
fig.savefig("mwe.png")
Result: