Different colors for the edges of a bar in a bar chart [duplicate] - matplotlib

I would like to set different edgecolors for the different edges of a bar plot, plotted with matplotlib.axes.Axes.bar. Does anyone know how to do this? For example: black edges for right edge but no edges/edgecolor for upper, lower and left edges.
Thanks for help!

The bars of a bar plot are of type matplotlib.patches.Rectangle which can only have one facecolor and only one edgecolor. If you want one side to have another color, you can loop through the generated bars and draw a separate line over the desired edge.
The example code below implements the right side draw in a thick black line. As a separate line doesn't join perfectly with the rectangle, the code also draws the left and upper side with the same color as the bar.
from matplotlib import pyplot as plt
import numpy as np
fig, ax = plt.subplots()
bars = ax.bar(np.arange(10), np.random.randint(2, 50, 10), color='turquoise')
for bar in bars:
x, y = bar.get_xy()
w, h = bar.get_width(), bar.get_height()
ax.plot([x, x], [y, y + h], color=bar.get_facecolor(), lw=4)
ax.plot([x, x + w], [y + h, y + h], color=bar.get_facecolor(), lw=4)
ax.plot([x + w, x + w], [y, y + h], color='black', lw=4)
ax.margins(x=0.02)
plt.show()
PS: If the bars were created in another way (or example using Seaborn), you can investigate the containers of the ax. ax.containers is a list of containers; a container is a grouping of individual graphic objects, usually rectangles. There can be multiple containers, for example in a stacked bar plot.
for container in ax.containers:
for bar in container:
if type(bar) == 'matplotlib.patches.Rectangle':
x, y = bar.get_xy()
w, h = bar.get_width(), bar.get_height()
ax.plot([x + w, x + w], [y, y + h], color='black')

Related

set_position and set_size_inches does not work properly when overlaying imshow and scatter in matplotlib

