Connecting lines between points plotted in Matplotlib - python

I have 4 X and Y lists that I am plotting separately with matplotlib.pyplot, i.e. point1[x1,y1], point2[x2,y2], point3[x3,y3] and point4[x4,y4]. What I am trying to do in the plot is to connect point1 to point2, point2 to point3, etc. until all 4 points are connected which would represent a square in my case. The data is for dynamic x and y displacements for a rectangular pump I'm working on that shows if a displacement limitation is exceeded inside a vessel's moonpool.
Here is the code I have so far that gives me the following plot and the plot generated:
## SSLP displacement time histories to be plotted
point1 = (3.61, 4, -3)
point2 = (3.61, -4, -3)
point3 = (-3.61, -4, -3)
point4 = (-3.61, 4, -3)
SSLPXPoint1 = SSLP.TimeHistory('X', 1, objectExtra=OrcFxAPI.oeBuoy(point1))
SSLPYPoint1 = SSLP.TimeHistory('Y', 1, objectExtra=OrcFxAPI.oeBuoy(point1))
SSLPXPoint2 = SSLP.TimeHistory('X', 1, objectExtra=OrcFxAPI.oeBuoy(point2))
SSLPYPoint2 = SSLP.TimeHistory('Y', 1, objectExtra=OrcFxAPI.oeBuoy(point2))
SSLPXPoint3 = SSLP.TimeHistory('X', 1, objectExtra=OrcFxAPI.oeBuoy(point3))
SSLPYPoint3 = SSLP.TimeHistory('Y', 1, objectExtra=OrcFxAPI.oeBuoy(point3))
SSLPXPoint4 = SSLP.TimeHistory('X', 1, objectExtra=OrcFxAPI.oeBuoy(point4))
SSLPYPoint4 = SSLP.TimeHistory('Y', 1, objectExtra=OrcFxAPI.oeBuoy(point4))
# setup plot
caseName = os.path.splitext(info.modelFileName)[0]
point1Plot = [3.61, 4]
point2Plot = [3.61, -4]
point3Plot = [-3.61, -4]
point4Plot = [-3.61, 4]
vesselPointsX = [90.89, 100.89, 100.89, 90.89, 90.89]
vesselPointsY = [5, 5, -5, -5, 5]
moonpoolCLX = [89, 103]
moonpoolCLY = [0, 0]
fig = plt.figure(figsize=(20, 15))
ax = fig.add_subplot(1, 1, 1)
plt.plot(vesselPointsX, vesselPointsY, 'r', lw=2, label='OCV Moonpool Limits')
plt.plot(moonpoolCLX, moonpoolCLY, 'k--', label='Moonpool CL')
plt.plot(SSLPXPoint1, SSLPYPoint1, 'k')
plt.plot(SSLPXPoint2, SSLPYPoint2, 'k')
plt.plot(SSLPXPoint3, SSLPYPoint3, 'k')
plt.plot(SSLPXPoint4, SSLPYPoint4, 'k')
ax.set_title("SSLP Maximum Offsets Inside Moonpool for {}".format(caseName), fontsize=20)
ax.set_xlabel('Distance Along OCV from Stern [m]', fontsize=15)
ax.set_ylabel('Distance from Moonpool Centerline, (+) Towards Portside [m]', fontsize=15)
ax.set_xlim(89, 103)
ax.set_ylim(-7, 7)
plt.gca().set_aspect('equal', adjustable='box')
plt.draw()
plt.legend()
plt.tight_layout()
plt.show()
Any help would be greatly appreciated.
Thanks,
Brian

This should help you. Make your changes.
from matplotlib.pyplot import plot, show
plot([SSLPXPoint1, SSLPXPoint2], [SSLPYPoint1, SSLPYPoint2])
plot([SSLPXPoint3, SSLPXPoint2], [SSLPYPoint3, SSLPYPoint2])
plot([SSLPXPoint3, SSLPXPoint4], [SSLPYPoint3, SSLPYPoint4])
show()
Edited, because previous one was connecting all dots.

Related

How can I remove borders/frames of subplots while plotting xarray dataset using imshow of matplotlib in Python?

I have a netCDF file downloaded from the NASA website called gistemp1200_GHCNv4_ERSSTv5.nc.gz
I open the data using xarray that contains temperature anomaly data.
import xarray as xr
file = "../data/gistemp1200_GHCNv4_ERSSTv5.nc"
xr_df = xr.open_dataset(file)
xr_df_annual = xr_df.resample(time = "Y").mean()
anomaly = xr_df_annual["tempanomaly"]
anomaly looks as follows:
It contains annual temperature anomaly value relative to certain time period.
I want to plot the annual temperature values for four years in the form of subplots. I use the following code:
fig, axs = plt.subplots(2, 2, figsize = (10, 10))
fig.suptitle("Temperature anomaly in the end of last four decades\n relative to 1951-1980")
def get_plot(year, i, j, k):
ax = fig.add_subplot(2, 2, k,
projection = ccrs.PlateCarree())
ax.add_feature(NaturalEarthFeature('cultural', 'admin_0_countries', '10m'),
facecolor='none', edgecolor='black')
ax.set_extent([-150, 150, -55, 85])
xr_df_sub = anomaly.loc[f"{year}-12-31"]
ax.set_title(f"{year}")
ax.set_axis_off()
xr.plot.imshow(xr_df_sub,
ax = ax,
add_labels = False,
vmin = -4, vmax = 4,
cmap = "coolwarm",
add_colorbar = False,
interpolation = "bicubic")
axs[0, 0] = get_plot(1990, 0, 0, 1)
axs[0, 1] = get_plot(2000, 0, 1, 2)
axs[1, 0] = get_plot(2010, 0, 1, 3)
axs[1, 1] = get_plot(2020, 0, 1, 4)
# add colorbar
cax = fig.add_axes([0.92, 0.15, 0.01, 0.7]) #[left, bottom, width, height]
sm = plt.cm.ScalarMappable(cmap='coolwarm',
norm=plt.Normalize(vmin= -4, vmax= 4))
# fake up the array of the scalar mappable.
sm._A = []
lgd=fig.colorbar(sm, cax=cax, extend = "both"
).set_label("°C", rotation=0,y=1.1, labelpad= -35)
plt.show()
I created a function called get_plot(year, i, j, k) for each subplot. i, j refer to row and column number and k refer to the index number of the subplot. The function to plot the temperature anomaly for given year.
I got a plot as shown below:
This is the desired plot, however, I am getting outer black frames for each subplot, which I want to remove. I used following code:
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
ax.spines['bottom'].set_visible(False)
ax.spines['left'].set_visible(False)
However, it does not change anything. I also tried
fig.patch.set_visible(False)
It also does not change anything.
How can I remove the outer borders with 0 to 1 in x-axis and y-axis for each subplot? Also, I'd like to get advice on bringing subplots closer to each other?
Based on suggestion of Dahn, I figured out that I had used plt.subplot() and again used fig.add_subplot() twice.
I solved the problem as follows:
fig = plt.figure(figsize = (10, 10))
fig.suptitle("Temperature anomaly in the end of last four decades\n relative to 1951-1980",
y = 0.8)
plt.subplots_adjust(bottom = 0.3, top = 0.7, wspace=0.1, hspace= 0.25)
def get_plot(year, i, j, k):
ax = fig.add_subplot(2, 2, k,
projection = ccrs.PlateCarree())
ax.add_feature(NaturalEarthFeature('cultural', 'admin_0_countries', '10m'),
facecolor='none', edgecolor='black')
ax.set_extent([-150, 150, -55, 85])
xr_df_sub = anomaly.loc[f"{year}-12-31"]
ax.set_title(f"{year}")
ax.axis("off")
ax = xr.plot.imshow(xr_df_sub,
add_labels = True,
vmin = -4, vmax = 4,
cmap = "coolwarm",
add_colorbar = False,
interpolation = "bicubic",
)
return ax
ax[0, 0] = get_plot(1990, 0, 0, 1)
ax[0, 1] = get_plot(2000, 0, 1, 2)
ax[1, 0] = get_plot(2010, 0, 1, 3)
ax[1, 1] = get_plot(2020, 0, 1, 4)
# add colorbar
cax = fig.add_axes([0.92, 0.3, 0.02, 0.5]) #[left, bottom, width, height]
sm = plt.cm.ScalarMappable(cmap='coolwarm',
norm=plt.Normalize(vmin= -4, vmax= 4))
# fake up the array of the scalar mappable.
sm._A = []
lgd=fig.colorbar(sm, cax=cax, extend = "both"
).set_label("°C", rotation=0,y=1.1, labelpad= -35)
plt.show()
I got the required plot as shown below:

Plot a curve on top of 2 subplots simultaneously

