My goal is to make a matplotlib barchart that has only the label names on a single axis, then bars, and then the value of those bars at the top like this. However, I want to be able to embed an image like this online and want the background to be transparent. But when I savefig as transparent=True it comes out like this There are these lines in the background that are very distracting and the names of my y labels become transparent as well which I don't want.
I just need help gaining more control over transparency parameters.
fig, ax = plt.subplots(figsize=(8, 12), frameon=False)
# x dummy data
x = prez_counts['presidents']
# y dummy data
y = prez_counts['occurence']
# make facecolor transparent
ax.set_facecolor('w')
ax.patch.set_alpha(0)
# plot bar plot
ax.barh(x,y,color=sns.color_palette("gnuplot_r", len(x)), alpha=0.7)
# remove xaxis ticks
ax.get_xaxis().set_visible(False)
# add x values of y to end of bars
for i, v in enumerate(y):
ax.text(v + .5, i - .20, str(v), fontsize=(8))
sns.despine(left=True, bottom=True, right=True, top=True)
plt.tight_layout()
plt.savefig('../3_output/test.png', transparent=True)
I would like the output to be the same plot, just without the lines in the background and without clear/translucent y labels.
Related
I am making a scatter plot in matplotlib with logarithmic color scaling, which is working fine, see attached plot. My problem is, I would like to have the x-tick labels on the r.h.s. of the color bar to be in float format, rather than scientific notation. Interestingly, this works only for some of the labels.
I have my data x, y for the scatter plot and the weights which specify the colors.
This is my code:
fig = plt.figure(dpi=200)
# Plot data, this is the relevant part:
sc = plt.scatter(x, y, c=weights, cmap='rainbow', s=20, alpha=0.5,
norm=mpl.colors.LogNorm(vmin=0.3, vmax=3))
cbar = fig.colorbar(sc, format='%.1f', label='$T_{IDL} / T_{Py}$') # format arg. supposed to do what I want
print(cbar.get_ticks()) # For debugging
# Plot dashed line:
xmin, xmax = plt.gca().get_xlim()
const = np.linspace(xmin, xmax, 500)
plt.plot(const, const, linestyle='--', color='black', alpha=0.3)
# Add titles and axis labels:
fig.suptitle(suptitle)
plt.title(f'{len(amp_py_cmn)} Common Flares -- Duration Ratio: Mean {np.mean(dur_ratio):.2f},'
f' St.Dev. {np.std(dur_ratio):.2f}',
fontsize=7)
plt.xlabel('$A_{Py}$')
plt.ylabel('$A_{IDL}$')
# Save figure and show:
fig.savefig(f'{savePath}/{suptitle}.pdf')
plt.show()
This is the resulting Plot:
Scatter Plot
I added the call of cbar.get_ticks() while debugging, and interestingly the output gives
[1.]
which corresponds to the only label that looks according to my wishes. So the question is, where do the other labels come from, and how can I format them?
Thanks!
I have written my code to create a scatter plot with a color bar on the right. But the color bar does not look right, in the sense that the color is too light to be mapped to the actual color used in the plot. I am not sure what is missing or wrong here. But I am hoping to get something similar to what's shown here: https://medium.com/#juliansteam/what-bert-topic-modelling-reveal-about-the-2021-unrest-in-south-africa-d0d15629a9b4 (about in the middle of the page)
df = .... # data loading
df["topic"] = topics
# Plot parameters
top_n = topn
fontsize = 15
# some data preparation
to_plot = df.copy()
to_plot[df.topic >= top_n] = -1
outliers = to_plot.loc[to_plot.topic == -1]
non_outliers = to_plot.loc[to_plot.topic != -1]
#the actual plot
fig, ax = plt.subplots(figsize=(15, 15))
scatter_outliers = ax.scatter(outliers['x'], outliers['y'], color="#E0E0E0", s=1, alpha=.3)
scatter = ax.scatter(non_outliers['x'], non_outliers['y'], c=non_outliers['topic'], s=1, alpha=.3, cmap='hsv_r')
ax.text(0.99, 0.01, f"BERTopic - Top {top_n} topics", transform=ax.transAxes, horizontalalignment="right", color="black")
plt.xticks([], [])
plt.yticks([], [])
plt.colorbar(scatter)
plt.savefig(outfile+"_1.png", format='png', dpi=300)
plt.clf()
plt.close()
As you can see, an example plot looks like this. The color bar is created, but compared to that shown in the link above, the color is very light and does not seem to map to those on the scatter plot. Any suggestions?
The colorbar uses the given alpha=.3. In the scatterplot, many dots with the same color are superimposed, causing them to look brighter than a single dot.
One way to tackle this, is to create a ScalarMappable object to be used by the colorbar, taking the colormap and the norm of the scatter plot (but not its alpha). Note that simply changing the alpha of the scatter object (scatter.set_alpha(1)) would also change the plot itself.
import matplotlib.pyplot as plt
from matplotlib.cm import ScalarMappable
import numpy as np
x = np.random.normal(np.repeat(np.random.uniform(0, 20, 10), 1000))
y = np.random.normal(np.repeat(np.random.uniform(0, 10, 10), 1000))
c = np.repeat(np.arange(10), 1000)
scatter = plt.scatter(x, y, c=c, cmap='hsv_r', alpha=.3, s=3)
plt.colorbar(ScalarMappable(cmap=scatter.get_cmap(), norm=scatter.norm))
plt.tight_layout()
plt.show()
Basically, I found these instructions on how to add a custom line or point on a colorbar, but nothing happens with my code.
I tried changing the zorder, or switching to the pyplot style, but still couldn't get it to work.
fig, ax = plt.subplots()
im = ax.imshow(data, cmap='rainbow')
cbar = ax.figure.colorbar(im)
cbar.outline.set_visible(False)
cbar.ax.set_ylabel("α", rotation=0, va="bottom")
# None of them does anything
cbar.ax.plot(0.5, 0.5, 'k.', label="Ref")
cbar.ax.plot(0.5, 3, 'k.', label="Ref")
cbar.ax.plot([0, 1], [3, 3], 'k', label="Ref")
The figure so far looks like this
I am using matplotlib 3.1.1
Matplotlib will set the x and y limits of the colorbar to the same values. So if the y limits of your color bar are 1.8 and 7.2, the x limits are as well. You will need to plot within these limits to see your markers.
Using axhline might be an easier option instead of plotting a line with plot. To label this marked value, you can add it to the colorbar ticks and ticklabels.
# clim = cbar.ax.get_ylim() # find axis limits
# cbar.ax.plot(mean(clim), 3, 'k.') # point at 3 in the middle of x axis
cbar.ax.axhline(y=3, c='w') # line at 3
# Set label
original_ticks = list(cbar.get_ticks())
cbar.set_ticks(original_ticks + [3])
cbar.set_ticklabels(original_ticks + ['Ref'])
You probably want annotate instead of plot in this case. The label you specified in plot call won't show up until a legend() call, and even in that case the legend will not show up where you want them to be.
fig, ax = plt.subplots()
im = ax.imshow(np.random.normal(size=100).reshape(10, 10), cmap='rainbow')
cbar = ax.figure.colorbar(im)
cbar.outline.set_visible(False)
cbar.ax.set_ylabel("α", rotation=0, va="bottom")
cbar.ax.annotate('Ref', (0, 0))
Check the x axis of the figure below. How can I move the labels a bit to the left so that they align with their respective ticks?
I'm rotating the labels using:
ax.set_xticks(xlabels_positions)
ax.set_xticklabels(xlabels, rotation=45)
But, as you can see, the rotation is centered on the middle of the text labels. Which makes it look like they are shifted to the right.
I've tried using this instead:
ax.set_xticklabels(xlabels, rotation=45, rotation_mode="anchor")
... but it doesn't do what I wished for. And "anchor" seems to be the only value allowed for the rotation_mode parameter.
You can set the horizontal alignment of ticklabels, see the example below. If you imagine a rectangular box around the rotated label, which side of the rectangle do you want to be aligned with the tickpoint?
Given your description, you want: ha='right'
n=5
x = np.arange(n)
y = np.sin(np.linspace(-3,3,n))
xlabels = ['Ticklabel %i' % i for i in range(n)]
fig, axs = plt.subplots(1,3, figsize=(12,3))
ha = ['right', 'center', 'left']
for n, ax in enumerate(axs):
ax.plot(x,y, 'o-')
ax.set_title(ha[n])
ax.set_xticks(x)
ax.set_xticklabels(xlabels, rotation=40, ha=ha[n])
ha='right' is not enough to visually align labels with ticks:
For rotation=45, use both ha='right' and rotation_mode='anchor'
For other angles, use a ScaledTranslation() instead
rotation_mode='anchor'
If the rotation angle is roughly 45°, combine ha='right' with rotation_mode='anchor':
ax.set_xticks(ticks)
ax.set_xticklabels(labels, rotation=45, ha='right', rotation_mode='anchor')
Or in matplotlib 3.5.0+, set ticks and labels at once:
ax.set_xticks(ticks, labels, rotation=45, ha='right', rotation_mode='anchor')
ScaledTranslation()
If the rotation angle is more extreme (e.g., 70°) or you just want more fine-grained control, anchoring won't work well. Instead, apply a linear transform:
ax.set_xticks(ticks)
ax.set_xticklabels(labels, rotation=70)
# create -5pt offset in x direction
from matplotlib.transforms import ScaledTranslation
dx, dy = -5, 0
offset = ScaledTranslation(dx / fig.dpi, dy / fig.dpi, fig.dpi_scale_trans)
# apply offset to all xticklabels
for label in ax.xaxis.get_majorticklabels():
label.set_transform(label.get_transform() + offset)
Rotating the labels is certainly possible. Note though that doing so reduces the readability of the text. One alternative is to alternate label positions using a code like this:
import numpy as np
n=5
x = np.arange(n)
y = np.sin(np.linspace(-3,3,n))
xlabels = ['Long ticklabel %i' % i for i in range(n)]
fig, ax = plt.subplots()
ax.plot(x,y, 'o-')
ax.set_xticks(x)
labels = ax.set_xticklabels(xlabels)
for i, label in enumerate(labels):
label.set_y(label.get_position()[1] - (i % 2) * 0.075)
For more background and alternatives, see this post on my blog
An easy, loop-free alternative is to use the horizontalalignment Text property as a keyword argument to xticks[1]. In the below, at the commented line, I've forced the xticks alignment to be "right".
n=5
x = np.arange(n)
y = np.sin(np.linspace(-3,3,n))
xlabels = ['Long ticklabel %i' % i for i in range(n)]
fig, ax = plt.subplots()
ax.plot(x,y, 'o-')
plt.xticks(
[0,1,2,3,4],
["this label extends way past the figure's left boundary",
"bad motorfinger", "green", "in the age of octopus diplomacy", "x"],
rotation=45,
horizontalalignment="right") # here
plt.show()
(yticks already aligns the right edge with the tick by default, but for xticks the default appears to be "center".)
[1] You find that described in the xticks documentation if you search for the phrase "Text properties".
I am clearly late but there is an official example which uses
plt.setp(ax.get_xticklabels(), rotation=45, ha="right", rotation_mode="anchor")
to rotate the labels while keeping them correctly aligned with the ticks, which is both clean and easy.
See: https://matplotlib.org/stable/gallery/images_contours_and_fields/image_annotated_heatmap.html
I have the following graph:
I'd like to add custom ticks with labels on the right hand side of the graph, to identify the dashed horizontal lines. How can I do that?
ax = gca()
ax.axhline(.5, linestyle='--')
trans = matplotlib.transforms.blended_transform_factory(
ax.transAxes,
ax.transData)
ax.annotate('label', xy=(1.01, .5), xycoords=trans, clip_on=False, va='center')
ax.set_xlim([0,2])
plt.draw()
See here for details on blended transforms. The x coordinate in is axis units (so it will always be just a tad off to the right, and the y-coordinate is is data units so you can put it exactly where you want. There isn't much point in putting in ticks on the right because you dashed lines will cover them up.
If you want a new scale, use twinx().
fig = plt.figure()
ax = []
ax.append(fig.add_subplot(111))
ax.append(ax[0].twinx())
ax[0].plot(...)
ax[1].set_yticks([...])
ax[1].set_yticklabels([...])
plt.show()
If you want just a label, use a text thingy, as #tcaswell wrote.