I am trying to create an image from a matrix z2 over a raster defined by np.meshgrid(grid_x, grid_y) such that the value of the image at vx=grid_x[i], vy=grid_y[j] is z2[i, j]. On top of this image, I am trying to add a scatter plot of a number of points obtained by three vectors x, y, z such that the i-th point has the coordinate (x[k], y[k]) and the value z[k]. All of these scattered points lies within the region of the aforementioned raster.
Here's an example of the aforementioned data I am trying to plot.
import numpy as np
np.random.seed(1)
z2 = np.ones((1000, 1000)) * 0.66
z2[0, 0] = 0
z2[-1, -1] = 1
x = np.random.rand(1000) * 1000
y = np.random.rand(1000) * 1000
z = np.random.rand(1000)
grid_x = np.linspace(0, 999, 1000)
grid_y = np.linspace(0, 999, 1000)
In order to do this, I am using a 2D plot where the x and y values are used to define the position of the points and z is indicated by a color drawn from a colormap.
What is required of this image is that 1) there should be no white space between the actual plot and the edge of the figure; 2) the unit length on the x and y axis should be equal; 3) the image should not be too large. In order to achieve these, I am using the following code for plotting.
import matplotlib.pyplot as plt
from matplotlib import cm
def plot_img(x, y, z, grid_x, grid_y, z2, set_fig_size=True):
# determine the figure size
if set_fig_size:
height, width = np.array(z2.shape, dtype=float)
dpi = max(max(640 // height, 640 // width), 1)
width, height = width * dpi, height * dpi
plt.gcf().set_size_inches(width, height)
plt.gcf().set_dpi(dpi)
# plot the figure
plt.gca().axis('off')
plt.gca().axis('equal')
plt.gca().set_position([0, 0, 1, 1])
plt.xlim((grid_x[0], grid_x[-1]))
plt.ylim((grid_y[0], grid_y[-1]))
# the raster
cmap = cm.get_cmap('gray')
cmap.set_bad(color='red', alpha=0.5)
plt.imshow(z2, cmap=cmap, interpolation='none', origin='lower',
extent=(grid_x[0], grid_x[-1], grid_y[0], grid_y[-1]))
# the scatter plot
min_z, max_z = np.min(z), np.max(z)
c = (z - min_z) / (max_z - min_z)
plt.scatter(x, y, marker='o', c=c, cmap='Greens')
plt.show()
Strangely, when I run plot_img(x, y, z, grid_x, grid_y, z2) using the aforementioned example data, the following image shows up.
Essentially, only the raster data got plotted, while the scattered data is not.
I then tried plot_img(x, y, z, grid_x, grid_y, z2, set_fig_size=False). The result is
Note that here to clearly show the white spaces in the figure, I kept the background of PyCharm surrounding it. Essentially, there are white spaces that I do not wish included in this figure.
I wonder why this is happening, and how I can fix the code to get the correct output, which is essentially the second result without the white spaces. Thanks!
Replace your dpi and figsize code by
# determine the figure size
height, width = np.array(z2.shape, dtype=float)
dpi = 200
# get size in inches:
width, height = height / dpi, width / dpi
plt.gcf().set_size_inches(width, height)
plt.gcf().set_dpi(dpi)
and you will have a 1000x1000 pixel figure, which at 200 dpi is 5"x5".

Tick labels in between colors (discrete colorer)

Hi I want to put the ticklabels between colors (center of the intervals), and the figure is plotted by discrete colors. But the min value is not 0. How can I write the code to do that?
I used following code to do that, but what I got is wrong...
n_clusters = len(cbar_tick_label)
tick_locs = (np.arange(n_clusters)+0.5)*(n_clusters-1)/(n_clusters)
cbar.set_ticks(tick_locs)
cbar.set_ticklabels(cbar_tick_label)
This code is from question: Discrete Color Bar with Tick labels in between colors. But it does not work when the min value of data is not zero.
Thanks!
Suppose there are N (e.g. 6) clusters. If you subdivide the range from the lowest number (e.g. 5) to the highest number (e.g. 10) into N equal parts, there will be a tick at every border between color cells. Subdividing into 2*N+1 equal parts, will also have a tick in the center of each color cell. Now, skipping every other of these 2*N+1 ticks will leave us with only the cell centers. So, np.linspace(5, 10, 6*2+1) are the ticks for borders and centers; taking np.linspace(5, 10, 6*2+1)[1::2] will be only the centers.
import numpy as np
import matplotlib.pyplot as plt
x, y = np.random.rand(2, 100)
c = np.random.randint(5, 11, x.shape)
n_clusters = c.max() - c.min() + 1
fig, ax = plt.subplots()
cmap = plt.get_cmap('inferno_r', n_clusters)
scat = ax.scatter(x, y, c=c, cmap=cmap)
cbar = plt.colorbar(scat)
tick_locs = np.linspace(c.min(), c.max(), 2 * n_clusters + 1)[1::2]
cbar_tick_label = np.arange(c.min(), c.max() + 1)
cbar.set_ticks(tick_locs)
cbar.set_ticklabels(cbar_tick_label)
plt.show()

In Matplotlib, how can I clear an axes' contents without erasing its axis labels?

Is there an alternative to axes.clear() that leaves the axis labels untouched, while erasing the axes' contents?
Context:
I have an interactive script that flips through some flow images, and for each image, plots it using axes.quiver(). If I don't call axes.clear() between calls to axes.quiver(), each quiver() call just adds more arrows to the plot without first erasing the previously added arrows. However, when I call axes.clear(), it nukes the axes labels. I can re-set them, but it's a bit annoying.
You can remove the artists from an axes using the remove() of the artists. Below is a code showing two options to do so.
import matplotlib.pyplot as plt
import numpy as np
X, Y = np.meshgrid(np.arange(0, 2 * np.pi, .2), np.arange(0, 2 * np.pi, .2))
U = np.cos(X)
V = np.sin(Y)
plt.figure()
plt.title('Arrows scale with plot width, not view')
plt.xlabel('xlabel')
plt.xlabel('ylabel')
Q = plt.quiver(X, Y, U, V, units='width')
l, = plt.plot(X[0,:], U[4,:]+2)
# option 1, remove single artists
#Q.remove()
#l.remove()
# option 2, remove all lines and collections
for artist in plt.gca().lines + plt.gca().collections:
artist.remove()
plt.show()

How to plot animated dots in different colors with matplotlib?

I have a function that generates animated dots, here is the part that causes a problem :
dots = [dot() for i in range(N)]
fig = plt.figure()
ax = plt.axes(xlim=(0, 10), ylim=(0, 10))
d, = ax.plot([dot.x for dot in dots],[dot.y for dot in dots], 'ro', markersize=3)`
so, dot is the name of my class of objects et dots is the list that contains N objects. Every dot is plotted in red.
What I want to do is to plot, for example, N-1 dots in red and one dot in blue, is it possible with the command ax.plot ?
Thanks for your help
Yes, it is possible. You will need to segregate the points into two collections; there are a number of ways to do this; here I chose to extract one point from the list. then you must plot each collections separately on the same canvas.
import random
import matplotlib.pyplot as plt
class Dot(object):
def __init__(self, x, y):
self.x = x
self.y = y
def get_random_dot(dots):
random.shuffle(dots)
return dots.pop()
num_dots = 10
dots = [Dot(random.random(), random.random()) for _ in range(num_dots)]
fig = plt.figure()
ax = plt.axes()
selected_dot = get_random_dot(dots)
d, = ax.plot([dot.x for dot in dots],[dot.y for dot in dots], 'r.')
f, = ax.plot(selected_dot.x, selected_dot.y, color='blue', marker='o', linewidth=3)
plt.show()

How to enlarge the x, y or z axis of a 3d diagramm in matplotlib?

How can I set the diagramm to enlarge(!) and show all the ticks in one or more of the axis? See picture below
import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.mplot3d import axes3d
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ys = np.linspace(0, 30, 30)
xs = np.linspace(0, 10, 10)
X, Y = np.meshgrid(xs, ys)
ax.plot_wireframe(X, Y, X * Y)
plt.show()
I don't want square axis. I guess that the default plots square axis, i.e. the axis x, y, and z have all the same length. The default looks like the following (without the red arrow!).
I need to "enlarge", "stretch" or "scale" (sorry, I don't know how it is called) the axis. For example, when the x, y axis have 30x30 values, then it is ok to have square axies like the default figuer above or the next first figure. But if they have 30x10 or 10x30 values, I would like to plot them like the next second and third figure respectively:
The suggested plt.figure(figsize=(6,2)) or plt.figure(figsize=(2,6)) has no good results for 3d diagramms. The following is not what I'm looking for: