Multiple separated graphs on Matplotlib - python

I am facing a simple problem I can't figure out: I am trying to plot multiple graphs with Matplotlib, but in separated graphs. This is an example code with random numbers:
import numpy as np
import matplotlib.pyplot as plt
x1 = np.random.normal(10, 1, 100)
x2 = np.random.uniform(8, 12, 100)
fig, ax = plt.subplots()
ax.plot(np.sort(x1), label = 'Normal')
ax.plot(np.sort(x2), label = 'Uniform')
plt.legend()
plt.show()
In this way I get 1 graph with 2 lines (the ax objects). I know that I can use Subplot to get 2 graphs next to each other, but what I want is to plot two different graphs, 1 for each variable, with 1 line each, all at once without starting a new code a initializing a new graph from the beginning.

Without using subplots, I think one (quite ugly) way to do this is
x1 = np.random.normal(10, 1, 100)
x2 = np.random.uniform(8, 12, 100)
data = [x1, x2]
label_list = ['Normal', 'Uniform']
for i, x in enumerate(data):
fig = plt.figure()
plt.plot(np.sort(x), label = label_list[i])
plt.legend()
plt.show()

Related

Python Plots - Plotting a subplots in a subplots

I want to plot a graph representing the changes as per the varying variables. The sample figure is shown below.
The idea is to plot subplot within a subplot. Note It is different from plotting a graph using subplot with a predefined number of rows and columns, i.e matplotlib.pyplot.subplots(nrows=2, ncols=2)
Can I plot such figures using matplotlib/seaborn?
I have drawn the frames and placed the axes inside the frames, everything is based on the no. of subplots/frame, the no. of rows and columns of the frames' grid and the physical dimensions of the different elements.
I imagine that most of the code is self explanatory, except the part where we place the axes in the precise locations, that's stolen from the Demo Fixed Size Axes, if you see points in need of elucidation please ask
import matplotlib
from mpl_toolkits.axes_grid1 import Divider, Size
from mpl_toolkits.axes_grid1.mpl_axes import Axes
import matplotlib.pyplot as plt
import numpy as np
from itertools import product
mm = lambda d: d/25.4
nplots = 2
wp, hp = mm(40), mm(28)
dxp, dyp = mm(16), mm(12)
nrows, ncols = 3, 2
wf, hf = nplots*(wp+dxp), hp+dyp
dxf, dyf = mm(10), mm(8)
xcorners, ycorners = (np.arange(dxf/2,ncols*(wf+dxf),wf+dxf),
np.arange(dyf/2,nrows*(hf+dyf),hf+dyf))
# plus 10 mm for suptitle
fig = plt.figure(figsize=(ncols*(wf+dxf), nrows*(hf+dyf)+mm(10)))
rect = lambda xy: plt.Rectangle(xy, wf, hf,
transform=fig.dpi_scale_trans,
figure=fig,
edgecolor='k', facecolor='none')
fig.patches.extend([rect(xy) for xy in product(xcorners, ycorners)])
t = np.linspace(0,3.14,315); s = np.sin(t)
for nframe, (y, x) in enumerate(product(ycorners, xcorners), 1):
for n in range(nplots):
divider = Divider(fig, (0.0, 0.0, 1., 1.),
[Size.Fixed(x+0.7*dxp+n*(wp+dxp)), Size.Fixed(wp)],
[Size.Fixed(y+0.7*dyp ), Size.Fixed(hp)],
aspect=False)
ax = Axes(fig, divider.get_position())
ax.set_axes_locator(divider.new_locator(nx=1, ny=1))
ax.plot(t, s)
fig.add_axes(ax)
fig.text(x, y, 'Frame %d'%nframe, transform=fig.dpi_scale_trans)
figsize = fig.get_size_inches()
width = figsize[0]*25.4 # mm
fig.suptitle('Original figure width is %.2f mm - everything is scaled'%width)
fig.savefig('pippo.png', dpi=118, facecolor='#f8f8f0')
You will need to use Matplotlib to plot these graphs
You can follow the following example to create your own figure with the graphs:
import matplotlib.pyplot as plt
plt.subplot(1, 2, 1) # Args ( Lines, Columns, Reference )
plt.plot(x, y, 'r') # Reference will say what graph we are modding
plt.subplot(1, 2, 2)
plt.plot(y, x, 'g')
plt.show()
The code will create one graph like this:
And you can use plt.xlabel('name'), plt.ylabel('name') and plt.title('name') to define the labels and the title of your figure
Note: The code above will create one image with 2 graphs, and you can use this code inside another block of code to create the image that you want.
You can also use the following code:
import matplotlib.pyplot as plt
fig, ax = plt.subplots(nrows=5, ncols=5, figsize=(5, 5))
ax[0, 0].plot(x, y) # The method ax is now one array and is referred by indexes
ax[0, 0].set_title('Title')
ax[1, 1].plot(x, y)
ax[1, 1].set_title('Title')
plt.tight_layout() # It will separate the graphs to avoid overlays
plt.show()
It will create the following image:

