I have been trying to plot the linear speed of the proton against magnetic field. The proton is moving in a circular orbit in an
uniform magnetic field perpendicular to the velocity. The radius of the orbit changes from 14 cm to 8 cm when the uniform magnetic field increases from 0.35 T to 2 T.
#!/usr/bin/env python
import matplotlib.pyplot as plt
import numpy as np
import scipy.constants
p = 1.6
q = scipy.constants.e
p_mass = scipy.constants.proton_mass
B_List = []
r_List = []
v_List = []
for r in np.linspace (0.14, 0.08):
for B in np.linspace (0.35, 2):
v = (r*q*B)/p_mass
B_List.append(B)
v_List.append(v)
fig = plt.figure()
plt.plot(B_List,v_List)
fig.suptitle('Linear Speed Vs Magnatic Field', fontsize=18)
plt.xlabel('Magnatic field (T)', fontsize=14)
plt.ylabel('Speed of the Proton (m/s)', fontsize=14)
plt.show()
For some reasons, I got a multiple lines on the graph.
I tried to make the spaces = 2 (in linspace) as follows:
for r in np.linspace (0.14, 0.08,2, 2):
for B in np.linspace (0.35, 2, 2):
but still, those multiple lines are appearing.
My question here, how to get rid of those lines?
Your main problem is that you use the plot function in pyplot to show your points. That function draws line segments between consecutive plotted points.
Instead, use the scatter function, which just shows the points without any connecting line segments. When I replace plot with scatter, I get the result below. Click on the graphic to see it full size--then you will see that each of those apparent vertical line segments are actually 50 points close together. That seems to be correct, though there are no circular paths that you mention. Is that what you want?
Related
I have a 2 Dimensional set of time series data containing about 1000 samples. I.e I have a long list of 1000 elements each entry is a list of two numbers.
It can be thought of the position, x and y coordinates, of a car with a picture taken each second for 1000 seconds. When I plot this, as seen below, you get a decent idea of the trajectory but it's unclear where the car starts or finishes, i.e which direction it is traveling in. I was thinking about including arrows between each point but I think this would get quite clustered (maybe you know a way to overcome that issue?) Also, I thought of colouring each point with a spectrum that made it clear to see time increasing, i.e hotter points to colder points as time goes on. Any idea how to achieve this in matplotlib?
I believe both your ideas would work well, I just think you need to test which option works best for your case.
Option 1: arrows
To avoid a cluttered plot I believe you could plot arrows between only a selection of points to show the general direction of your trajectory. In my example below I only plot an arrow between points 1 and 2, 6 and 7, and so on and. You might want to increase the spacing between the points to make this work for your long series. It is also possible to connect points that are seperated by, say, 10 points to make them more clearly visible.
import numpy as np
import matplotlib.pyplot as plt
# example data
x = np.linspace(0, 10, 100)
y = x
plt.figure()
# plot the data points
for i in range(len(x)):
plt.plot(x[i], y[i], "ro")
# plot arrows between points 1 and 2, 6 and 7 and so on.
for i in range(0, len(x)-1, 5):
plt.arrow(x[i], y[i], x[i+1] - x[i], y[i+1] - y[i], color = "black",zorder = 2, width = 0.05)
plt.xlabel('x')
plt.ylabel('y')
plt.show()
This yields this plot.
Option 2: colors
You can generate any number of colors from a colormap, meaning you can make a list of 1000 sequential colors. This way you can plot each of your points in an increasingly warm color.
Example:
import numpy as np
import matplotlib.pyplot as plt
# example data
x = np.linspace(0, 10, 100)
y = x
# generate 100 (number of data points) colors from colormap
colors = [plt.get_cmap("coolwarm")(i) for i in np.linspace(0,1, len(x))]
plt.figure()
# plot the data points with the generated colors
for i in range(len(x)):
plt.plot(x[i], y[i], color = colors[i], marker = "o")
plt.xlabel('x')
plt.ylabel('y')
plt.show()
This yields this figure, where the oldest data point is cool (blue) and the newest is red (warm).
I plot a quiver with matplotlib.pyplot, and would like to:
Zoom around a chosen coordinate (and in a chosen range around this coordinate).
Not have to load all the plot and then set an xlim/ylim. It means that I can cut the plot before and it will not take a lot of time compared to if I need to compute the whole uncut plot (which is what is done for now in the code below).
Be able to zoom again if needed in the interactive plot (I use nbagg).
Get some arrows that are sufficiently big (i.e. not just the small heads!) in the window that is plotted.
Code using xlim and ylim:
import numpy as np
import matplotlib.pyplot as plt
ext = 4
points = 1000
target = (2,2)
around_target = 0.5
x,y = np.meshgrid(np.linspace(-ext,ext,points),np.linspace(-ext,ext,points))
u = x
v = y
plt.figure(figsize=(6,6))
plt.quiver(x,y,u,v,angles='xy', scale_units='xy')
plt.xlim(target[0]-around_target = 0.5,target[0]+around_target = 0.5)
plt.ylim(target[1]-around_target = 0.5,target[1]+around_target = 0.5)
plt.show()
I am trying to plot global storm tracks, but when the storms cross the dateline (and longitudes go from ~360 to ~0), the line loops all the way around the plotting space.
Here's what the plot looks like. See the weird straight lines near the top.
Here's my code:
ax = plt.axes(projection=ccrs.Robinson())
ax.set_global()
ax.coastlines()
for i in range(nstorms-1): #loop through each TC
bidx = start_idx[i]
eidx = start_idx[i+1]
plt.plot(clons[bidx:eidx],clats[bidx:eidx],transform=ccrs.PlateCarree())
If I try changing the transform to Geodetic, it looks like this:
To plot polylines that cross the dateline, you need to sanitize the longitudes properly. For example, values 359 to 2 should be adjusted to 359 to 362. In the demo code below, sanitize_lonlist() is used to sanitize a list of longitude values before using it to plot a red zigzag line.
import cartopy.crs as ccrs
import matplotlib.pyplot as plt
def sanitize_lonlist(lons):
new_list = []
oldval = 0
treshold = 10 # used to compare adjacent longitudes
for ix,ea in enumerate(lons):
diff = oldval - ea
if (ix>0):
if (diff>treshold):
ea = ea+360
oldval = ea
new_list.append(ea)
return new_list
ax = plt.axes(projection=ccrs.Robinson())
ax.set_global()
ax.coastlines(alpha=0.3)
# sample long/lat data for demo purposes
# xdateline: list of longitudes that cross dateline several times
xdateline = [347,349,352,358,4,7,8,3,359,358,360,3,5,359,1,357,0,8,12,6,357,349]
# ydateline: list of accompanying latitudes
ydateline = range(len(xdateline))
# plot the line crossing dateline using `sanitized` values of longitudes
plt.plot(sanitize_lonlist(xdateline), ydateline, transform=ccrs.PlateCarree(), color='red')
plt.show()
Using the raw values of xdateline to plot with the line of code:-
plt.plot(xdateline, ydateline, transform=ccrs.PlateCarree(), color='red')
the plot will be:-
As per this github issue, this is expected behaviour since PlateCarree is a projected coordinate system.
The PlateCarree coordinate system is Cartesian where a line between two points is straight (in that coordinate system). The Cartesian system has no knowledge of datelines/antimeridians and so when you ask for a line between -170 and +170 you get a line of length 340. It can never be the case that the PlateCarree projection interprets these numbers and chooses to draw a non-cartesian line
One solution is to use the Geodetic transform in your plot calls:
plt.plot(clons[bidx:eidx], clats[bidx:eidx], transform=ccrs.Geodetic())
Or modify your data to make more sense when using the PlateCarree system, e.g. by identifying where values loop from 360-> 0 and adding 360 to all values after that occurs. You could shift them onto a different range (e.g. -180..180) but you'll have the same issue with data crossing +/- 180 as you do with 0/360 currently.
this plot shows the voltage course lines of a simulated neuron:
i would like to place a zoom in plot in the upper right corner so that you can see the current fluctuations of the lines better (the scale here is such that you can hardly see them)
attached the code for the plot
factor to define voltage-amplitude heights
v_amp_factor = 1/(50)
##### distances between lines and x-axis
offset = np.cumsum(distance_comps_middle)/meter
offset = (offset/max(offset))*10
plt.close(plot_name)
voltage_course = plt.figure(plot_name)
for ii in comps_to_plot:
plt.plot(time_vector/ms, offset[ii] - v_amp_factor*(voltage_matrix[ii, :]-V_res)/mV, "#000000")
plt.yticks(np.linspace(0,10, int(length_neuron/mm)+1),range(0,int(length_neuron/mm)+1,1))
plt.xlabel('Time/ms', fontsize=16)
plt.gca().invert_yaxis() # inverts y-axis => - v_amp_factor*(.... has to be written above
##### no grid
plt.grid(False)
plt.savefig('plot_name', dpi=600)
plt.show(plot_name)
parameter description:
Parameters
----------
plot_name : string
This defines how the plot window will be named.
time_vector : list of time values
Vector contains the time points, that correspond to the voltage values
of the voltage matrix.
voltage_matrix : matrix of membrane potentials
Matrix has one row for each compartment and one columns for each time
step. Number of columns has to be the same as the length of the time vector
comps_to_plot : vector of integers
This vector includes the numbers of the compartments, which should be part of the plot.
distance_comps_middle : list of lengths
This list contains the distances of the compartments, which allows that
the lines in the plot are spaced according to the real compartment distances
length_neuron : length
Defines the total length of the neuron.
V_res : voltage
Defines the resting potential of the model.
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1.inset_locator import zoomed_inset_axes
# let's plot something similar to your stuff
t = np.linspace(-5, 5, 2001)
y = np.exp(-20*t**2)
fig, ax = plt.subplots()
for i in range(14):
start = 900-10*i
ax.plot(t[1000:1500], -5*y[start:start+500]/(1+i*0.3)+i, 'k')
ax.set_ylim((15, -10)) ; ax.set_yticks(range(14))
# now, create an inset axis, in the upper right corner, with
# a zoom factor of two
zax = zoomed_inset_axes(ax, 2, loc=1)
# plot again (PLOT AGAIN) the same stuff as before in the new axes
for i in range(14):
start = 900-10*i
zax.plot(t[1000:1500], -5*y[start:start+500]/(1+i*0.3)+i, 'k')
# and eventually specify the x, y limits for the zoomed plot,
# as well as the tick positions
zax.set_xlim((0.2, 0.7)) ; zax.set_xticks((0.2, 0.3, 0.4, 0.5, 0.6, 0.7))
zax.set_ylim((1, -6)) ; zax.set_yticks([1]+[-i*0.5 for i in range(12)]) ;
# that's all folks
plt.show()
Doing some research I found a method that should be exactly what you are looking for using the inset_axes method of matplotlib, you can find a nice working example in the docs. I hope it helps, I would have tried to apply it to your code, but without the data you used to plot there wasn't much I could do.
As you can find in the docs this method will allow you to do something as shown in this image:
I'm trying to plot projections of coordinates onto a line, but for some reason, Matplotlib is plotting the projections in a slightly slanted manner. Ideally, I would like the (blue) projections to be perpendicular to the (green) line. Here's an image of how it looks with sample data:
As you can see, the angles between the blue lines and the green line are slightly obtuse instead of right. I tried playing around with the rotation parameter to the annotate function, but this did not help. The code for this plot is below, although the data might look a bit different since the random generator is not seeded:
import numpy as np
import matplotlib.pyplot as plt
prefs = {'color':'purple','edgecolors':'black'}
X = np.dot(np.random.rand(2,2), np.random.rand(2,50)).T
pts = np.linspace(-1,1)
v1_m = 0.8076549717643662
plt.scatter(X[:,0],X[:,1],**prefs)
plt.plot(pts, [v1_m*x for x in pts], color='lightgreen')
for x,y in X:
# slope of connecting line
# y = mx+b
m = -np.reciprocal(v1_m)
b = y-m*x
# find intersecting point
zx = b/(v1_m-m)
zy = v1_m*zx
# draw line
plt.annotate('',(zx,zy),(x,y),arrowprops=dict(linewidth=2,arrowstyle='-',color='lightblue'))
plt.show()
The problem lies in the unequal axes which makes it look like they are not at a right angle. Use plt.axis('equal') to have equal axis spans on x- and y-axis and a square figure with equal height and width. plt.axis('scaled') works the same way. As pointed out by #CedricZoppolo, you should set the equal aspect ratios before plt.show(). As per docs, setting the aspect ratio to "equal" means
same scaling from data to plot units for x and y
import numpy as np
import matplotlib.pyplot as plt
fig = plt.figure(figsize=(8,8))
# Your code here
plt.axis('equal')
plt.show()
Choosing a square figure is not necessary as it works also with rectangular figures as
fig = plt.figure(figsize=(8,6))
# Your code here
plt.axis('equal')
plt.show()
The blue lines not being perpendicular is due to axis not being equal.
You just need to add below line before plt.show()
plt.gca().set_aspect('equal')
Below you can see the resulted graph: