Joining the points in a scatter plot - matplotlib

I’ve a scatter plot which almost looks like a circle. I would like to join the outer points with a line to show that almost circle like shape. Is there a way to do that in matplotlib?

You can use ConvexHull from scipy.spatial to find the outer points of your scatter plot and then connect these points using a PolyCollection from matplotlib.collections:
from matplotlib import pyplot as plt
import numpy as np
from scipy.spatial import ConvexHull
from matplotlib.collections import PolyCollection
fig, ax = plt.subplots()
length = 1000
#using some normally distributed data as example:
x = np.random.normal(0, 1, length)
y = np.random.normal(0, 1, length)
points = np.concatenate([x,y]).reshape((2,length)).T
hull = ConvexHull(points)
ax.scatter(x,y)
ax.add_collection(PolyCollection(
[points[hull.vertices,:]],
edgecolors='r',
facecolors='w',
linewidths=2,
zorder=-1,
))
plt.show()
The result looks like this:
EDIT
Actually, you can skip the PolyCollection and just do a simple line plot using the hull vertices. You only have to make the line circular by appending the first vertex to the list of vertices (making that list one element longer):
circular_hull_verts = np.append(hull.vertices,hull.vertices[0])
ax.plot(
x[circular_hull_verts], y[circular_hull_verts], 'r-', lw=2, zorder=-1,
)
EDIT 2:
I noticed that there is an example in the scipy documentation that looks quite similar to mine.

Related

Plotting fuzzy data with matplotlib

I don't know where to start, as I think it is a new approach for me. Using matplotlib with python, I would like to plot a set of fuzzy numbers (for instance a set of triangular or bell curve fuzzy numbers) as in the picture below:
You can plot the curves recurrently. My try at reproducing your example (including the superposition of labels 1 and 6):
import matplotlib.pyplot as plt
import numpy as np
# creating the figure and axis
fig, ax = plt.subplots(1,1,constrained_layout=True)
# generic gaussian
y = np.linspace(-1,1,100)
x = np.exp(-5*y**2)
center_x = (0,2,4,1,3,0,5)
center_y = (6,2,3,4,5,6,7)
# loop for all the values
for i in range(len(center_x)):
x_c, y_c = center_x[i], center_y[i]
# plotting the several bells, relocated to (x_c, y_c)
ax.plot(x + x_c,y + y_c,
color='red',linewidth=2.0)
ax.plot(x_c,y_c,
'o',color='blue',markersize=3)
# adding label
ax.annotate(
str(i+1),
(x_c - 0.1,y_c), # slight shift in x
horizontalalignment='right',
verticalalignment='center',
color='blue',
)
ax.grid()
Every call to ax.plot() is adding points or curves (to be more precise, Artists) to the same axis. The same for ax.annotate() to create the labels.

Is there a way to draw shapes on a python pandas plot

I am creating shot plots for NHL games and I have succeeded in making the plot, but I would like to draw the lines that you see on a hockey rink on it. I basically just want to draw two circles and two lines on the plot like this.
Let me know if this is possible/how I could do it
Pandas plot is in fact matplotlib plot, you can assign it to variable and modify it according to your needs ( add horizontal and vertical lines or shapes, text, etc)
# plot your data, but instead diplaying it assing Figure and Axis to variables
fig, ax = df.plot()
ax.vlines(x, ymin, ymax, colors='k', linestyles='solid') # adjust to your needs
plt.show()
working code sample
import pandas as pd
import matplotlib.pyplot as plt
import seaborn
from matplotlib.patches import Circle
from matplotlib.collections import PatchCollection
df = seaborn.load_dataset('tips')
ax = df.plot.scatter(x='total_bill', y='tip')
ax.vlines(x=40, ymin=0, ymax=20, colors='red')
patches = [Circle((50,10), radius=3)]
collection = PatchCollection(patches, alpha=0.4)
ax.add_collection(collection)
plt.show()

Is it possible to do draw plot in matplotlib where all points are linked to the x axis?

