How to plot computed values? - python-3.x

How to plot force lines one by one? Now, the slice plots an equal part of all force lines.
The desired result is to plot the shortest lines completely and do not draw the big force lines.
The code is:
import matplotlib.pyplot as plt
import numpy as np
from scipy.integrate import ode as ode
# Class for defining charge's position and magnitude
class charge:
def __init__(self, q, pos):
self.q=q
self.pos=pos
def E_point_charge(q, a, x, y):
return q*(x-a[0])/((x-a[0])**2+(y-a[1])**2)**(1.5), \
q*(y-a[1])/((x-a[0])**2+(y-a[1])**2)**(1.5)
def E_total(x, y, charges):
Ex, Ey=0, 0
for C in charges:
E=E_point_charge(C.q, C.pos, x, y)
Ex=Ex+E[0]
Ey=Ey+E[1]
return [ Ex, Ey ]
def E_dir(t, y, charges):
Ex, Ey=E_total(y[0], y[1], charges)
n=np.sqrt(Ex**2+Ey*Ey)
return [Ex/n, Ey/n]
# Defining charges
charges=[ charge(-2, [-0.1, 0]), charge(2, [0.1, 0])]
# Calculation of field lines
x0, x1=-3, 3
y0, y1=-3, 3
R=0.01
# Loop over all charges
xs,ys = [],[]
for C in charges:
# plot field lines starting in current charge
dt=0.8*R
if C.q<0:
dt=-dt
# loop over field lines starting in different directions around current charge
for alpha in np.linspace(0, 2*np.pi*15/16, 16):
r=ode(E_dir)
r.set_integrator('vode')
r.set_f_params(charges)
x=[ C.pos[0] + np.cos(alpha)*R ]
y=[ C.pos[1] + np.sin(alpha)*R ]
r.set_initial_value([x[0], y[0]], 0)
while r.successful():
r.integrate(r.t+dt)
x.append(r.y[0])
y.append(r.y[1])
hit_charge=False
# check if field line left drwaing area or ends in some charge
for C2 in charges:
if np.sqrt((r.y[0]-C2.pos[0])**2+(r.y[1]-C2.pos[1])**2)<R:
hit_charge=True
if hit_charge or (not (x0<r.y[0] and r.y[0]<x1)) or \
(not (y0<r.y[1] and r.y[1]<y1)):
break
xs.append(x)
ys.append(y)
fig, ax = plt.subplots(figsize = (5,5))
# plot field line
for x, y in zip(xs,ys):
plt.plot(x[:25], y[:25], color="k")
ax.set_xlim(-0.5, 0.5)
ax.set_ylim(-1, 1)
plt.show()
My question is related to # plot field line, xs and ys are generated in the loop before.
The code is composed of classes, a loop that computes xs and ys, and the plotting part follows.

Just print only those lines whose x coords are within the orginal charges x positions plus a certain tolerance eps (due to numerical errors):
eps = 1e-3
for x, y in zip(xs,ys):
if min(x) >= charges[0].pos[0] - eps and max(x) <= charges[1].pos[0] + eps:
plt.plot(x[:25], y[:25], color="k")

Related

Plotting 3-D Solid of Revolution in Python Using Matplotlib

Given the region bounded by the curves y=x^2, y=(x-2)^2 and the axis.
I want to plot the 3-D solid rotated about the x-axis.
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
# Define the function to rotate
def f(x):
return x**2
def g(x):
return (x-2)**2
# Define the range of x values to plot
x = np.linspace(0, 1, 100)
x2=np.linspace(1, 2, 100)
# Define the range of angles to rotate over
theta = np.linspace(0, 2*np.pi, 100)
# Create a meshgrid of x and theta values
X, Theta = np.meshgrid(x, theta)
X2, Theta = np.meshgrid(x2, theta)
# Calculate the corresponding cylindrical coordinates
R = X
Y = R*np.sin(Theta)
Z = R*np.cos(Theta)*f(X)
R2 = X2
Y2 = R2*np.sin(Theta)
Z2 = R2*np.cos(Theta)*g(X2)
# Create the 3D plot
fig = plt.figure(figsize = (11,8))
ax = fig.add_subplot(111, projection='3d')
ax.plot_surface(X, Y, Z)
ax.plot_surface(X2, Y2, Z2)
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
plt.show()
Output:
As you can see, it works fine for the first curve y = x^2 (blue) but it's not rendering correctly for y=(x-2)^2 (orange). Why is it doing that?
The code and output attached above.
I used a trick to make the plotting process easier.
Instead of rotating around the x-axis, it is much easier rotating around the z-axis using spherical coordinates. matplotlib has intuitive example of utilizing spherical coordinates to draw a ball. Hence, we can swap the axis (e.g. treat the x-axis in the 2D plot as the z-axis in the 3D plot), compute the required spherical coordinates from the given two functions, and then convert back to Cartesian for plotting.
Since we swap the coordinates, eventually we have to rotate the plot and manually assign the axis label.
import matplotlib.pyplot as plt
import numpy as np
from typing import Tuple, Callable
fig = plt.figure(figsize=(10, 10))
ax = fig.add_subplot(projection='3d')
# Define the function to rotate
def f(x):
return x**2
def g(x):
return (x-2)**2
def get_p(phi: np.ndarray, f: Callable, x0: float = 0) -> np.ndarray:
"""Get the distance p
Let the origin be O and a line starting from O with its angle relative to
x-axis being phi intersect with the curve y = f(x) at point Q, the distance
p is the length of the line segment OQ.
:param phi: the angle relative to x-axis
:type phi: np.ndarray
:param f: the curve to be rotated around its x-axis
:type f: Callable
:param x0: starting estimate of x-coord of intersection Q. Use this to
control which intersection is desired. default to 0
:type x0: optional, float
:return: an array of distance, corresponding to each given phi
:rtype: np.ndarray
"""
ks = np.tan(phi)
x = []
for k in ks:
func = lambda x : f(x) - k * x
# we only look for one root
x.append(scipy.optimize.fsolve(func, x0)[0])
x = np.array(x)
y = x * ks
return np.sqrt(x**2 + y**2)
def get_xyz(
theta: np.ndarray, phi: np.ndarray, p: np.ndarray,
) -> Tuple[np.ndarray, np.ndarray, np.ndarray]:
"""Produce the Cartesian coordinates from the given spherical coordinates.
For reference, see: https://mathinsight.org/spherical_coordinates#:~:text=In%20summary%2C%20the%20formulas%20for,%CE%B8z%3D%CF%81cos%CF%95.
:param theta: in the 3D coordinate, given its origin O, a point P and its
projection Q on the XY plane, theta is the angle between line segment
OQ and positive x-axis.
:type theta: np.ndarray
:param phi: using the same setup as described above, phi is the angle
between line segment OP and positive z-axis
:type phi: np.ndarray
:param p: using the same setup as described above, p is the length of line
segment OP.
:type p: np.ndarray
:return: the Cartesian coordinates converted from the spherical coordinates
in the form of (x, y, z)
:rtype: Tuple[np.ndarray, np.ndarray, np.ndarray]
"""
return (
np.outer(np.cos(theta), np.sin(phi) * p),
np.outer(np.sin(theta), np.sin(phi) * p),
np.outer(np.ones(np.size(theta)), np.cos(phi) * p),
)
# Make data
theta = np.linspace(0, 2 * np.pi, 100)
phi_intercept = np.pi / 4 # the angle relative to x-axis when the two curves meet
# Plot y = x^2 half
phi2 = np.linspace(0, phi_intercept, 50)
p2 = get_p(phi2, f, x0=1)
ax.plot_surface(*get_xyz(theta, phi2, p2))
# Plot y = (x - 2)^2 half
phi1 = np.linspace(0, phi_intercept, 50)
p1 = get_p(phi1, g, x0=1)
ax.plot_surface(*get_xyz(theta, phi1, p1))
# Set plot properties
ax.set_box_aspect([1,1,1])
# x axis in the 2D plot becomes z here
ax.set_zlim(0, 2)
ax.set_zlabel('X')
# y axis in the 2D plot is still y here
ax.set_ylim(-1, 1)
ax.set_ylabel('Y')
# the new z axis after rotation becomes x here
ax.set_xlim(-1, 1)
ax.set_xlabel('Z')
# rotate the plot
ax.view_init(10, 0, -90)
plt.savefig('demo.png', dpi=100)

Pyplot: subsequent plots with a gradient of colours [duplicate]

