Is it possible to control matplotlib in predictable way? - python

The following code behaves absolutely ununderstandable for me:
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
import numpy as np
img=mpimg.imread('stinkbug.png')
imgplot = plt.imshow(img)
circle = plt.Circle((0, 0), radius=100, fc='y')
plt.figure(0)
#plt.show(imgplot)
plt.show(circle)
It displays two figures, although no only one show() function called.
It displays stinkbug in figure, although imgplot was never shown.
It does not display circle, although circle was shown.

You are telling matplotlib to do the following:
Load an image (... so far so good)
Create a figure displaying the image (Figure 1 by default) (... so far so good)
Create a patch object that represents a circle. This is not associated with any axes or anything where it could be drawn.
Create an empty Figure 0. Why? We may never know.
Call plt.show() with a patch as an argument. Because matplotlib is being nice, it ignores this argument and just displays the two figures as predicted.
Some Notes
Patch objects are just representations of a shape. You have to plot them somewhere for them to work.
plt.show() just displays all the figures if you are not in interactive mode.
A Solution
Given all that, here is what I think you were trying to do:
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
import numpy as np
img = mpimg.imread('stinkbug.png')
circle = plt.Circle((0, 0), radius=100, fc='y')
fig, ax = plt.subplots()
ax.imshow(img)
ax.add_artist(circle)
fig.show()
subplots creates both a figure and axes for you. You can then use ax.imshow to display the image and ax.add_artist to display the circle. fig.show() and plt.show() are identical in this case.

Related

Rightmost part of axes disappears in Matplotlib PostScript figure

I'm creating a Matplotlib figure, which I need to be quite wide (174 mm) and in .eps format. I also need it to be created with LaTeX for consistency with other figures. The problem is that the rightmost parts of the axes do not appear in the output figure, and the legend's box and handles also disappear.
The problem appears only if the figure if very wide, when I use LaTeX to produce it, and when I save it in .eps. The figure is as expected if it is thinner, if I save it in .pdf or .png, or if I just replace plt.savefig(...) with plt.show() and use Matplotlib's default viewer.
To be clearer, consider the following code.
import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
x = np.linspace(-1, 1, 100)
y = np.exp(x)
mpl.rcParams['text.usetex'] = True
mm = 1/25.4
fig = plt.figure(figsize=(174*mm, 44*mm))
plt.plot(x, y, label='exponential')
plt.legend(loc='lower right')
plt.tight_layout()
plt.savefig('test.eps')
This outputs the following figure, where the legend handle and the rightmost part of the axes do not appear.
If it can help, the .eps file output by the above code is available here.

Creating a histogram of Yearly Returns