Bar Chart using Matlplotlib

I have two values:
test1 = 0.75565
test2 = 0.77615
I am trying to plot a bar chart (using matlplotlib in jupyter notebook) with the x-axis as the the two test values and the y-axis as the resulting values but I keep getting a crazy plot with just one big box
here is the code I've tried:
plt.bar(test1, 1, width = 2, label = 'test1')
plt.bar(test2, 1, width = 2, label = 'test2')
As you can see in this example, you should define X and Y in two separated arrays, so you can do it like this :
import matplotlib.pyplot as plt
import numpy as np
x = np.arange(2)
y = [0.75565,0.77615]
fig, ax = plt.subplots()
plt.bar(x, y)
# set your labels for the x axis here :
plt.xticks(x, ('test1', 'test2'))
plt.show()
the final plot would be like :
UPDATE
If you want to draw each bar with a different color, you should call the bar method multiple times and give it colors to draw, although it has default colors :
import matplotlib.pyplot as plt
import numpy as np
number_of_points = 2
x = np.arange(number_of_points)
y = [0.75565,0.77615]
fig, ax = plt.subplots()
for i in range(number_of_points):
plt.bar(x[i], y[i])
# set your labels for the x axis here :
plt.xticks(x, ('test1', 'test2'))
plt.show()
or you can do it even more better and choose the colors yourself :
import matplotlib.pyplot as plt
import numpy as np
number_of_points = 2
x = np.arange(number_of_points)
y = [0.75565,0.77615]
# choosing the colors and keeping them in a list
colors = ['g','b']
fig, ax = plt.subplots()
for i in range(number_of_points):
plt.bar(x[i], y[i],color = colors[i])
# set your labels for the x axis here :
plt.xticks(x, ('test1', 'test2'))
plt.show()
The main reason your plot is showing one large value is because you are setting a width for the columns that is greater than the distance between the explicit x values that you have set. Reduce the width to see the individual columns. The only advantage to doing it this way is if you need to set the x values (and y values) explicitly for some reason on a bar chart. Otherwise, the other answer is what you need for a "traditional bar chart".
import matplotlib.pyplot as plt
test1 = 0.75565
test2 = 0.77615
plt.bar(test1, 1, width = 0.01, label = 'test1')
plt.bar(test2, 1, width = 0.01, label = 'test2')

Matplotlib iterate to combine legend handles and labels