EDIT: My question was closed because someone thought another question was responding to it (but it doesn't: Matplotlib different size subplots). To clarify what I want:
I would like to replicate something like what is done on this photo: having a 3rd dataset plotted on top of 2 subplots, with its y-axis displayed on the right.
I have 3 datasets spanning the same time interval (speed, position, precipitation). I would like to plot the speed and position in 2 horizontal subplots, and the precipitation spanning the 2 subplots.
For example in the code below, instead of having the twinx() only on the first subplot, I would like to have it overlap the two subplots (ie. on the right side have a y-axis with 0 at the bottom right of the 2nd subplot, and 20 at the top right of the 1st subplot).
I could I achieve that ?
import matplotlib.pyplot as plt
import numpy as np
fig, ax = plt.subplots(2,1,figsize=(20,15), dpi = 600)
#plot 1:
x = np.array([0, 1, 2, 3])
y = np.array([3, 8, 1, 10])
ax[0].plot(x,y, label = 'speed')
plt.legend()
#plot 2:
x = np.array([0, 1, 2, 3])
y = np.array([3, 8, 1, 10])
ax[1].plot(x,y, label = 'position')
plt.legend()
#plot 3:
x = np.array([0, 1, 2, 3])
y = np.array([10, 0, 4, 20])
ax2=ax[0].twinx()
ax2.plot(x,y, label = 'precipitation')
plt.legend(loc='upper right')
plt.show()
Best way I found is not very elegant but it works:
# Prepare 2 subplots
fig, ax = plt.subplots(2,1,figsize=(20,15), dpi = 600)
#plot 1:
# Dummy values for plotting
x = np.array([0, 1, 2, 3])
y = np.array([3, 8, 1, 10])
ax[0].plot(x,y, label = 'speed')
# Prints the legend
plt.legend()
#plot 2:
x = np.array([0, 1, 2, 3])
y = np.array([3, 8, 1, 10])
ax[1].plot(x,y, label = 'position')
plt.legend()
#plot 3:
x = np.array([0, 1, 2, 3])
y = np.array([10, 0, 4, 20])
# Add manually a 3rd subplot that stands on top of the 2 others
ax2 = fig.add_subplot(111, label="new subplot", facecolor="none")
# Move the y-axis to the right otherwise it will overlap with the ones on the left
ax2.yaxis.set_label_position("right")
# "Erase" every tick and label of this 3rd plot
ax2.tick_params(left=False, right=True, labelleft=False, labelright=True,
bottom=False, labelbottom=False)
# This line merges the x axes of the 1st and 3rd plot, and indicates
# that the y-axis of the 3rd plot will be drawn on the entirety of the
# figure instead of just 1 subplot (because fig.add_subplot(111) makes it spread on the entirety of the figure)
ax[0].get_shared_x_axes().join(ax[0],ax2)
ax2.plot(x,y, label = 'precipitation')
# Prints the legend in the upper right corner
plt.legend(loc='upper right')
plt.show()

How to have 2 different scales on same Y axis in Python using Matplotlib

I need to draw 4 X vs Y plots, where X is constant but different Y Values. I used below code to get the plots but need to show the Y scale on either side of the Secondary Y axes (Y Axis 2 in the image), the way Primary Y Axis has (both inward and outward). Right now, it comes on same side of Secondary Y Axis. How to modify the below code to get this done.
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
fig.subplots_adjust(left=0.1,right=0.9)
twin2 = ax.twinx()
twin3 = ax.twinx()
twin4 = ax.twinx()
twin3.spines['right'].set_position(("axes", 0.0))
twin4.spines['right'].set_position(('axes', 1.0))
p1, = ax.plot([0, 1, 2], [0, 1, 2], "b-", label="plot1")
p2, = twin2.plot([0, 1, 2], [0, 3, 2], "r-", label="plot2")
p3, = twin3.plot([0, 1, 2], [50, 30, 15], "g-", label="plot3")
p4, = twin4.plot([0, 1, 2], [5, 3, 1], "y-", label="plot4")
ax.set_xlim(0, 2)
ax.set_ylim(0, 2)
ax.set_xlabel("X Axis")
ax.set_ylabel("Y Axis")
twin2.set_ylabel("Y Axis 2")
plt.show()
On your 4th axes, set tick_left and move the left spine to the right-hand side:
twin4.yaxis.tick_left()
twin4.spines['left'].set_position(('axes', 1.0))

How to create a step-plot with a gradient based on y-value?

In Python matplotlib, how can you get the line in a line or step plot to display a gradient based on the y-value?
Example plot (made in Tableau):
Code for step plot with a line that changes gradient according to x-value, adapted from this answer:
fig, ax = plt.subplots(figsize=(10, 4))
x = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
y = [2, 3, 9, 10, 2, 9, 0, 1, 9, 1, -8]
T = np.linspace(0,1,np.size(x))**2
s = 1
for i in range(0, len(x)-s, s):
ax.step(x[i:i+s+1], y[i:i+s+1], marker='.', color=(0.0,0.5,T[i]))
ax.tick_params(axis='both', colors='lightgray', labelsize=8)
The following code is inspired by the multicolored-line example from the matplotlib docs. First the horizontal line segments are drawn and colored using their y-value. The vertical segments are subdivided in small chunks to colored individually.
vmin of the norm is set a bit lower to avoid the too-light range of the colormap.
import matplotlib.pyplot as plt
from matplotlib.collections import LineCollection
import numpy as np
x = np.arange(50)
y = np.random.randint(-3, 4, x.size).cumsum()
fig, ax = plt.subplots()
norm = plt.Normalize(y.min() - y.ptp() * .2, y.max())
cmap = 'inferno_r' # 'Reds'
horizontal_lines = np.array([x[:-1], y[:-1], x[1:], y[:-1]]).T.reshape(-1, 2, 2)
hor_lc = LineCollection(horizontal_lines, cmap=cmap, norm=norm)
hor_lc.set_array(y[:-1])
ax.add_collection(hor_lc)
factor = 10
long_y0 = np.linspace(y[:-1], y[1:], factor)[:-1, :].T.ravel()
long_y1 = np.linspace(y[:-1], y[1:], factor)[1:, :].T.ravel()
long_x = np.repeat(x[1:], factor - 1)
vertical_lines = np.array([long_x, long_y0, long_x, long_y1]).T.reshape(-1, 2, 2)
ver_lc = LineCollection(vertical_lines, cmap=cmap, norm=norm)
ver_lc.set_array((long_y0 + long_y1) / 2)
ax.add_collection(ver_lc)
ax.scatter(x, y, c=y, cmap=cmap, norm=norm)
plt.autoscale() # needed in case the scatter plot would be omited
plt.show()
Here is another example, with a black background. In this case the darkest part of the colormap is avoided. The changed code parts are:
y = np.random.randint(-9, 10, x.size)
ax.patch.set_color('black')
norm = plt.Normalize(y.min(), y.max() + y.ptp() * .2)
cmap = 'plasma_r'
Here is an example with a TwoSlopeNorm and the blue-white-red colormap:
from matplotlib.colors import TwoSlopeNorm
y = np.random.uniform(-1, 1, x.size * 10).cumsum()[::10]
y = (y - y.min()) / y.ptp() * 15 - 5
norm = TwoSlopeNorm(vmin=-5, vcenter=0, vmax=10)
cmap = 'bwr'

Using Python to graph student progress

I'm experimenting with python graphing for the first time and I want try what I've learned by graphing some of my student's progress. My progress data is in a table in a format the same as what I have mocked up below. I Have used MSPaint (sorry) to mock up what I think would be a decent graph to show them their progress.
What is the right name for this type of graph and what would be the first steps to achieve it? I can't see anything quite like it on http://matplotlib.org/ or on https://plot.ly/
Please feel free to tell me I am laying out the graph all wrong.
I took a stab at generating your example chart in matplotlib. I suspect others with stronger matplotlib-foo could greatly improve this :)
import matplotlib.pyplot as plt
import numpy as np
students = ['steve', 'bob', 'ralph']
progress = [
[1, 3, 4, 4, 5],
[2, 3, 4, 4, 5],
[3, 3, 4, 5, 5]]
(fig, ax) = plt.subplots(1, 1)
# Offset lines by some fraction of one
dx = 1.0 / len(progress)
xoff = dx / 2.0
for i, (name, data) in enumerate(zip(students, progress)):
ax.plot(np.arange(len(data)) + xoff, data, label=name, marker='o')
xoff += dx
ax.set_xticks(np.arange(0, len(progress[0]) + 0.01, dx), minor=True)
ax.set_xticks(np.arange(1, len(progress[0])+1))
labels = students * len(progress[0])
week = 1
for i,l in enumerate(labels):
if l == students[1]:
# hack to add Week label below the second label for each block
labels[i] = "%s\nWeek %s" % (l, week)
week += 1
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
ax.set_xticklabels(labels, fontsize=8, ha='left', minor=True)
ax.set_xticklabels([])
ax.tick_params(which='both', direction = 'out')
ax.tick_params(axis='x', which='major', width=4)
ax.tick_params(axis='x', which='major', length=7)
ax.tick_params(axis='y', which='major', width=0, length=0)
ax.set_ylim(0, 6)
ax.set_yticks(range(1, 6))
ax.get_xaxis().tick_bottom()
ax.get_yaxis().tick_left()
ax.set_title("Student Progress")
ax.legend(loc='best')
fig.show()
Something like this is probably what you are looking for
import matplotlib.pyplot as plt
weeks = range(1,6)
steve = [1, 3, 4, 4, 5]
bob = [2, 3, 4, 4, 5]
ralph = [3, 3, 4, 5, 5]
plt.figure()
plt.plot(weeks, bob, label='Bob')
plt.plot(weeks, steve, label='Steve')
plt.plot(weeks, ralph, label='Ralph')
plt.title('Student Progress')
plt.ylabel('Score')
plt.xlabel('Week')
plt.xticks(range(6))
plt.ylim(0, 6)
plt.legend(loc='lower right')
plt.show()
Try bokeh. It supports categorical axes, and additionally supports datetime categorical axes (docs link)

Categories

Resources