I am plotting multiple lines on a single plot and I want them to run through the spectrum of a colormap, not just the same 6 or 7 colors. The code is akin to this:
for i in range(20):
for k in range(100):
y[k] = i*x[i]
plt.plot(x,y)
plt.show()
Both with colormap "jet" and another that I imported from seaborn, I get the same 7 colors repeated in the same order. I would like to be able to plot up to ~60 different lines, all with different colors.
The Matplotlib colormaps accept an argument (0..1, scalar or array) which you use to get colors from a colormap. For example:
col = pl.cm.jet([0.25,0.75])
Gives you an array with (two) RGBA colors:
array([[ 0. , 0.50392157, 1. , 1. ],
[ 1. , 0.58169935, 0. , 1. ]])
You can use that to create N different colors:
import numpy as np
import matplotlib.pylab as pl
x = np.linspace(0, 2*np.pi, 64)
y = np.cos(x)
pl.figure()
pl.plot(x,y)
n = 20
colors = pl.cm.jet(np.linspace(0,1,n))
for i in range(n):
pl.plot(x, i*y, color=colors[i])
Bart's solution is nice and simple but has two shortcomings.
plt.colorbar() won't work in a nice way because the line plots aren't mappable (compared to, e.g., an image)
It can be slow for large numbers of lines due to the for loop (though this is maybe not a problem for most applications?)
These issues can be addressed by using LineCollection. However, this isn't too user-friendly in my (humble) opinion. There is an open suggestion on GitHub for adding a multicolor line plot function, similar to the plt.scatter(...) function.
Here is a working example I was able to hack together
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.collections import LineCollection
def multiline(xs, ys, c, ax=None, **kwargs):
"""Plot lines with different colorings
Parameters
----------
xs : iterable container of x coordinates
ys : iterable container of y coordinates
c : iterable container of numbers mapped to colormap
ax (optional): Axes to plot on.
kwargs (optional): passed to LineCollection
Notes:
len(xs) == len(ys) == len(c) is the number of line segments
len(xs[i]) == len(ys[i]) is the number of points for each line (indexed by i)
Returns
-------
lc : LineCollection instance.
"""
# find axes
ax = plt.gca() if ax is None else ax
# create LineCollection
segments = [np.column_stack([x, y]) for x, y in zip(xs, ys)]
lc = LineCollection(segments, **kwargs)
# set coloring of line segments
# Note: I get an error if I pass c as a list here... not sure why.
lc.set_array(np.asarray(c))
# add lines to axes and rescale
# Note: adding a collection doesn't autoscalee xlim/ylim
ax.add_collection(lc)
ax.autoscale()
return lc
Here is a very simple example:
xs = [[0, 1],
[0, 1, 2]]
ys = [[0, 0],
[1, 2, 1]]
c = [0, 1]
lc = multiline(xs, ys, c, cmap='bwr', lw=2)
Produces:
And something a little more sophisticated:
n_lines = 30
x = np.arange(100)
yint = np.arange(0, n_lines*10, 10)
ys = np.array([x + b for b in yint])
xs = np.array([x for i in range(n_lines)]) # could also use np.tile
colors = np.arange(n_lines)
fig, ax = plt.subplots()
lc = multiline(xs, ys, yint, cmap='bwr', lw=2)
axcb = fig.colorbar(lc)
axcb.set_label('Y-intercept')
ax.set_title('Line Collection with mapped colors')
Produces:
Hope this helps!
An anternative to Bart's answer, in which you do not specify the color in each call to plt.plot is to define a new color cycle with set_prop_cycle. His example can be translated into the following code (I've also changed the import of matplotlib to the recommended style):
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(0, 2*np.pi, 64)
y = np.cos(x)
n = 20
ax = plt.axes()
ax.set_prop_cycle('color',[plt.cm.jet(i) for i in np.linspace(0, 1, n)])
for i in range(n):
plt.plot(x, i*y)
If you are using continuous color pallets like brg, hsv, jet or the default one then you can do like this:
color = plt.cm.hsv(r) # r is 0 to 1 inclusive
Now you can pass this color value to any API you want like this:
line = matplotlib.lines.Line2D(xdata, ydata, color=color)
This approach seems to me like the most concise, user-friendly and does not require a loop to be used. It does not rely on user-made functions either.
import numpy as np
import matplotlib.pyplot as plt
# make 5 lines
n_lines = 5
x = np.arange(0, 2).reshape(-1, 1)
A = np.linspace(0, 2, n_lines).reshape(1, -1)
Y = x # A
# create colormap
cm = plt.cm.bwr(np.linspace(0, 1, n_lines))
# plot
ax = plt.subplot(111)
ax.set_prop_cycle('color', list(cm))
ax.plot(x, Y)
plt.show()
Resulting figure here

Find coordinate on curve

I have plotted curve created by a list with several values. How to find out the x-coordinate that correspond with y-coordinate 0.04400918? This value is not exactly included in the list that describes the curve. Thank you very much.
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D # 3d graph
from mpl_toolkits.mplot3d import proj3d # 3d graph
import matplotlib.pylab as pl
fig=pl.figure()
ax = Axes3D(fig)
x=[0.02554897, 0.02587839, 0.02623991, 0.02663096, 0.02704882, 0.02749103, 0.02795535, 0.02844018, 0.02894404, 0.02946527, 0.03000235]
y=[0.04739086, 0.0460989, 0.04481555, 0.04354088, 0.04227474, 0.04101689, 0.03976702, 0.03852497, 0.03729052, 0.0360633, 0.03484293]
z=[1.05764017e-18, 1.57788964e-18, 2.00281370e-18, 2.40500994e-18, 2.80239565e-18, 3.19420769e-18, 3.58001701e-18, 3.96024361e-18, 4.33484911e-18, 4.70364652e-18, 5.06672528e-18]
y_point=0.04400918
ax.plot3D(x,y,z)
plt.show()
Here is a specific resolution for your problem.
Some works have already been done for solving line-plane equation. This topic explains how to solve it. Even better, this snippet implements a solution.
For now, we only need to adapt it to our problem.
The first step is to find all the time the line is crossing the plan. To do that, we will iterate over the y dataset and collect all consecutive values when y_point is between them:
lines = []
for i in range(len(y) - 1):
if y[i] >= y_point and y_point >= y[i+1]:
lines.append([[x[i], y[i], z[i]], [x[i+1], y[i+1], z[i+1]]])
Then, for all of these lines, we will solve the intersection equation with the plane. We will use the function provided in sources above.
Finally, we will plot the results
Full code:
# Modules
import numpy as np
import matplotlib.pyplot as plt
# Data
x = [0.02554897, 0.02587839, 0.02623991, 0.02663096, 0.02704882, 0.02749103, 0.02795535, 0.02844018, 0.02894404, 0.02946527, 0.03000235]
y = [0.04739086, 0.0460989, 0.04481555, 0.04354088, 0.04227474, 0.04101689, 0.03976702, 0.03852497, 0.03729052, 0.0360633, 0.03484293]
z = [1.05764017e-18, 1.57788964e-18, 2.00281370e-18, 2.40500994e-18, 2.80239565e-18, 3.19420769e-18, 3.58001701e-18, 3.96024361e-18, 4.33484911e-18, 4.70364652e-18, 5.06672528e-18]
y_point = 0.04400918
# Source: https://rosettacode.org/wiki/Find_the_intersection_of_a_line_with_a_plane#Python
# Resolve intersection
def LinePlaneCollision(planeNormal, planePoint, rayDirection, rayPoint, epsilon=1e-6):
ndotu = planeNormal.dot(rayDirection)
if abs(ndotu) < epsilon:
raise RuntimeError("no intersection or line is within plane")
w = rayPoint - planePoint
si = -planeNormal.dot(w) / ndotu
Psi = w + si * rayDirection + planePoint
return Psi
# For all line, apply the solving process
def solveAllPoints(lines, y_point):
collision_points = []
for line in lines:
# Define plane
planeNormal = np.array([0, 1, 0]) # Plane normal (e.g. y vector)
planePoint = np.array([0, y_point, 0]) # Any point on the plane
# Define ray
rayDirection = line[1] - line[0] # Line direction
rayPoint = line[0] # Any point of the line
# Append point
collision_points.append(LinePlaneCollision(planeNormal, planePoint, rayDirection, rayPoint))
return collision_points
# Find all consecutive Y points crossing the plane.
# This function is only working for the given problem (intersection of the line
# with 1 plan defined by a normal vector = [0,1,0])
def getCrossingLines(y_point, x, y, z):
lines = []
for i in range(len(y) - 1):
if y[i] >= y_point and y_point >= y[i+1]:
lines.append([[x[i], y[i], z[i]], [x[i+1], y[i+1], z[i+1]]])
return np.array(lines)
# Get coordinates for drawing our plane
# Related topic: https://stackoverflow.com/questions/53115276/matplotlib-how-to-draw-a-vertical-plane-in-3d-figure
def getXYZPlane(x, y, z):
xs = np.linspace(min(x), max(x), 100)
zs = np.linspace(min(z), max(z), 100)
X, Z = np.meshgrid(xs, zs)
Y = np.array([y_point for _ in X])
return X, Y, Z
# Create plot
plt3d = plt.figure().gca(projection='3d')
ax = plt.gca()
# Draw data line
ax.plot3D(x,y,z)
# Plot plan
X, Y, Z = getXYZPlane(x, y, z)
ax.plot_surface(X, Y, Z)
# Draw crossing points (lines-planes)
lines = getCrossingLines(y_point, x, y , z)
for pt in solveAllPoints(lines, y_point):
ax.scatter(pt[0], pt[1], pt[2], color='green')
plt.show()
Output

Obtaining coordinates in projected map using Cartopy

I'm trying to obtain the coordinates of the features of a map using Cartopy but I would like to obtain the map projected coordinates instead of the data from the original projection.
For instance:
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
fig = plt.figure(figsize=(10, 10))
ax = plt.axes(projection=ccrs.epsg(3857))
fig.add_axes(ax)
ax.coastlines()
ax.set_global()
lines = ax.plot((0, 360), (-85.06, 85.06), transform=ccrs.PlateCarree())
fig.show()
The previous code shows a map with two lines using the map projection but lines (a list with matplotlib.lines.Line2D instances) is just only one object with the coordinates in the original projection of the data (lines[0].get_data() ---> (array([ 0, 360]), array([-85.06, 85.06]))).
On an interactive plot, a Qt5 backend obtained after fig.show(), I can see coordinates in EPSG:3857 and in PlateCarree when the cursor is over the map so I wonder if there is an easy way to get lines in EPSG:3857 coordinates.
EDIT: The example above is quite simplified. I've tried to do it simple for better understanding but maybe is better to show the real problem.
I have a grid of data with longitudes in the range [0, 360]. I can modify the arrays in order to have inputs in the range [-180, 180] and I'm using Cartopy/Matplotlib to plot contours. From the contours I'm obtaining a matplotlib.contour.QuadContourSet with several matplotlib.collections.LineCollection. From each matplotlib.collections.LineCollection I can obtain the matplotlib.path.Paths and I would like to have the coordinates of each Path in EPSG:3857 instead of in the original PlateCarree so I can use cartopy.mpl.patch.path_to_geos to convert each Path to a shapely geometry object in the EPSG:3857 projection without having to extract vertices from each Path, convert them from PlateCarree to EPSG:3857 and then create a new Path with the converted coordinates to use cartopy.mpl.patch.path_to_geos to obtain geometries in the crs I need.
The question asks for a coordinate transformation using Cartopy's feature, and maybe something else.
Here I provide the code that performs coordinate transformation and computation check.
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
import numpy as np
# Test data in geographic lon, lat (degrees)
lons = np.array((0, 360.01)) # any number of longitude
lats = np.array((-85.06, 85.06)) # .. longitude
# define all CRS
crs_longlat = ccrs.PlateCarree()
crs_3857 = ccrs.epsg(3857)
# Transformation function
def coordXform(orig_crs, target_crs, x, y):
"""
Converts array of (y,x) from orig_crs -> target_crs
y, x: numpy array of float values
orig_crs: source CRS
target_crs: target CRS
"""
# original code is one-liner
# it leaves an open axes that need to plt.close() later
# return plt.axes( projection = target_crs ).projection.transform_points( orig_crs, x, y )
# new improved code follows
xys = plt.axes( projection = target_crs ).projection.transform_points( orig_crs, x, y )
# print(plt.gca()) # current axes: GeoAxes: _EPSGProjection(3857)
plt.close() # Kill GeoAxes
# print(plt.gca()) # AxesSubplot (new current axes)
return xys
# Transform geographic (lon-lat) to (x, y) of epsg(3857)
xys = coordXform(crs_longlat, crs_3857, lons, lats)
for ea in xys:
print("(x, y) meters: " + str(ea[0]) + ', ' + str(ea[1]))
#Output(1)
#(x, y) meters: 0.0, -20006332.4374
#(x, y) meters: 1113.19490794, 20006332.4374
# Computation check
# Transform (x, y) of epsg(3857) to geographic (lon-lat), degrees
xs = xys[:,0] # all x's
ys = xys[:,1] # all y's
lls = coordXform(crs_3857, crs_longlat, xs, ys)
for ea in lls:
print("(lon, lat) degrees: " + str(ea[0]) + ', ' + str(ea[1]))
#Output(2)
#(lon, lat) degrees: 0.0, -85.06
#(lon, lat) degrees: 0.01, 85.06
# plt.close() # no need now
Edit 2
According to the constructive comments, the transformation function above can be written as follows:
def coordXform(orig_crs, target_crs, x, y):
return target_crs.transform_points( orig_crs, x, y )

Plotting Multiple Plots on a single figure from within a for loop - Python

I have reviewed the response to this question: How would I iterate over a list of files and plot them as subplots on a single figure?
But am none the wiser on how to achieve my goal. I would like to plot multiple data sets, with differing x axes, onto a single figure in Python. I have included a snippet of my code below, which performs an FFT on a dataset, then calculates 3 Butterworth filter outputs. Ideally I would like to have all plotted on a single figure, which I have attempted to achieve in the code below.
The for loop calculates the 3 Butterworth filter outputs, the code above - the FFT and the code directly below attempts to append the FFT curve and sqrt(0.5) line to the previously generated plots for display.
Any Direction or advice would be appreciated.
"""Performs a Fast Fourier Transform on the data specified at the base of the code"""
def FFT(col):
x = io2.loc[1:,'Time']
y = io2.loc[1:,col]
# Number of samplepoints
#N = 600
N = pd.Series.count(x)
N2 = int(N/2)
# sample spacing
#T = 1.0 / 800.0
T = 1/(io2.loc[2,'Time'] - io2.loc[1,'Time'])
#x = np.linspace(0.0, N*T, N)
#y = np.sin(50.0 * 2.0*np.pi*x) + 0.5*np.sin(80.0 * 2.0*np.pi*x)
yf = scipy.fftpack.fft(y)
xf = np.linspace(0.0, 1.0/(2.0*T), N2)
fig=plt.figure()
plt.clf()
i=1
for order in [3, 6, 9]:
ax=fig.add_subplot(111, label="order = %d" % order)
b, a = butter_lowpass(cutoff, fs, order=order)
w, h = freqz(b, a, worN=2000)
ax.plot((fs * 0.5 / np.pi) * w, abs(h))
i=i+1
ax4=fig.add_subplot(111, label='sqrt(0.5)', frame_on=False)
ax5=fig.add_subplot(111, label="FFT of "+col, frame_on=False)
ax4.plot([0, 0.5 * fs], [np.sqrt(0.5), np.sqrt(0.5)], '--')
ax5.plot(xf, 2.0/N * np.abs(yf[:N2]))
plt.xlabel('Frequency (Hz)')
plt.ylabel('Gain')
plt.grid(True)
plt.legend(loc='best')
#fig, ax = plt.subplots()
#ax.plot(xf, 2.0/N * np.abs(yf[:N2]), label="FFT of "+col)
plt.axis([0,5000,0,0.1])
#plt.xlabel('Frequency (Hz)')
#plt.ylabel('Amplitude (mm)')
#plt.legend(loc=0)
plt.show()
return
Kind Regards,
Here you can find a minimal example of how to plot multiple lines with different x and y datasets. You are recreating the plot every time you type add_subplot(111). Instead, you should call plot multiple times. I have added an example for a single plot with multiple lines, as well as an example for one subplot per line.
import numpy as np
import matplotlib.pyplot as plt
x1 = np.arange(0, 10, 1)
x2 = np.arange(3, 12, 0.1)
x3 = np.arange(2, 8, 0.01)
y1 = np.sin(x1)
y2 = np.cos(x2**0.8)
y3 = np.sin(4.*x3)**3
data = []
data.append((x1, y1, 'label1'))
data.append((x2, y2, 'label2'))
data.append((x3, y3, 'label3'))
# All lines in one plot.
plt.figure()
for n in data:
plt.plot(n[0], n[1], label=n[2])
plt.legend(loc=0, frameon=False)
# One subplot per data set.
cols = 2
rows = len(data)//2 + len(data)%2
plt.figure()
gs = plt.GridSpec(rows, cols)
for n in range(len(data)):
i = n%2
j = n//2
plt.subplot(gs[j,i])
plt.plot(data[n][0], data[n][1])
plt.title(data[n][2])
plt.tight_layout()
plt.show()

Resources