The code below draws a plot that looks almost exactly the way I want it to be. However, I'd like the ylabel to be horizontal and left-aligned with the yticks. Currently, the ylabel is placed left relative to the yticks which looks ugly (the image below shows the upper left corner of the plot). Does someone know how to fix this?
import matplotlib.pyplot as plt
import numpy as np
xvals = range(0,10);
yvals = lambda s: [ x*x*s for x in xvals ]
# setting the x=... option does NOT help
yprops = dict(rotation=0, y=1.05, horizontalalignment='left')
plt.subplot(111,axisbg='#BBBBBB',alpha=0.1)
plt.grid(color='white', alpha=0.5, linewidth=2, linestyle='-', axis='y')
for spine_name in ['top', 'left', 'right']:
plt.gca().spines[spine_name].set_color('none')
plt.ylabel('y label', **yprops)
plt.xlabel('x label')
plt.gca().tick_params(direction='out', length=0, color='k')
plt.plot(xvals, yvals(1), 'bo-', linewidth=2)
plt.gca().set_axisbelow(True)
plt.show()
You can adjust the coordinates using ax.yaxis.set_label_coords like in this example.
With your data:
import matplotlib.pyplot as plt
import numpy as np
xvals = range(0,10);
yvals = lambda s: [ x*x*s for x in xvals ]
yprops = dict(rotation=0, x=0, y=1.05)
fig, ax = plt.subplots(1, 1, figsize=(5,3))
ax.set_ylabel('y label', **yprops )
ax.set_xlabel('x label')
ax.plot(xvals, yvals(1), 'bo-', linewidth=2)
print(ax.get_position())
ax.yaxis.set_label_coords(-0.1,1.05)
fig.savefig('cucu.png')
plt.show()
Note that if you go further away, the label will be placed outside the figure. If that is the case, you can adjust the margins before:
fig, ax = plt.subplots(1, 1, figsize=(5,3))
ax.set_ylabel('y label', **yprops )
ax.set_xlabel('x label')
ax.plot(xvals, yvals(1), 'bo-', linewidth=2)
fig.subplots_adjust(left=0.2, bottom=0.2, right=0.8, top=0.8)
ax.yaxis.set_label_coords(-0.2,1.2)
fig.savefig('cucu2.png')
plt.show()
See also this answer
Related
I am having diffculties to move the text "Rank" exactly one line above the first label and by not using guesswork as I have different chart types with variable sizes, widths and also paddings between the labels and bars.
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
from pylab import rcParams
rcParams['figure.figsize'] = 8, 6
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
df = pd.DataFrame.from_records(zip(np.arange(1,30)))
df.plot.barh(width=0.8,ax=ax,legend=False)
ax.spines['right'].set_visible(False)
ax.spines['top'].set_visible(False)
ax.spines['left'].set_visible(False)
ax.spines['bottom'].set_visible(False)
ax.tick_params(left=False, bottom=False)
ax.tick_params(axis='y', which='major', pad=36)
ax.set_title("Rankings")
ax.text(-5,30,"Rank")
plt.show()
Using transData.transform didn't get me any further. The problem seems to be that ax.text() with the position params of (0,0) aligns with the start of the bars and not the yticklabels which I need, so getting the exact position of yticklabels relative to the axis would be helpful.
The following approach creates an offset_copy transform, using "axes coordinates". The top left corner of the main plot is at position 0, 1 in axes coordinates. The ticks have a "pad" (between label and tick mark) and a "padding" (length of the tick mark), both measured in "points".
The text can be right aligned, just as the ticks. With "bottom" as vertical alignment, it will be just above the main plot. If that distance is too low, you could try ax.text(0, 1.01, ...) to have it a bit higher.
import matplotlib.pyplot as plt
from matplotlib.transforms import offset_copy
import pandas as pd
import numpy as np
from matplotlib import rcParams
rcParams['figure.figsize'] = 8, 6
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
df = pd.DataFrame.from_records(zip(np.arange(1, 30)))
df.plot.barh(width=0.8, ax=ax, legend=False)
ax.spines['right'].set_visible(False)
ax.spines['top'].set_visible(False)
ax.spines['left'].set_visible(False)
ax.spines['bottom'].set_visible(False)
ax.tick_params(left=False, bottom=False)
ax.tick_params(axis='y', which='major', pad=36)
ax.set_title("Rankings")
tick = ax.yaxis.get_major_ticks()[-1] # get information of one of the ticks
padding = tick.get_pad() + tick.get_tick_padding()
trans_offset = offset_copy(ax.transAxes, fig=fig, x=-padding, y=0, units='points')
ax.text(0, 1, "Rank", ha='right', va='bottom', transform=trans_offset)
# optionally also use tick.label.get_fontproperties()
plt.tight_layout()
plt.show()
I've answered my own question while Johan was had posted his one - which is pretty good and what I wanted. However, I post mine anyways as it uses an entirely different approach. Here I add a "ghost" row into the dataframe and label it appropriately which solves the problem:
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
from pylab import rcParams
rcParams['figure.figsize'] = 8, 6
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
df = pd.DataFrame.from_records(zip(np.arange(1,30)),columns=["val"])
#add a temporary header
new_row = pd.DataFrame({"val":0}, index=[0])
df = pd.concat([df[:],new_row]).reset_index(drop = True)
df.plot.barh(width=0.8,ax=ax,legend=False)
ax.spines['right'].set_visible(False)
ax.spines['top'].set_visible(False)
ax.spines['left'].set_visible(False)
ax.spines['bottom'].set_visible(False)
ax.tick_params(left=False, bottom=False)
ax.tick_params(axis='y', which='major', pad=36)
ax.set_title("Rankings")
# Set the top label to "Rank"
yticklabels = [t for t in ax.get_yticklabels()]
yticklabels[-1]="Rank"
# Left align all labels
[t.set_ha("left") for t in ax.get_yticklabels()]
ax.set_yticklabels(yticklabels)
# delete the top bar effectively by setting it's height to 0
ax.patches[-1].set_height(0)
plt.show()
Perhaps the advantage is that it is always a constant distance above the top label, but with the disadvantage that this is a bit "patchy" in the most literal sense to transform your dataframe for this task.
I would like to have a single legend that nicely fits on top of both the subplots (doesn't necessarily need to span the entire width of the plots, but needs to be outside the plot). I know you can work with bbox_to_anchor() but somehow this doesn't seem to work nicely. It always moves one subplot away.
fig, ax = plt.subplots(1, 2)
sns.barplot(x="day", y="total_bill", hue="sex", data=tips, ax = ax[0])
ax[0].legend_.remove()
sns.barplot(x="day", y="total_bill", hue="sex", data=tips, ax = ax[1])
sns.move_legend(ax[1], loc = "center", bbox_to_anchor=(-0.5, 1.1), ncol=2, title=None, frameon=False)
fig.tight_layout()
There are a couple of ways that I would approach closing the gap.
1: Use a sns.catplot:
This potentially requires doubling your data, though if you're plotting different variables in each subplot you may be able to melt your data
import pandas as pd
import seaborn as sns
# Load the dataset twice
tips_a = sns.load_dataset("tips")
tips_b = sns.load_dataset("tips")
# Add a dummy facet variable
tips_a["col"] = "A"
tips_b["col"] = "B"
# Concat them
tips = pd.concat([tips_a, tips_b])
# Use the dummy variable for the `col` param
g = sns.catplot(x="day", y="total_bill", hue="sex", data=tips, kind="bar", col="col")
# Remove the titles and move the legend
g.set_titles("")
sns.move_legend(g, loc="upper center", ncol=2, title=None, frameon=False)
2: autoscale the axes
This still requires a little bit of bbox_to_anchor fiddling and you probably want to change the right y-axis label (and ticks/ticklabels).
import matplotlib.pyplot as plt
import seaborn as sns
fig, ax = plt.subplots(1, 2, figsize=(7, 4))
sns.barplot(x="day", y="total_bill", hue="sex", data=tips, ax=ax[0])
ax[0].legend_.remove()
sns.barplot(x="day", y="total_bill", hue="sex", data=tips, ax=ax[1])
sns.move_legend(
ax[1],
loc="upper center",
bbox_to_anchor=(-0.1, 1.1),
ncol=2,
title=None,
frameon=False,
)
ax[0].autoscale()
ax[1].autoscale()
I have a 2 line title and first line has a number at the end of the line.
Can we plot a circle around the number?
Here is the code to generate the figure.
from matplotlib import rcParams
from matplotlib import pyplot as plt
import numpy as np
import os
rcParams.update({'figure.autolayout': True})
some_text = 'XXX'
any_number=15
title = '%s: %d\n YYY ZZZZ WWWWW' % (some_text,any_number)
fig = plt.figure(figsize=(8, 8), dpi=100)
plt.tick_params(axis='y', which='major', labelsize=60, width=3, length=10, pad=40)
plt.tick_params(axis='y', which='minor', labelsize=60, width=3, length=10, pad=40)
ax = plt.gca()
plt.title(title, fontsize=60, pad=40, loc='center', fontweight='semibold')
plt.style.use('ggplot')
ax.set_facecolor('white')
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
ax.spines['bottom'].set_visible(False)
ax.spines['left'].set_visible(True)
for edge_i in ['left']:
ax.spines[edge_i].set_edgecolor("black")
ax.spines[edge_i].set_linewidth(3)
ax.spines[edge_i].set_bounds(0, 1)
x = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
plt.yticks(np.arange(0, 1.01, step=0.2))
data_list= np.array([1,1,1,1,1,0.9, 0.8, 0.7, 0.8,0.85])
plt.bar(x, data_list, 0.9, color='indianred',edgecolor="black", linewidth=3,zorder=1)
plt.tick_params(
axis='x', # changes apply to the x-axis
which='both', # both major and minor ticks are affected
bottom=False, # ticks along the bottom edge are off
top=False, # ticks along the top edge are off
labelbottom=False) # labels along the bottom edge are off
figure_name = 'figure_with_circle.png'
figure_file = os.path.join('/Users/burcakotlu/Desktop',figure_name)
fig.savefig(figure_file, dpi=100, bbox_inches="tight")
plt.close(fig)
Here is the current figure and the wanted circle.
One could use the following without ax.bar():
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
ax.set_title('title')
circle1 = plt.Circle((2,4.15), 0.2, color='k', clip_on=False, zorder=100, fill=False)
ax.add_patch(circle1)
ax.set_xlim(0,4)
ax.set_ylim(0,4)
plt.show()
I have found a way to plot circle together with bar plots without distorting bars. Here is the code below:
from matplotlib import rcParams
from matplotlib import pyplot as plt
import numpy as np
import os
import matplotlib.patches as patches
from matplotlib.offsetbox import AnchoredText
rcParams.update({'figure.autolayout': True})
some_text = 'XXX'
any_number=15
title = '%s: %d\n YYY ZZZZ WWWWW' % (some_text,any_number)
fig = plt.figure(figsize=(12,12), dpi=100)
plt.tick_params(axis='y', which='major', labelsize=60, width=3, length=10, pad=40)
plt.tick_params(axis='y', which='minor', labelsize=60, width=3, length=10, pad=40)
ax = plt.gca()
number_of_xxx = '12'
anchored_text_number_of_xxx = AnchoredText(number_of_xxx,
frameon=False, borderpad=0, pad=0.1,
loc='upper right',
bbox_to_anchor=[0.95, 1.3],
bbox_transform=plt.gca().transAxes,
prop={'fontsize': 60,
'fontweight': 'semibold'})
ax.add_artist(anchored_text_number_of_xxx)
circle1 = patches.Circle((0.88, 1.25), radius=0.1, transform=ax.transAxes, zorder=100, fill=False, color='gold', lw=8, clip_on=False)
ax.add_patch(circle1)
ax.set_title(title, fontsize=60, pad=40, loc='center', fontweight='semibold', zorder=50)
ax.set_facecolor('white')
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
ax.spines['bottom'].set_visible(False)
ax.spines['left'].set_visible(True)
for edge_i in ['left']:
ax.spines[edge_i].set_edgecolor("black")
ax.spines[edge_i].set_linewidth(3)
ax.spines[edge_i].set_bounds(0, 1)
x = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
ax.set_yticks(np.arange(0, 1.01, step=0.2))
data_list= np.array([1,1,1,1,1,0.9, 0.8, 0.7, 0.8,0.85])
ax.bar(x, data_list, 0.9, color='indianred',edgecolor="black", linewidth=3,zorder=1)
ax.tick_params(
axis='x', # changes apply to the x-axis
which='both', # both major and minor ticks are affected
bottom=False, # ticks along the bottom edge are off
top=False, # ticks along the top edge are off
labelbottom=False) # labels along the bottom edge are off
figure_name = 'figure_with_circle.png'
figure_file = os.path.join('/Users/burcakotlu/Desktop',figure_name)
fig.savefig(figure_file, dpi=100, bbox_inches="tight")
plt.close(fig)
def visualize(goal_x, goal_y, goal_z, epoch_arr):
# %% Create Color Map
colormap = plt.get_cmap("binary")
norm = matplotlib.colors.Normalize(vmin=min(epoch_arr), vmax=max(epoch_arr))
# %% 3D Plot
fig = plt.figure()
ax3D = fig.add_subplot(111, projection='3d')
ax3D.set_facecolor('xkcd:salmon')
ax3D.scatter(goal_x, goal_y, goal_z, s=100, c=colormap(norm(epoch_arr.values)), marker='o')
plt.show()
The above code produces the following picture:
However, as you can see there is a point in the right side that is clearly still not 100% opaque. You can see the grid lines through the point. How do I make the scatter plot points 100% opaque, no transparency?
Some tricks will help. Here I plot all the markers in white first, then plot again on top using the intended color.
import matplotlib
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
# make-up some data
goal_x = list(range(10))
goal_y = list(range(10))
goal_z = list(range(10))
epoch_arr = np.linspace(0,1,10)
fig = plt.figure(figsize=(8,8))
ax3D = fig.add_subplot(111, projection='3d')
ax3D.set_facecolor('xkcd:salmon')
# First plot: all markers are in white color
ax3D.scatter(goal_x, goal_y, goal_z, s=500, c='w', marker='o', alpha=1.0, zorder=10)
colormap = plt.get_cmap("binary")
norm = matplotlib.colors.Normalize(vmin=min(epoch_arr), vmax=max(epoch_arr))
#ax3D.scatter(goal_x, goal_y, goal_z, s=100, c=colormap(norm(epoch_arr.values)), marker='o')
# Second plot: use intended colormap
ax3D.scatter(goal_x, goal_y, goal_z, s=500, c='b', marker='o', zorder=11)
plt.show()
The resulting plot:
Setting alpha=1 should be enough.
ax3D.scatter(..., alpha=1)
Alternatively set depthshade=False
ax3D.scatter(..., depthshade=False)
The result will be the same in both cases.
I am a newbie in drawing plots. I have written the following code with matplotlib in python to build a scatterplot:
import numpy as np
import matplotlib.pyplot as plt
Edge_matrix=[[269, 270], [270, 269], [274, 275], [275, 274], [341, 342],
[342, 341], [711, 712], [712, 711]]
x=[]; y=[]
for i in Edge_matrix:
x.append(i[0])
y.append(i[1])
#print(x,y)
plt.scatter(x,y, s=1, c='blue', alpha=1)
plt.axhline(576, linewidth=0.3, color='blue', label='zorder=2', zorder=2)
plt.axvline(576, linewidth=0.3, color='blue', label='zorder=2', zorder=2)
plt.show()
I want the x1 and y1 axes start from 0 to 821 and make new x2 axis starting from 1 to 577 to the vertical line and after passing vertical line, again starting from 1 to 243; I need a new y2 axis exactly like x2. Is there any way to change my code for getting my favorite plot?
This is the plot after running the code:
The plot I would like to have is the following:
You may use twin axes to produce another axes for which you may set different ticks and ticklabels.
import numpy as np
import matplotlib.pyplot as plt
Edge_matrix=[[269, 270], [270, 269], [274, 275], [275, 274], [341, 342],
[342, 341], [711, 712], [712, 711]]
x,y = zip(*Edge_matrix)
limits = [0,821]
sec_lim = 243
bp = 576
ax = plt.gca()
ax.set_xlim(limits)
ax.set_ylim(limits)
ax.scatter(x,y, s=1, c='blue', alpha=1)
ax.axhline(bp, linewidth=0.3, color='blue', label='zorder=2', zorder=2)
ax.axvline(bp, linewidth=0.3, color='blue', label='zorder=2', zorder=2)
ax2 = ax.twinx()
ax2.yaxis.tick_right()
ax2.set_ylim(ax.get_ylim())
yticks = ax.get_yticks()
ax2.set_yticks(np.append(yticks[yticks<bp], [bp,bp+sec_lim]) )
ax2.set_yticklabels(np.append(yticks[yticks<bp], [0,sec_lim]).astype(int) )
ax3 = ax.twiny()
ax3.xaxis.tick_top()
ax3.set_xlim(ax.get_xlim())
xticks = ax.get_xticks()
ax3.set_xticks(np.append(xticks[xticks<bp], [bp,bp+sec_lim]) )
ax3.set_xticklabels(np.append(xticks[xticks<bp], [0,sec_lim]).astype(int) )
plt.show()