Plotly - Remove axis ticks and numbers but keep label - python

In this MWE, I have a plot with ticks and labels.
fig = go.Figure(data=go.Scatter(x=[2.3], y=[5.3], mode='markers'))
fig.update_xaxes(range=[0,10], constrain="domain",title_text="Some x label",title_font={"size":22,"color":"black"}, showgrid=False)
fig.update_yaxes(scaleanchor="x",scaleratio = 1, range=[0,10], title_text="Some y label",title_font={"size":22,"color":"black"}, showgrid=False)
fig.show()
If I remove the grid and the ticks, the x label becomes the plot title and the y label stays far away from the plot. How can I just remove the grid and numbers while keeping the axis labels where they are (or even getting them a bit closer to the axis lines as there are no numbers now)?
fig = go.Figure(data=go.Scatter(x=[2.3], y=[5.3], mode='markers'))
fig.update_xaxes(range=[0,10], constrain="domain",showgrid=False,showticklabels=False,rangemode="nonnegative",
title_text="Some x label",title_font={"size":22,"color":"black"})
fig.update_yaxes(scaleanchor="x",scaleratio = 1, range=[0,10], showgrid=False,showticklabels=False,rangemode="nonnegative",
title_text="Some y label",title_font={"size":22,"color":"black"})
fig.show()

Problem only found in recent version of plotly (5.12). For the time being, best solution is downgrade to plotly==5.6.0. pip install plotly==5.6.0

I do the following for my own plots: by adjusting the various margin parameters (t - top, b - bottom, l - left, and r - right) till I get the look I wanted.
something.update_layout(
width=800,
height=400,
font_family="Times",
font_size=10,
margin_t=50,
margin_b=100,
margin_l=280,
margin_r=150,
margin_pad=5)
Hope this helps.

Related

Matplotlib tight_layout set rect tuple

I have created the following code, which prints a plot and formats the axis labels and ticks in a way that is useful to me. I have a problem with tight_layout, which leaves my vertically rotated x-axis tick labels as well as the x-axis label outside the figure window.
To try and solve the problem, what I did was to manually rescale the plot window, and set the rect tuple manually from the figure window. After some tries, I found that the optimal values for (left, bottom, right, top) in my case were [0.163, 0.391, 0.905, 0.977]. Next, I thought I should incorporate that to my code, so that my plots emerge with correct sizing in the first place: To that end, I used the command:
fig.tight_layout(rect=[0.163, 0.391, 0.905, 0.977])
However, it did not work, and the figure emerges with different rect values than the ones I set.
Question 1: How can I set the rect values from my code, rather than setting them manually?
Question 2: Is there a better/easier alternative to achieve the desired functionality?
# dt_objects is a list of datetime objects. My x-axis is timestamps
# for y_axis, set any series. The code will set the y axis based on the min,max value of y-values
matdates=date2num(dt_objects)
x_axis=matdates
fig,ax = plt.subplots()
ax.plot_date(x_axis,y_axis,markersize=8)
ax.axhline(y=y_axis.mean(),linestyle='--',color='red',alpha=0.5)
ax.xaxis.set_major_locator(AutoDateLocator(minticks=1, maxticks=5)) #Set position of Major X ticks
ax.xaxis.set_minor_locator(AutoDateLocator(minticks=10, maxticks=30)) #Set position of Minor X ticks
ax.xaxis.set_major_formatter( DateFormatter('%Y/%m/%d-%H:%M:%S')) #Set format of Major X ticks
ax.xaxis.set_minor_formatter( DateFormatter('%H:%M:%S')) #Set format of X ticks
ax.tick_params(axis='x',which='major',rotation=90,labelsize=14) #Set parameters of Major X ticks
ax.tick_params(axis='x',which='minor',rotation=80,labelsize=12) #Set parameters of Major X ticks
plt.setp(ax.get_xticklabels(), fontsize=14, fontweight="bold") #Set font of Major X ticks
ax.yaxis.set_major_formatter(FormatStrFormatter('%.2f')) #Set format of Major Y ticks
ax.tick_params(axis='y',which='major',labelsize=14)
calculateYpadding=(y_axis.max()-y_axis.min())*0.1 # Padding is 10% of the max difference in y values :)
ax.set_ylim(round(y_axis.min(),2)-calculateYpadding, round(y_axis.max(),2)+calculateYpadding) #Set boundaries of Y axis
ax.yaxis.set_major_locator(MaxNLocator(nbins = 'auto',min_n_ticks = 5))
plt.grid()
ax.set_xlabel("Time",style='italic',fontsize=14)
ax.xaxis.set_label_coords(1.08, -0.1)
ax.set_ylabel(str(MeasurementType),labelpad=10,style='italic', fontsize=14)
#ax.yaxis.set_label_coords(-0.1, 0.5)
#plt.xlabel("Time",horizontalalignment='right', position=(1,60))\
#ax.set_title(str(MeasurementType),fontweight="bold", pad=20,fontsize=20)
rstButton.config(state=tk.NORMAL)
fig.tight_layout(rect=[0.163, 0.391, 0.905, 0.977])
plt.show()
EDIT: Since I was told my question is not clear, I am including two screenshots to better explain the problem. Here is the result of the above-mentioned code. Also, on the bottom left, you can see on the window that top, bottom, left, right have different values than the ones set at rect tuple in my code.
My desired output is this:
It is achieved by manually tweaking the parameters of the figure, until it reaches a point that is satisfactory. It is from here that i extracted the values and placed them in the rect tuple, but it did not work. Hopefully it is clearer now what I want to achieve, and what the problem is.
EDIT 2: Here are the results of the suggested solution
fig,ax = plt.subplots(constrained_layout=True)
As you can see, the labels of both axles are not correctly placed.
Try:
fig, ax = plt.subplots(constrained_layout=True)

Setting font size in matplotlib plot [duplicate]

I have too many ticks on my graph and they are running into each other.
How can I reduce the number of ticks?
For example, I have ticks:
1E-6, 1E-5, 1E-4, ... 1E6, 1E7
And I only want:
1E-5, 1E-3, ... 1E5, 1E7
I've tried playing with the LogLocator, but I haven't been able to figure this out.
Alternatively, if you want to simply set the number of ticks while allowing matplotlib to position them (currently only with MaxNLocator), there is pyplot.locator_params,
pyplot.locator_params(nbins=4)
You can specify specific axis in this method as mentioned below, default is both:
# To specify the number of ticks on both or any single axes
pyplot.locator_params(axis='y', nbins=6)
pyplot.locator_params(axis='x', nbins=10)
To solve the issue of customisation and appearance of the ticks, see the Tick Locators guide on the matplotlib website
ax.xaxis.set_major_locator(plt.MaxNLocator(3))
would set the total number of ticks in the x-axis to 3, and evenly distribute them across the axis.
There is also a nice tutorial about this
If somebody still gets this page in search results:
fig, ax = plt.subplots()
plt.plot(...)
every_nth = 4
for n, label in enumerate(ax.xaxis.get_ticklabels()):
if n % every_nth != 0:
label.set_visible(False)
There's a set_ticks() function for axis objects.
in case somebody still needs it, and since nothing
here really worked for me, i came up with a very
simple way that keeps the appearance of the
generated plot "as is" while fixing the number
of ticks to exactly N:
import numpy as np
import matplotlib.pyplot as plt
f, ax = plt.subplots()
ax.plot(range(100))
ymin, ymax = ax.get_ylim()
ax.set_yticks(np.round(np.linspace(ymin, ymax, N), 2))
The solution #raphael gave is straightforward and quite helpful.
Still, the displayed tick labels will not be values sampled from the original distribution but from the indexes of the array returned by np.linspace(ymin, ymax, N).
To display N values evenly spaced from your original tick labels, use the set_yticklabels() method. Here is a snippet for the y axis, with integer labels:
import numpy as np
import matplotlib.pyplot as plt
ax = plt.gca()
ymin, ymax = ax.get_ylim()
custom_ticks = np.linspace(ymin, ymax, N, dtype=int)
ax.set_yticks(custom_ticks)
ax.set_yticklabels(custom_ticks)
If you need one tick every N=3 ticks :
N = 3 # 1 tick every 3
xticks_pos, xticks_labels = plt.xticks() # get all axis ticks
myticks = [j for i,j in enumerate(xticks_pos) if not i%N] # index of selected ticks
newlabels = [label for i,label in enumerate(xticks_labels) if not i%N]
or with fig,ax = plt.subplots() :
N = 3 # 1 tick every 3
xticks_pos = ax.get_xticks()
xticks_labels = ax.get_xticklabels()
myticks = [j for i,j in enumerate(xticks_pos) if not i%N] # index of selected ticks
newlabels = [label for i,label in enumerate(xticks_labels) if not i%N]
(obviously you can adjust the offset with (i+offset)%N).
Note that you can get uneven ticks if you wish, e.g. myticks = [1, 3, 8].
Then you can use
plt.gca().set_xticks(myticks) # set new X axis ticks
or if you want to replace labels as well
plt.xticks(myticks, newlabels) # set new X axis ticks and labels
Beware that axis limits must be set after the axis ticks.
Finally, you may wish to draw only an arbitrary set of ticks :
mylabels = ['03/2018', '09/2019', '10/2020']
plt.draw() # needed to populate xticks with actual labels
xticks_pos, xticks_labels = plt.xticks() # get all axis ticks
myticks = [i for i,j in enumerate(b) if j.get_text() in mylabels]
plt.xticks(myticks, mylabels)
(assuming mylabels is ordered ; if it is not, then sort myticks and reorder it).
xticks function auto iterates with range function
start_number = 0
end_number = len(data you have)
step_number = how many skips to make from strat to end
rotation = 90 degrees tilt will help with long ticks
plt.xticks(range(start_number,end_number,step_number),rotation=90)
if you want 10 ticks:
for y axis: ax.set_yticks(ax.get_yticks()[::len(ax.get_yticks())//10])
for x axis: ax.set_xticks(ax.get_xticks()[::len(ax.get_xticks())//10])
this simply gets your ticks and chooses every 10th of the list and sets it back to your ticks. you can change the number of ticks as you wish.
When a log scale is used the number of major ticks can be fixed with the following command
import matplotlib.pyplot as plt
....
plt.locator_params(numticks=12)
plt.show()
The value set to numticks determines the number of axis ticks to be displayed.
Credits to #bgamari's post for introducing the locator_params() function, but the nticks parameter throws an error when a log scale is used.

The first plot of my plotly subplot takes up the entire figure (2x4 axis grids)

I am struggling with something and I can't find a reasonable explanation even though I'm certain it's something small that I've overlooked.
While trying to plot a 2x4 subplot in plotly, the first of the 8 graphs is plotted over the entire 8 grids, rather than being in position (1,1) of the subplot. The remaining 7 graphs are plotted as expected, with the first plot partially visible underneith them. This is the code I am using:
#make a new trace (plot) for every sample:
def make_trace(x_list, y_list, samplename):
trace = go.Scatter(
x = x_list,
y = y_list,
name=samplename
)
layout = go.Layout(
title=samplename,
showlegend=True
)
)
return trace, layout
#call the make_trace function for every sample and append to the figure
def make_subplot():
fig = tls.make_subplots(rows=2, cols=4,plot_titles=cols_list)
row_num = [1,1,1,1,2,2,2,2,3,3,3,3]
column_num = [1,2,3,4,1,2,3,4,1,2,3,4]
i = 0
for sample in cols_list[1:]:
trace, layout = make_trace(normalised_df['percentage'],
normalised_df[sample], sample)
fig.append_trace(trace, row_num[i], column_num[i])
i += 1
fig['layout'].update(title='Normalized Mean Coverage', height=600,
width=800,showlegend=False,font=dict(size=14),
xaxis=dict(title="Transcript Length (%)"),
yaxis=dict(title="Normalised Mean Coverage"))
iplot(fig)
#call the function to create the entire figure:
make_subplot()
The output looks like this:
coverage plot
**Last words: The solution seems to lie somewhere in setting the xaxis/yaxis titles... When I change them in 'layout' in the function call rather than calling fig.update() afterwards the subplot works as expected but its just way too small. The problem is that I want to keep make_trace() as a separate function, as I call it later for a combined plot, so I can't change its x/yaxis titles in this function.
You need to set the xaxis and yaxis titles for each subplot individually:
for idx, sample in enumerate(cols_list):
trace, layout = make_trace(normalised_df['percentage'],
normalised_df[sample], sample)
fig.append_trace(trace, row_num[i], column_num[i])
fig['layout']['xaxis{}'.format(idx+1)].update(title='Transcript Length (%)')
fig['layout']['yaxis{}'.format(idx+1)].update(title='Normalised Mean Coverage')
This seems that this was a bug in the plotly library. It has been solved in my case by updating the plotly package to 3.6.0 for R. In Python I do not know if this is also a bug. But if so just do a bug report in github.
Best regards
Sinan
Have you tried setting axis and axis anchors for each of the subplots? Such as this:
t1 = go.Scatter(x=x, y=y1(x), xaxis="x1", yaxis="y1", name="Trace 1")
t2 = go.Scatter(x=x, y=y2(x), xaxis="x2", yaxis="y2", name="Trace 2")
layout = go.Layout(
xaxis1=dict(anchor="y1", domain=[0, 1]),
yaxis1=dict(anchor="x1", domain=[0.25, 0.45]),
xaxis2=dict(anchor="y2", domain=[0.5, 1]),
yaxis2=dict(anchor="x2", domain=[0.8, 1]))

Change distance between boxplots in the same figure in python [duplicate]

I'm drawing the bloxplot shown below using python and matplotlib. Is there any way I can reduce the distance between the two boxplots on the X axis?
This is the code that I'm using to get the figure above:
import matplotlib.pyplot as plt
from matplotlib import rcParams
rcParams['ytick.direction'] = 'out'
rcParams['xtick.direction'] = 'out'
fig = plt.figure()
xlabels = ["CG", "EG"]
ax = fig.add_subplot(111)
ax.boxplot([values_cg, values_eg])
ax.set_xticks(np.arange(len(xlabels))+1)
ax.set_xticklabels(xlabels, rotation=45, ha='right')
fig.subplots_adjust(bottom=0.3)
ylabels = yticks = np.linspace(0, 20, 5)
ax.set_yticks(yticks)
ax.set_yticklabels(ylabels)
ax.tick_params(axis='x', pad=10)
ax.tick_params(axis='y', pad=10)
plt.savefig(os.path.join(output_dir, "output.pdf"))
And this is an example closer to what I'd like to get visually (although I wouldn't mind if the boxplots were even a bit closer to each other):
You can either change the aspect ratio of plot or use the widths kwarg (doc) as such:
ax.boxplot([values_cg, values_eg], widths=1)
to make the boxes wider.
Try changing the aspect ratio using
ax.set_aspect(1.5) # or some other float
The larger then number, the narrower (and taller) the plot should be:
a circle will be stretched such that the height is num times the width. aspect=1 is the same as aspect=’equal’.
http://matplotlib.org/api/axes_api.html#matplotlib.axes.Axes.set_aspect
When your code writes:
ax.set_xticks(np.arange(len(xlabels))+1)
You're putting the first box plot on 0 and the second one on 1 (event though you change the tick labels afterwards), just like in the second, "wanted" example you gave they are set on 1,2,3.
So i think an alternative solution would be to play with the xticks position and the xlim of the plot.
for example using
ax.set_xlim(-1.5,2.5)
would place them closer.
positions : array-like, optional
Sets the positions of the boxes. The ticks and limits are automatically set to match the positions. Defaults to range(1, N+1) where N is the number of boxes to be drawn.
https://matplotlib.org/3.1.1/api/_as_gen/matplotlib.pyplot.boxplot.html
This should do the job!
As #Stevie mentioned, you can use the positions kwarg (doc) to manually set the x-coordinates of the boxes:
ax.boxplot([values_cg, values_eg], positions=[1, 1.3])

reducing number of plot ticks

I have too many ticks on my graph and they are running into each other.
How can I reduce the number of ticks?
For example, I have ticks:
1E-6, 1E-5, 1E-4, ... 1E6, 1E7
And I only want:
1E-5, 1E-3, ... 1E5, 1E7
I've tried playing with the LogLocator, but I haven't been able to figure this out.
Alternatively, if you want to simply set the number of ticks while allowing matplotlib to position them (currently only with MaxNLocator), there is pyplot.locator_params,
pyplot.locator_params(nbins=4)
You can specify specific axis in this method as mentioned below, default is both:
# To specify the number of ticks on both or any single axes
pyplot.locator_params(axis='y', nbins=6)
pyplot.locator_params(axis='x', nbins=10)
To solve the issue of customisation and appearance of the ticks, see the Tick Locators guide on the matplotlib website
ax.xaxis.set_major_locator(plt.MaxNLocator(3))
would set the total number of ticks in the x-axis to 3, and evenly distribute them across the axis.
There is also a nice tutorial about this
If somebody still gets this page in search results:
fig, ax = plt.subplots()
plt.plot(...)
every_nth = 4
for n, label in enumerate(ax.xaxis.get_ticklabels()):
if n % every_nth != 0:
label.set_visible(False)
There's a set_ticks() function for axis objects.
in case somebody still needs it, and since nothing
here really worked for me, i came up with a very
simple way that keeps the appearance of the
generated plot "as is" while fixing the number
of ticks to exactly N:
import numpy as np
import matplotlib.pyplot as plt
f, ax = plt.subplots()
ax.plot(range(100))
ymin, ymax = ax.get_ylim()
ax.set_yticks(np.round(np.linspace(ymin, ymax, N), 2))
The solution #raphael gave is straightforward and quite helpful.
Still, the displayed tick labels will not be values sampled from the original distribution but from the indexes of the array returned by np.linspace(ymin, ymax, N).
To display N values evenly spaced from your original tick labels, use the set_yticklabels() method. Here is a snippet for the y axis, with integer labels:
import numpy as np
import matplotlib.pyplot as plt
ax = plt.gca()
ymin, ymax = ax.get_ylim()
custom_ticks = np.linspace(ymin, ymax, N, dtype=int)
ax.set_yticks(custom_ticks)
ax.set_yticklabels(custom_ticks)
If you need one tick every N=3 ticks :
N = 3 # 1 tick every 3
xticks_pos, xticks_labels = plt.xticks() # get all axis ticks
myticks = [j for i,j in enumerate(xticks_pos) if not i%N] # index of selected ticks
newlabels = [label for i,label in enumerate(xticks_labels) if not i%N]
or with fig,ax = plt.subplots() :
N = 3 # 1 tick every 3
xticks_pos = ax.get_xticks()
xticks_labels = ax.get_xticklabels()
myticks = [j for i,j in enumerate(xticks_pos) if not i%N] # index of selected ticks
newlabels = [label for i,label in enumerate(xticks_labels) if not i%N]
(obviously you can adjust the offset with (i+offset)%N).
Note that you can get uneven ticks if you wish, e.g. myticks = [1, 3, 8].
Then you can use
plt.gca().set_xticks(myticks) # set new X axis ticks
or if you want to replace labels as well
plt.xticks(myticks, newlabels) # set new X axis ticks and labels
Beware that axis limits must be set after the axis ticks.
Finally, you may wish to draw only an arbitrary set of ticks :
mylabels = ['03/2018', '09/2019', '10/2020']
plt.draw() # needed to populate xticks with actual labels
xticks_pos, xticks_labels = plt.xticks() # get all axis ticks
myticks = [i for i,j in enumerate(b) if j.get_text() in mylabels]
plt.xticks(myticks, mylabels)
(assuming mylabels is ordered ; if it is not, then sort myticks and reorder it).
xticks function auto iterates with range function
start_number = 0
end_number = len(data you have)
step_number = how many skips to make from strat to end
rotation = 90 degrees tilt will help with long ticks
plt.xticks(range(start_number,end_number,step_number),rotation=90)
if you want 10 ticks:
for y axis: ax.set_yticks(ax.get_yticks()[::len(ax.get_yticks())//10])
for x axis: ax.set_xticks(ax.get_xticks()[::len(ax.get_xticks())//10])
this simply gets your ticks and chooses every 10th of the list and sets it back to your ticks. you can change the number of ticks as you wish.
When a log scale is used the number of major ticks can be fixed with the following command
import matplotlib.pyplot as plt
....
plt.locator_params(numticks=12)
plt.show()
The value set to numticks determines the number of axis ticks to be displayed.
Credits to #bgamari's post for introducing the locator_params() function, but the nticks parameter throws an error when a log scale is used.

Categories

Resources