I am trying to find a way in matplotlib to draw a lineplot, except that I don't want to draw a line between points. Instead I want to draw a perpendicular line between each of my points and the x axis.
When I do a standard plot, I obtain the following :
import numpy as np
import matplotlib.pyplot as plt
data = np.array([0,1,3,2,3,1,4])
plt.plot(data)
plt.xlim([-0.2,6.2])
plt.ylim([-0.2,5])
Instead I want to obtain the following :
Any ideas how to do this ?
Thanks
There are two other options apart from stem and bar chart is the following using vlines() and LineCollection()
Option 1 -- Using vlines()
for x, y in enumerate(data):
plt.vlines(x=x, ymin=0, ymax=y, color='r')
Or in a single line without using loops
plt.vlines(x=range(data.size), ymin=0, ymax=data, color='r')
Option 2 -- Using LineCollection()
from matplotlib.collections import LineCollection
lines = [[(x, 0), (x, y)] for x, y in enumerate(data)]
linesCol = LineCollection(lines, linewidths=3, color='r')
fig, ax = plt.subplots()
ax.add_collection(linesCol)
plt.scatter(range(len(data)), data, s=0)

Customize the axis label in seaborn jointplot

I seem to have got stuck at a relatively simple problem but couldn't fix it after searching for last hour and after lot of experimenting.
I have two numpy arrays x and y and I am using seaborn's jointplot to plot them:
sns.jointplot(x, y)
Now I want to label the xaxis and yaxis as "X-axis label" and "Y-axis label" respectively. If I use plt.xlabel, the labels goes to the marginal distribution. How can I make them appear on the joint axes?
sns.jointplot returns a JointGrid object, which gives you access to the matplotlib axes and you can then manipulate from there.
import seaborn as sns
import numpy as np
# example data
X = np.random.randn(1000,)
Y = 0.2 * np.random.randn(1000) + 0.5
h = sns.jointplot(X, Y)
# JointGrid has a convenience function
h.set_axis_labels('x', 'y', fontsize=16)
# or set labels via the axes objects
h.ax_joint.set_xlabel('new x label', fontweight='bold')
# also possible to manipulate the histogram plots this way, e.g.
h.ax_marg_y.grid('on') # with ugly consequences...
# labels appear outside of plot area, so auto-adjust
h.figure.tight_layout()
(The problem with your attempt is that functions such as plt.xlabel("text") operate on the current axis, which is not the central one in sns.jointplot; but the object-oriented interface is more specific as to what it will operate on).
Note that the last command uses the figure attribute of the JointGrid. The initial version of this answer used the simpler - but not object-oriented - approach via the matplotlib.pyplot interface.
To use the pyplot interface:
import matplotlib.pyplot as plt
plt.tight_layout()
Alternatively, you can specify the axes labels in a pandas DataFrame in the call to jointplot.
import pandas as pd
import seaborn as sns
x = ...
y = ...
data = pd.DataFrame({
'X-axis label': x,
'Y-axis label': y,
})
sns.jointplot(x='X-axis label', y='Y-axis label', data=data)

Creating a 1D heat map from a line graph

Is it possible to create a 1D heat map from data in a line graph? i.e. I'd like the highest values in y to represent the warmer colours in a heat map. I've attached an example image of the heat map I'd like it to look like as well as data I currently have in the line graph.
1D heat map and graph example:
To get the heatmap in the image shown I used the following code in python with matplotlib.pyplot:
heatmap, xedges, yedges = np.histogram2d(x, y, bins=(np.linspace(0,length_track,length_track+1),1))
extent = [0, length_track+1, 0, 50]
plt.imshow(heatmap.T, extent=extent, origin='lower', cmap='jet',vmin=0,vmax=None)
But I believe this only works if the data is represented as a scatter plot.
If we assume that the data is equally spaced, one may use an imshow plot to recreate the plot from the question.
import matplotlib.pyplot as plt
import numpy as np; np.random.seed(1)
plt.rcParams["figure.figsize"] = 5,2
x = np.linspace(-3,3)
y = np.cumsum(np.random.randn(50))+6
fig, (ax,ax2) = plt.subplots(nrows=2, sharex=True)
extent = [x[0]-(x[1]-x[0])/2., x[-1]+(x[1]-x[0])/2.,0,1]
ax.imshow(y[np.newaxis,:], cmap="plasma", aspect="auto", extent=extent)
ax.set_yticks([])
ax.set_xlim(extent[0], extent[1])
ax2.plot(x,y)
plt.tight_layout()
plt.show()