Related
I want to save an instance of a plot into an object so that I can display it later by just calling that object.
import numpy as np
import matplotlib.pyplot as plt
x = np.arange(0, 10, 0.1)
y1 = x
y2 = np.sin(x)
plt.plot(x, y1, linewidth=1, color = 'deepskyblue')
fig1 = plt.gcf()
plt.plot(x, y2, linewidth=1, color = 'red')
fig2 = plt.gcf()
In this example, I first draw a blue line (y1=x) and use plt.gcf() to save an instance of this plot in fig1. Then I add a red curve (y2=sin(x)) to the plot and use plt.gcf() again to save this plot in fig2. Now, I expect that when I call fig1 I only get the blue line, and when I call fig2 I get both lines. Like this (I'm in Jupyter):
fig1 # or fig1.show() if not in Jupyter
Only blue curve
fig2
Both curves
But, in reality, when I call fig1 and fig2, both of them show both curves (like the second picture). Can someone please help how I can correctly get an instance of each plot so that I can display each of them later whenever I want?
You need to force matplotlib to actually draw the figure by setting a plot.show() in your code:
import numpy as np
import matplotlib.pyplot as plt
x = np.arange(0, 10, 0.1)
y1 = x
y2 = np.sin(x)
plt.plot(x, y1, linewidth=1, color = 'deepskyblue')
plt.show()
fig1 = plt.gcf()
plt.plot(x, y2, linewidth=1, color = 'red')
plt.show()
fig2 = plt.gcf()
Using the function plt.plot() always plots to the current axis (if no axis is present, a new one is created).
You can also tell matplotlib explicitly to open a new figure:
import numpy as np
import matplotlib.pyplot as plt
x = np.arange(0, 10, 0.1)
y1 = x
y2 = np.sin(x)
# open first figure
fig1 = plt.figure()
# plot
plt.plot(x, y1, linewidth=1, color = 'deepskyblue')
# open second figure
fig2 = plt.figure()
#plot
plt.plot(x, y2, linewidth=1, color = 'red')
Although this already fixes your problem, it is considered good practice to use an object-oriented version of this like this:
import numpy as np
import matplotlib.pyplot as plt
x = np.arange(0, 10, 0.1)
y1 = x
y2 = np.sin(x)
# open first figure, axis
fig1, ax1 = plt.subplots()
# plot
ax1.plot(x, y1, linewidth=1, color = 'deepskyblue')
# open second figure, axis
fig2, ax2 = plt.subplots()
#plot
ax2.plot(x, y2, linewidth=1, color = 'red')
In all cases, you will get:
Now, why don't you need plt.show() in the other approaches? Well, matplotlib by explicitly opening a new figure/axis, it is obvious that the previous axis is finished and can be drawn.
The last approach is the clearest as you tell exactly which figure and which axis you are considering.
i wanted to know how to make a plot with two y-axis so that my plot that looks like this :
to something more like this by adding another y-axis :
i'm only using this line of code from my plot in order to get the top 10 EngineVersions from my data frame :
sns.countplot(x='EngineVersion', data=train, order=train.EngineVersion.value_counts().iloc[:10].index);
I think you are looking for something like:
import matplotlib.pyplot as plt
x = [1,2,3,4,5]
y = [1000,2000,500,8000,3000]
y1 = [1050,3000,2000,4000,6000]
fig, ax1 = plt.subplots()
ax2 = ax1.twinx()
ax1.bar(x, y)
ax2.plot(x, y1, 'o-', color="red" )
ax1.set_xlabel('X data')
ax1.set_ylabel('Counts', color='g')
ax2.set_ylabel('Detection Rates', color='b')
plt.show()
Output:
#gdubs If you want to do this with Seaborn's library, this code set up worked for me. Instead of setting the ax assignment "outside" of the plot function in matplotlib, you do it "inside" of the plot function in Seaborn, where ax is the variable that stores the plot.
import seaborn as sns # Calls in seaborn
# These lines generate the data to be plotted
x = [1,2,3,4,5]
y = [1000,2000,500,8000,3000]
y1 = [1050,3000,2000,4000,6000]
fig, ax1 = plt.subplots() # initializes figure and plots
ax2 = ax1.twinx() # applies twinx to ax2, which is the second y axis.
sns.barplot(x = x, y = y, ax = ax1, color = 'blue') # plots the first set of data, and sets it to ax1.
sns.lineplot(x = x, y = y1, marker = 'o', color = 'red', ax = ax2) # plots the second set, and sets to ax2.
# these lines add the annotations for the plot.
ax1.set_xlabel('X data')
ax1.set_ylabel('Counts', color='g')
ax2.set_ylabel('Detection Rates', color='b')
plt.show(); # shows the plot.
Output:
Seaborn output example
You could try this code to obtain a very similar image to what you originally wanted.
import seaborn as sb
from matplotlib.lines import Line2D
from matplotlib.patches import Rectangle
x = ['1.1','1.2','1.2.1','2.0','2.1(beta)']
y = [1000,2000,500,8000,3000]
y1 = [3,4,1,8,5]
g = sb.barplot(x=x, y=y, color='blue')
g2 = sb.lineplot(x=range(len(x)), y=y1, color='orange', marker='o', ax=g.axes.twinx())
g.set_xticklabels(g.get_xticklabels(), rotation=-30)
g.set_xlabel('EngineVersion')
g.set_ylabel('Counts')
g2.set_ylabel('Detections rate')
g.legend(handles=[Rectangle((0,0), 0, 0, color='blue', label='Nontouch device counts'), Line2D([], [], marker='o', color='orange', label='Detections rate for nontouch devices')], loc=(1.1,0.8))
I want to change y axis ticks in python. I am using the code
import pylab as plt
y1 = [0,1,2,...10]
y2 = [90,40,65,12,....]
labels = [0.30,0.29,0.28,....]
plt.plot(y1)
plt.plot(y2,'r')
plt.yticks(y1, labels)
plt.yticks(y2, labels)
plt.show()
But all the y axis label appear at one place on top of one another
Borrowing heavily from this example, the code below demonstrates one possible way to have two plots on one figure.
import pylab as plt
fig, ax1 = plt.subplots()
y1 = [0,1,2,3,4,5,6,7,8,9,10]
labels = [0.30,0.29,0.28,0.27,0.26,0.25,0.24,0.23,0.22,0.21,0.20]
ax1.plot(labels, y1, 'b-')
ax1.set_xlabel('labels')
# Make the y-axis label, ticks and tick labels match the line color.
ax1.set_ylabel('y1', color='b', rotation="horizontal")
ax1.tick_params('y', colors='b')
ax2 = ax1.twinx()
y2 = [90,40,65,12,23,43,54,13,42,53,63]
ax2.plot(labels, y2, 'r-')
ax2.set_ylabel('y2', color='r', rotation="horizontal")
ax2.tick_params('y', colors='r')
fig.tight_layout()
plt.show()
What is wrong with my residual plot that is causing to not be aligned with my main graph? My code is below.
import matplotlib.pyplot as plt
from scipy import stats
import numpy as np
x = np.array([0.030956,0.032956,0.034956,0.036956,0.038956,0.040956])
y = np.array([10.57821088,11.90701212,12.55570876,13.97542486,16.05403248,16.36634177])
yerr = [0.101614114,0.363255259,0.057234211,0.09289917,0.093288198,0.420165796]
xerr = [0.00021]*len(x)
fig1 = plt.figure(1)
frame1=fig1.add_axes((.1,.3,.8,.6))
m, b = np.polyfit(x, y, 1)
print 'gradient',m,'intercept',b
plt.plot(x, m*x + b, '-', color='grey', alpha=0.5)
plt.plot(x,y,'.',color='black',markersize=6)
plt.errorbar(x,y,xerr=0,yerr=yerr,linestyle="None",color='black')
plt.ylabel('$1/\sqrt{F}$ $(N)$',fontsize=20)
plt.autoscale(enable=True, axis=u'both', tight=True)
plt.grid(False)
frame2=fig1.add_axes((.1,.1,.8,.2))
s = m*x+b #(np.sqrt(4*np.pi*8.85E-12)/2.23E-8)*x
difference = y-s
plt.plot(x, difference, 'ro')
frame2.set_ylabel('$Residual$',fontsize=20)
plt.xlabel('$2s+d_0$ $(m)$',fontsize=20)
you can specify the axis limits. the problem is that autoscale is moving your two plots differently. if you insert 2 lines of code, each specifying the axis limits, it will fix it.
plt.axis([.030,.0415, 10, 17]) #line 17
plt.axis([.030,.0415, -.6, .8]) #line 26
i believe this is what you're looking for.
Try using GridSpec.
from matplotlib import gridspec
fig = plt.figure()
gs = gridspec.GridSpec(2, 1, height_ratios=[3, 1])
ax0 = plt.subplot(gs[0])
ax1 = plt.subplot(gs[1])
ax0.plot(x, m*x + b, '-', color='grey', alpha=0.5)
ax0.plot(x,y,'.',color='black',markersize=6)
ax1.plot(x, difference, 'ro')
And use set_ylabel instead of ylabel (which you use for plt for example) for axes.
I'm writing a program, which has two outputs: a GUI and a printed report on paper (a simple pdf printed out).
On both outputs I would like to have a diagram, but with different styles.
dark_background on the GUI (http://matplotlib.org/examples/style_sheets/plot_dark_background.html)
and fivethirtyeight on the paper http://matplotlib.org/examples/style_sheets/plot_fivethirtyeight.html
Somehow I could not manage to generate 2 images with proper styles. Only one of them was correct. I do not have enough experience-points to post pictures. So I will post only my code.
My first idea was:
import numpy as np
import matplotlib.pyplot as plt
def plot():
#set pic size
fig = plt.figure(figsize=(16, 9), dpi=100)
ax = plt.subplot(111)
# set x data
x = range(10)
# set y data
y1 = np.zeros(10)
y2 = [0,1,2,3,4,5,6,7,8,9]
y3 = [10,9,8,7,6,5,4,3,2,1]
#plot as errorbar
ax.errorbar(x, y1, fmt='o', color='green', markersize=8, label='Normal')
ax.errorbar(x, y2, yerr=0.1, fmt='o', color='orange', markersize=8, label='abw_up')
ax.errorbar(x, y3, yerr=0.1, fmt='o', color='purple', markersize=8,label='abw_down')
# limits
ax.axhline(0.1*10, color='red', lw=2)
ax.axhline(-0.1*10, color='red', lw=2)
#set limit of y-Axis
ax.set_ylim((-1.3,1.3))
# Labels
ax.set_xlabel('points number')
ax.set_ylabel('values')
# legend
legend=ax.legend(loc=('upper center'), shadow='true',bbox_to_anchor=(0.5, 1.05),ncol=3, fancybox=True)
plt.style.use('dark_background')
plt.savefig('result_dark.png')
plt.style.use('fivethirtyeight')
plt.savefig('result_white.png')
But it did not work properly. One of the images was correct. The second had a correct backgroundcolor, but the fontcolor of legend/labels did not change. I tried to separate the 2 images, the result was the same:
import numpy as np
import matplotlib.pyplot as plt
import os
def plot():
#set pic size
ax = plt.subplot(111)
# set x data
x = range(10)
# set y data
y1 = np.zeros(10)
y2 = [1,2,3,1,2,3,1,2,3,1]
y3 = [3,1,2,3,1,2,3,1,2,3]
#plot as errorbar
ax.errorbar(x, y1, fmt='o', color='green', markersize=8, label='Normal')
ax.errorbar(x, y2, yerr=0.2, fmt='o', color='orange', markersize=8, label='abw_up')
ax.errorbar(x, y3, yerr=0.1, fmt='o', color='purple', markersize=8,label='abw_down')
# limits
ax.axhline(0.1*10, color='red', lw=2)
ax.axhline(-0.1*10, color='red', lw=2)
#set limit of y-Axis
ax.set_ylim((-1.3,5.3))
# Labels
ax.set_xlabel('Messpunkte-Nr.\nMeasurement points number')
ax.set_ylabel('Spezifikationsgrenze normiert\nnormed to specification')
# legend
legend=ax.legend(loc=('upper center'), shadow='true',bbox_to_anchor=(0.5, 1.05),ncol=3, fancybox=True)
texts =legend.get_texts()
texts[0].set_color('green')
texts[1].set_color('orange')
texts[2].set_color('purple')
fig = plt.figure(figsize=(16, 9), dpi=100)
plt.style.use('dark_background')
plot()
plt.savefig('result_dark.png')
plt.clf()
#plt.close()
fig = plt.figure(figsize=(16, 9), dpi=100)
plt.style.use('fivethirtyeight')
plot()
plt.savefig('result_white.png')
plt.clf()
#plt.close()
How should I fix my code to have 2 images with the same values, but different styles?
I would suggest structuring you code something like:
from matplotlib.style import context
def my_plot_function(ax, data, style):
# do all of your plotting in here, should be fast as no computation
pass
with context('dark'):
fig, ax = plt.subplots(1, 1)
my_plot_function(ax, data, style)
fig.savefig(...)
with context('fivethirtyeight'):
fig, ax = plt.subplots(1, 1)
my_plot_function(ax, data, style)
fig.savefig(...)
This is a design feature, not a bug. Almost all of the values controlled by rcparams are set at object creation time, not a draw time, because having what your figure will look like when you render it depend on global state is terrifying. This also allows you to use context managers for the rcparams, as shown above. Calling use only over-rides the values that the style sheet explicitly sets (which is also a design feature so you can apply multiple styles a-la cascading style sheets).
So your problem appears to be that you do a lot of plotting and then tell pylab you'd like your plots to have a particular style. That instruction doesn't seem to be updating everything. So instead, tell it you want to use a particular style. Then plot. Then clear everything. Then plot again.
import numpy as np
import matplotlib.pyplot as plt
def plot():
#set pic size
fig = plt.figure(figsize=(16, 9), dpi=100)
ax = plt.subplot(111)
# set x data
x = range(10)
# set y data
y1 = np.zeros(10)
y2 = [0,1,2,3,4,5,6,7,8,9]
y3 = [10,9,8,7,6,5,4,3,2,1]
#plot as errorbar
ax.errorbar(x, y1, fmt='o', color='green', markersize=8, label='Normal')
ax.errorbar(x, y2, yerr=0.1, fmt='o', color='orange', markersize=8, label='abw_up')
ax.errorbar(x, y3, yerr=0.1, fmt='o', color='purple', markersize=8,label='abw_down')
# limits
ax.axhline(0.1*10, color='red', lw=2)
ax.axhline(-0.1*10, color='red', lw=2)
#set limit of y-Axis
ax.set_ylim((-1.3,1.3))
# Labels
ax.set_xlabel('points number')
ax.set_ylabel('values')
# legend
legend=ax.legend(loc=('upper center'), shadow='true',bbox_to_anchor=(0.5, 1.05),ncol=3, fancybox=True)
plt.style.use('dark_background')
plot()
plt.savefig('result_dark.png')
plt.clf()
plt.style.use('fivethirtyeight')
plot()
plt.savefig('result_white.png')
Does this give what you want? Here are the figures I got.