If I have the following plotting routine that plots a scatter plot and corresponding linear regression and combines the legend handles:
import pandas as pd
from scipy.stats import linregress
import numpy as np
import matplotlib.pyplot as plt
#data and Regression
x = np.arange(0,5,1)
y = np.arange(0,10,2)
df = pd.DataFrame(data = {'x':x,'y':y})
s, intcpt, r, p, serr = linregress(df.x, df.y)
xHat = np.linspace(0,5,100)
# do the plotting
fig,ax = plt.subplots()
df.plot(x='x',y='y',ax=ax,label='series1',ls=' ',marker='x',c='blue')
ls_handle, = ax.plot(xHat, s*xHat + intcpt, linestyle='-', marker=None, c='blue')
handle2merge = [ls_handle]
handles, labels = ax.get_legend_handles_labels()
handle_combined = zip(handles, handle2merge)
ax.legend(handle_combined, labels)
Which returns the where the marker and line handles are merged looking like:
Now I want to plot another dataset in a similar fashion:
#get current axis handles and labels
handle_start, label_start = ax.get_legend_handles_labels()
#second dataset and regression
x1 = np.arange(0,5,1)
y1 = np.arange(0,2.5,0.5)
df1 = pd.DataFrame(data = {'x':x1,'y':y1})
s1, intcpt1, r1, p1, serr1 = linregress(df1.x, df1.y)
xHat1 = np.linspace(0,5,100)
#plot second data set on same figure
marker_handle2, = ax.plot(df1.x, df1.y, marker = 'x', zorder=10,c='k', linestyle=' ')
line_handle2, = ax.plot(xHat, s1*xHat1 + intcpt1, linestyle='--', marker=None, c='k')
new_line_handles = [line_handle2]
new_marker_handles= [marker_handle2]
ax.legend(handle_start + zip(new_marker_handles,new_line_handles), label_start + ['series2'])
This returns a plot where the handles for series1 legend handle only contains the marker.
Why is len(handle_start)=1 when I constructed the handle with handle_combined = zip(handles, handle2merge)?
I have poked around the code a little. What you are doing is passing a list of tuples to ax.legend, which apparently draws each Artist in each tuple as one entry in the legend. I have actually not come across this behaviour before; it could be a bug, or unintended use of ax.legend.
Nevertheless, I think that in this case, since you know what your lines should look like beforehand, instead of going the roundabout way with zip and stuff, you could just pass a custom Line2D to legend directly:
import numpy as np
from scipy.stats import linregress
from matplotlib import pyplot as plt
from matplotlib import lines
x1 = np.arange(0, 5, 1)
y1 = np.arange(0, 10, 2)
x2 = np.arange(0, 5, 1)
y2 = np.arange(0, 2.5, 0.5)
m1, c1, r1, p1, serr1 = linregress(x1, y1)
m2, c2, r2, p2, serr2 = linregress(x2, y2)
x_pred = np.linspace(0,5,100)
fig, ax = plt.subplots()
first_line, = ax.plot(x_pred, x_pred * m1 + c1, ls='-', c='blue')
first_scatter = ax.scatter(x1, y1, marker='x', c='blue')
second_line, = ax.plot(x_pred, x_pred * m2 + c2, ls='--', c='black')
second_scatter = ax.scatter(x2, y2, marker='x', c='black')
ax.legend([lines.Line2D([0], [0], marker='x', ls='-', c='blue'),
lines.Line2D([0], [0], marker='x', ls='--', c='black')],
['series_1', 'series_2'])
I cleaned up your code a little, but feel free to take only the last line and the necessary import.
In the last line, just use the already created merged handle handle_combined instead of the handle_start.
ax.legend(handle_combined + list(zip(new_marker_handles,new_line_handles)),
label_start + ['series2'])
The length is 1 but if you look into the contents of the list, it is a tuple consisting of two objects. If you print handle_combined, you get a list of two Line2D objects, one of which is marker and the other is the line.
print (handle_combined)
# [(<matplotlib.lines.Line2D object at xxxxxxxxx>, <matplotlib.lines.Line2D object at xxxxxxxxx>)]
However, if you print handle_start, it returns just a single Line2D object
print (handle_start)
# [<matplotlib.lines.Line2D object at xxxxxxxxx>]

add new plot to existing figure

I have a script with some plots ( see example code). After some other things i want to add a new plot to an existing one. But when i try that it add the plot by the last created figure(now fig2).
I can't figure out how to change that...
import matplotlib.pylab as plt
import numpy as np
n = 10
x1 = np.arange(n)
y1 = np.arange(n)
fig1 = plt.figure()
ax1 = fig1.add_subplot(111)
ax1.plot(x1,y1)
fig1.show()
x2 = np.arange(10)
y2 = n/x2
# add new data and create new figure
fig2 = plt.figure()
ax2 = fig2.add_subplot(111)
ax2.plot(x2,y2)
fig2.show()
# do something with data to compare with new data
y1_geq = y1 >= y2
y1_a = y1**2
ax1.plot(y1_geq.nonzero()[0],y1[y1_geq],'ro')
fig1.canvas.draw
Since your code is not runnable without errors I'll provide a sample snippet showing how to plot several data in same graph/diagram:
import matplotlib.pyplot as plt
xvals = [i for i in range(0, 10)]
yvals1 = [i**2 for i in range(0, 10)]
yvals2 = [i**3 for i in range(0, 10)]
f, ax = plt.subplots(1)
ax.plot(xvals, yvals1)
ax.plot(xvals, yvals2)
So the basic idea is to call ax.plot() for all datasets you need to plot into the same plot.

plotting in loop, only getting last plot

I'm using matplotlib. Code:
for new_counter in range(counter+1):
print new_counter
Qbers = final_data[(final_data["Dataset"]==counter) & (final_data["Qber"] > 0) ]
x1 = Qbers.index.tolist()
y1 = Qbers["Qber"].tolist()
Raws = final_data[(final_data["Dataset"]==counter) & (final_data["Raw key"] > 0) ]
x2 = Raws.index.tolist()
y2 = Raws["Raw key"].tolist()
# Two subplots, the axes array is 1-d http://matplotlib.org/examples/pylab_examples/subplots_demo.html
f, axarr = plt.subplots(2, sharex=True)
axarr[0].grid()
axarr[0].plot(x1, y1)
axarr[0].set_title('Sharing X axis')
axarr[1].grid()
axarr[1].plot(x2, y2)
plt.savefig(str(counter)+'foo.eps')
plt.clf()
I'm receiving only file with last plot, and with my data I should receive 6 of them. How to fix that? Additional question: How to prevent creation of interactive window with plot?
It looks like you are not generating unique file names. You probably want:
plt.savefig(str(new_counter)+'foo.eps')

Categories

Resources