I am trying to finish a task for a project and my task is to create a histogram of yearly returns of Dow Jones historical returns. I have uploaded a picture of the task and my progress below. The problem I have at this point is that I can't find a way to separate the years in the histogram as it shows in the task and I don't know how to modify the y-axix and the legend to show the information that is showing in the first picture.
Any help is appreciated
What I am trying to make and My progress so far
Here is my code:
# Importing packages
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import datetime
#setting the order
order=[-60,-50,-40,-30,-20,-10,
0,10,20,30,40,50,60,70]
#getting the data
dow_jones_returns = pd.read_csv('data/dow-jones-by-year-historical-annual-returns (2).csv')
dow_jones=pd.DataFrame(data=dow_jones_returns)
dow_jones['date']=pd.to_datetime(dow_jones['date'])
dow_jones['date']=pd.DatetimeIndex(dow_jones['date']).year
pd.to_numeric(dow_jones.value)
up_to_2019=dow_jones.iloc[0:99]
lastyear= dow_jones.iloc[-1]
#ploting the histogram
fig = plt.figure()
up_to_2019['value'].plot.hist(bins = order)
plt.show()
Hi to just give you some further directions,
Regarding the Textbox
the textbox looks like it contains the summary statistics of DataFrame.describe() + a few additional ones. You can create a textbox by utilzing a combination of .text() and .subplot()
I found this guide to be very useful for creating a textbox in a plot
Since we dont have the data,
here a pseudo code:
import numpy as np
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
textstr = str(up_to_2019['value'].describe())
ax.hist(up_to_2019['value'], bins = order)
# these are matplotlib.patch.Patch properties
props = dict(boxstyle='round', facecolor='wheat', alpha=0.5)
# place a text box in upper left in axes coords
ax.text(0.05, 0.95, textstr, transform=ax.transAxes, fontsize=10,
verticalalignment='top', bbox=props)
plt.show()
Regarding the y-axis:
1) Here is how you set the right label: plt.ylabel("Number of Observations\n(Probability in%)")
2) Than add the Ticks plt.yticks(np.arange(1,27))
Regarding the labels inside the bins
Thats rather tricky, one option, though definitely not advised would to also include the labels via the .text() method. I dont know if it helps but here is how you do this in R.
Also might helpful are these two links:
how-to-add-a-text-into-a-rectangle
Change color for the patches in a hist
Apparently calling plt.hist() has three return values one of which is callled patches. You can iterate over patches and i.e. change the color of these (see the link above) however I couldn't figure how to put a text to them.
import numpy as np
import matplotlib.mlab as mlab
import matplotlib.pyplot as plt
x = [21,22,23,4,5,6,77,8,9,10,31,32,33,34,35,36,37,18,49,50,100]
num_bins = 5
n, bins, patches = plt.hist(x, num_bins, facecolor='blue', alpha=0.5)
for i,pat in enumerate(patches):
pat.set_test("Test") #this doesnt work sadly

Scale legend matplotlib [duplicate]

Any ideas on how can I insert a scale bar in a map in matplotlib that shows the length scale? something like the one I have attached.
Or maybe any ideas on measuring and showing distances automatically (not drawing an arrow and writing the distance manually!)?
Thanks :)
There is a an already existing class for scalebars in matplotlib called AnchoredSizeBar. In the below example AnchoredSizeBar is used to add a scalebar to an image (or map over a 100x100 meter area of randomness).
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1.anchored_artists import AnchoredSizeBar
import matplotlib.font_manager as fm
fontprops = fm.FontProperties(size=18)
fig, ax = plt.subplots()
ax.imshow(np.random.random((10,10)),extent=[0,100,0,100])
Extent defines the images max and min of the horizontal and vertical values.
scalebar = AnchoredSizeBar(ax.transData,
20, '20 m', 'lower center',
pad=0.1,
color='white',
frameon=False,
size_vertical=1,
fontproperties=fontprops)
ax.add_artist(scalebar)
The four first arguments to AnchoredSizeBar are the transformation object of the coordinate system, scalebar length, label and location. Further optional arguments change the layout. These are explained in the documentation.
ax.set_yticks([])
ax.set_xticks([])
This gives
I would try the matplotlib-scalebar package. (For something like your example c.)
Assuming you are plotting a map image with imshow or similar, and you know the pixel width/cell-size (the real-world equivalent size of one pixel on the map image), you can automatically create the scale bar:
This example is straight off the PyPi matplotlib-scalebar package page but here it is for completeness:
import matplotlib.pyplot as plt
import matplotlib.cbook as cbook
from matplotlib_scalebar.scalebar import ScaleBar
plt.figure()
image = plt.imread(cbook.get_sample_data('grace_hopper.png'))
plt.imshow(image)
scalebar = ScaleBar(0.2) # 1 pixel = 0.2 meter
plt.gca().add_artist(scalebar)
plt.show()

How to insert scale bar in a map in matplotlib

Any ideas on how can I insert a scale bar in a map in matplotlib that shows the length scale? something like the one I have attached.
Or maybe any ideas on measuring and showing distances automatically (not drawing an arrow and writing the distance manually!)?
Thanks :)
There is a an already existing class for scalebars in matplotlib called AnchoredSizeBar. In the below example AnchoredSizeBar is used to add a scalebar to an image (or map over a 100x100 meter area of randomness).
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1.anchored_artists import AnchoredSizeBar
import matplotlib.font_manager as fm
fontprops = fm.FontProperties(size=18)
fig, ax = plt.subplots()
ax.imshow(np.random.random((10,10)),extent=[0,100,0,100])
Extent defines the images max and min of the horizontal and vertical values.
scalebar = AnchoredSizeBar(ax.transData,
20, '20 m', 'lower center',
pad=0.1,
color='white',
frameon=False,
size_vertical=1,
fontproperties=fontprops)
ax.add_artist(scalebar)
The four first arguments to AnchoredSizeBar are the transformation object of the coordinate system, scalebar length, label and location. Further optional arguments change the layout. These are explained in the documentation.
ax.set_yticks([])
ax.set_xticks([])
This gives
I would try the matplotlib-scalebar package. (For something like your example c.)
Assuming you are plotting a map image with imshow or similar, and you know the pixel width/cell-size (the real-world equivalent size of one pixel on the map image), you can automatically create the scale bar:
This example is straight off the PyPi matplotlib-scalebar package page but here it is for completeness:
import matplotlib.pyplot as plt
import matplotlib.cbook as cbook
from matplotlib_scalebar.scalebar import ScaleBar
plt.figure()
image = plt.imread(cbook.get_sample_data('grace_hopper.png'))
plt.imshow(image)
scalebar = ScaleBar(0.2) # 1 pixel = 0.2 meter
plt.gca().add_artist(scalebar)
plt.show()

How to efficiently replace element in plot with ipywidgets?

How can I efficiently display similar plots with ipywidgets using Jupyter Notebook?
I wish to plot interactively a heavy plot (heavy in the sense that it has lots of data points and takes some time to plot it) and modify a single element of it using interact from ipywidgets without replotting all the complicated plot. Is there a builtin functionality to do this?
basically what I'm trying to do is
import numpy as np
import matplotlib.pyplot as plt
from ipywidgets import interact
import matplotlib.patches as patches
%matplotlib inline #ideally nbagg
def complicated plot(t):
plt.plot(HEAVY_DATA_SET)
ax = plt.gca()
p = patches.Rectangle(something_that_depends_on_t)
ax.add_patch(p)
interact(complicatedplot, t=(1, 100));
Right now it takes up to 2 seconds for each replot. I expect there are ways to keep the figure there and just replace that rectangle.
A hack would be to create a figure of the constant part, make it background to the plot and just plot the rectangle part. but the sounds too dirty
Thank you
This is an rough example of an interactive way to change a rectangle width (I'm assuming you are in an IPython or Jupyter notebook):
import matplotlib
import matplotlib.pyplot as plt
import matplotlib.patches as patches
import ipywidgets
from IPython.display import display
%matplotlib nbagg
f = plt.figure()
ax = plt.gca()
ax.add_patch(
patches.Rectangle(
(0.1, 0.1), # (x,y)
0.5, # width
0.5, # height
)
)
# There must be an easier way to reference the rectangle
rect = ax.get_children()[0]
# Create a slider widget
my_widget = ipywidgets.FloatSlider(value=0.5, min=0.1, max=1, step=0.1, description=('Slider'))
# This function will be called when the slider changes
# It takes the current value of the slider
def change_rectangle_width():
rect.set_width(my_widget.value)
plt.draw()
# Now define what is called when the slider changes
my_widget.on_trait_change(change_rectangle_width)
# Show the slider
display(my_widget)
Then if you move the slider, the width of the rectangle will change. I'll try to tidy up the code, but you may have the idea. To change the coordinates, you have to do rect.xy = (x0, y0), where x0 and y0 are new coordinates.

Categories

Resources