Filled errorbars in matplotlib (rectangles) - python

Is it possibile to make a plot like this in matplotlib? I care only about the red filled rectangles, the crosses are easily done with errorbar.

Unfortunately errorbar can't do this, but you can create a PatchCollection from your error data which can easily be added to the axes. See this quick script for an example of how you could do this.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.collections import PatchCollection
from matplotlib.patches import Rectangle
# Number of data points
n=5
# Dummy data
x=np.arange(0,n,1)
y=np.random.rand(n)*5.
# Dummy errors (above and below)
xerr=np.random.rand(2,n)
yerr=np.random.rand(2,n)
# Create figure and axes
fig,ax = plt.subplots(1)
# Plot data points
ax.errorbar(x,y,xerr=xerr,yerr=yerr,fmt='None',ecolor='k')
# Function to plot error boxes
def makeErrorBoxes(xdata,ydata,xerror,yerror,fc='r',ec='None',alpha=0.5):
# Create list for all the error patches
errorboxes = []
# Loop over data points; create box from errors at each point
for xc,yc,xe,ye in zip(xdata,ydata,xerror.T,yerror.T):
rect = Rectangle((xc-xe[0],yc-ye[0]),xe.sum(),ye.sum())
errorboxes.append(rect)
# Create patch collection with specified colour/alpha
pc = PatchCollection(errorboxes,facecolor=fc,alpha=alpha,edgecolor=ec)
# Add collection to axes
ax.add_collection(pc)
# Call function to create error boxes
makeErrorBoxes(x,y,xerr,yerr)
# Add some space around the data points on the axes
ax.margins(0.1)
plt.show()

Drawing squares is really easy with matplotlib.patches, e.g.:
import matplotlib.pyplot as pl
import matplotlib.patches
pl.figure()
ax = pl.gca()
ax.add_patch(
matplotlib.patches.Rectangle(
(1.0, 1.0), # x, y
2.0, # width
2.0, # height
color='r', alpha=0.5
) )
ax.set_aspect(1)
pl.xlim(0,4)
pl.ylim(0,4)

Related

Matplotlib: How to recreate `6 petal` polar diagram

For an assignment, I have to recreate the following plot (including all labels and ticks):
This is what I have tried so far with my code
import numpy as np
import matplotlib.pyplot as plt
nmax=101 # choose a high number to "smooth out" lines in plots
x = np.linspace(0,20,nmax) # create an array x
y_br = np.sin(3*x) # y for the bottom right subplot
fig = plt.figure()
ax4 = plt.subplot(224, projection = 'polar')
ax4.plot(x, y_br, 'tab:blue')
But if you were to run this yourself, this does not replicate the plot. What function could be used here and how can tick marks be changed in polar plots? Thanks in advance?

Python: Recreating rose plot

For an assignment, I am trying to recreate the rose plot
Here is my code:
import numpy as np
import matplotlib.pyplot as plt
nmax=101 # choose a high number to "smooth out" lines in plots
x_angle = np.linspace(0,2*np.pi,nmax) # create an array x for bottom right
y_br = abs(np.cos(3*x_angle)) # y for the bottom right subplot
# bottom right subplot controls
plt.plot(x_angle, y_br, 'tab:blue')
Is there a way to get it to look any closer to the original plot (e.g. make the petals narrower, set tick marks to 0.0, 0.5, 1.0)?
You can use polar projection for this type of plot:
for the ticks, it is set by rticks
for the petals, it is controlled by the nmax feature
Here is a code that gives a better drawing:
import numpy as np
import matplotlib.pyplot as plt
nmax=int(1e5) # choose a high number to "smooth out" lines in plots
x_angle = np.linspace(0,2*np.pi,nmax) # create an array x for bottom right
y_br = abs(np.cos(3*x_angle)) # y for the bottom right subplot
fig, ax = plt.subplots(subplot_kw={'projection': 'polar'})
ax.plot(x_angle, y_br, lw= 3)
ax.set_rticks([0, 0.5, 1]) # Less radial ticks

Matplotlib: orthographic projection of 3D data (in 2D plot)

I'm trying to plot 3D data in 2D using orthographic projection. Here is partially what I'm looking for:
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
fig = plt.figure(figsize=(10,10),facecolor='white')
axs = [fig.add_subplot(223)]
axs.append(fig.add_subplot(224))#,sharey=axs[0]))
axs.append(fig.add_subplot(221))#,sharex=axs[0]))
rng = np.random.default_rng(12345)
values = rng.random((100,3))-.5
values[:,1] = 1.6*values[:,1]
values[:,2] = .5*values[:,2]
for ax,axis in zip(axs,['y','x','z']):
axis1,axis2={'x':(1,2),'y':(0,2),'z':(0,1)}[axis]
ax.add_patch(plt.Circle([0,0], radius=.2, color='pink',zorder=-20))
ax.scatter(values[:,axis1],values[:,axis2])
axs[0].set_xlabel('x')
axs[2].set_ylabel('y')
axs[1].set_xlabel('y')
axs[0].set_ylabel('z')
fig.subplots_adjust(.08,.06,.99,.99,0,0)
plt.show()
There are some issues with this plot and the fixes I tried: I would need 'equal' aspect so that the circles are actually circle. I would also need the circles to be of the same size in each subplot. Finally, I would like the space to be optimized (i.e. with as little white space inside and between the subplots as possible).
I have tried sharing the axis between the subplots, then doing .axis('scaled') or .set_aspect('equal','box',share=True) for each axes, but the axis end up not being properly shared, and the circle in each subplot end up of different sizes. And while it crops the subplots to the data, it leaves a lot of space between the subplots. .axis('equal') or .set_aspect('equal','datalim',share=True) without axis shared leaves white space inside the subplots, and with shared axis, it leaves out some data.
Any way to make it work? And it would be perfect if it can work on matplotlib 3.4.3.
You can use a common xlim, ylim for your subplots and set your equal ratio with ax.set_aspect(aspect='equal', adjustable='datalim'):
See full code below:
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
fig = plt.figure(figsize=(10,10),facecolor='white')
axs = [fig.add_subplot(223)]
axs.append(fig.add_subplot(224))#,sharey=axs[0]))
axs.append(fig.add_subplot(221))#,sharex=axs[0]))
rng = np.random.default_rng(12345)
values = rng.random((100,3))-.5
values[:,1] = 1.6*values[:,1]
values[:,2] = .5*values[:,2]
for ax,axis in zip(axs,['y','x','z']):
axis1,axis2={'x':(1,2),'y':(0,2),'z':(0,1)}[axis]
ax.add_patch(plt.Circle([0,0], radius=.2, color='pink',zorder=-20))
ax.scatter(values[:,axis1],values[:,axis2])
ax.set_xlim([np.amin(values),np.amax(values)])
ax.set_ylim([np.amin(values),np.amax(values)])
ax.set_aspect('equal', adjustable='datalim')
axs[0].set_xlabel('x')
axs[2].set_ylabel('y')
axs[1].set_xlabel('y')
axs[0].set_ylabel('z')
fig.subplots_adjust(.08,.06,.99,.99,0,0)
plt.show()
The output gives:
I made it work using gridspec (I changed scatter for plot to visually make sure no data gets left out). It requires some tweaking of the figsize to really minimize the white space within the axes. Thank you to #jylls for the intermediate solution.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.gridspec import GridSpec
%matplotlib inline
rng = np.random.default_rng(12345)
values = rng.random((100,3))-.5
values[:,1] = 1.6*values[:,1]
values[:,2] = .5*values[:,2]
fig = plt.figure(figsize=(10,8),facecolor='white')
ranges = np.ptp(values,axis=0)
gs = GridSpec(2, 2, None,.08,.06,.99,.99,0,0, width_ratios=[ranges[0], ranges[1]], height_ratios=[ranges[1], ranges[2]])
axs = [fig.add_subplot(gs[2])]
axs.append(fig.add_subplot(gs[3]))#,sharey=axs[0]))
axs.append(fig.add_subplot(gs[0]))#,sharex=axs[0]))
for ax,axis in zip(axs,['y','x','z']):
axis1,axis2={'x':(1,2),'y':(0,2),'z':(0,1)}[axis]
ax.add_patch(plt.Circle([0,0], radius=.2, color='pink',zorder=-20))
ax.plot(values[:,axis1],values[:,axis2])
ax.set_aspect('equal', adjustable='datalim')
axs[0].set_xlabel('x')
axs[2].set_ylabel('y')
axs[1].set_xlabel('y')
axs[0].set_ylabel('z')
plt.show()

How to plot a 2-D cone in python (matplotlib)?

I'm considerably new to python and making a map of a room. I've plotted the room, obstacles etc. And some points (which are sensors). Now I want make a 2-D cone which shows the area in which sensors see. I will have an angle and radius for the cone.
I've tried searching but mostly 3-D cones have been discussed here in previous questions. How the cone should look
Any guidance is appreciated
You would use matplotlib.patches.Wedge such as this example. Another example that I've reduced to the more relevant bits is:
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
from matplotlib.collections import PatchCollection
import numpy as np
fig, ax = plt.subplots()
patches = []
wedge = mpatches.Wedge((.5, .5), 0.5, 30, 270, ec="none")
patches.append(wedge)
colors = np.linspace(0, 1, len(patches))
collection = PatchCollection(patches, cmap=plt.cm.hsv, alpha=0.3)
collection.set_array(np.array(colors))
ax.add_collection(collection)
plt.show()
Which produces something like:
Obviously you will need to tweak the theta1 and theta2 from their 30 and 270 to fit whatever angle you are trying to represent, and move the origin to wherever the sensors are located. Additionally you may want to color them all the same, instead of a rainbow but I'll let you figure out the details XD
Ended up using the following:
import matplotlib.pyplot as plt
from matplotlib.patches import Wedge
fig, ax = plt.subplots()
patches=[]
ax.axis('equal')
we = Wedge((2756.6747,5339751.8148),10,30,180,edgecolor='b',facecolor='none')
patches.append(we)
ax.add_artist(we)
ax.set(xlim=[2740, 2800], ylim=[5339740, 5339780])
plt.show()
thanks to the direction given by #reedinationer

Add Second Colorbar to a Seaborn Heatmap / Clustermap

I was trying to help someone add a colorbar for the vertical blue bar in the image below. We tried many variations of plt.colorbar(row_colors) (like above and below sns.clustermap()) and looked around online for 2 hours, but no luck. We just want to add a colorbar for the blues, please help!
import pickle
import numpy as np
import seaborn as sns
import pandas as pd
import matplotlib.pyplot as plt
feat_mat, freq, label = pickle.load(open('file.pkl', 'rb'))
feat_mat_df = pd.DataFrame(feat_mat[4])
freq_df = pd.DataFrame(freq)
freq_df_transposed = freq_df.transpose()
my_palette = dict(zip(set(freq_df_transposed[int('4')]), sns.color_palette("PuBu", len(set(freq_df_transposed[int('4')]))))))
row_colors = freq_df_transposed[int('4')].map(my_palette)
sns.clustermap(feat_mat_df, metric="euclidean", standard_scale=1, method="complete", cmap="coolwarm", row_colors = row_colors)
plt.show()
This is where he based his code from: #405 Dendrogram with heatmap and coloured leaves
I think something like this should work for your purposes- I didn't have a clustermap example available but the logic is the same to do what you want to do. Basically-you're going to take that list of colors you made and imshow it, then hide the imshow plot, and plot the colorbar in its place.
In my example, I use make_axes_locatable to place axes next to the plot with your data to put the colorbar inside - https://matplotlib.org/2.0.2/mpl_toolkits/axes_grid/users/overview.html. I find placing a new axes for other objects (legends color maps or otherwise) easier than trying to draw them on the same axes.
import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np; np.random.seed(0)
import seaborn as sns
from mpl_toolkits.axes_grid1 import make_axes_locatable
import random
uniform_data = np.random.rand(10, 12)
fig, ax = plt.subplots(1,1, figsize = (5,5))
divider = make_axes_locatable(ax)
axDivY = divider.append_axes( 'right', size=0.2, pad= 0.1)
axDivY2 = divider.append_axes( 'right', size=0.2, pad= 0.2)
# we will use this for the colorscale bar
axDivY3 = divider.append_axes( 'right', size=0.2, pad= 0.2)
ax1 = sns.heatmap(uniform_data, ax=ax, cbar_ax=axDivY)
# the palette you were using to make the label column on the clustermap
# some simulated labels for your data with values
color_label_list =[random.randint(0,20) for i in range(20)]
pal = sns.color_palette("PuBu", len(set(color_label_list)))
n = len(pal)
size = 1
# plot the colors with imshow to make a colormap later
ax2 = axDivY2.imshow(np.array([color_label_list]),
cmap=mpl.colors.ListedColormap(list(pal)),
interpolation="nearest", aspect="auto")
# turn off the axes so they aren't visible- note that you need ax.axis('off) if you have older matplotlib
axDivY2.set_axis_off()
axDivY2.set_visible(False)
# plot the colorbar on the other axes (which is on top of the one that we turned off)
plt.colorbar(ax2, cax = axDivY3) ;

Categories