In this question, they answer how to correctly use grid with imshow with matplotlib. I am trying to do the same as they do, but I want to remove all ticks (x and y). When I try to do it, it also eliminates the grid and I just the image displayed without grid and ticks. My code is:
fig, ax = plt.subplots()
data = np.random.rand(20,20)
ax.imshow(data)
ax.set_xticks(np.arange(20))
ax.set_xticklabels(np.arange(20))
ax.set_xticks(np.arange(20)+0.5, minor=True)
ax.grid(which='minor',color='w',axis='x',linewidth=6)
ax.axes.xaxis.set_visible(False)
ax.axes.yaxis.set_visible(False)
plt.show()
Does anyone how to remove the ticks while keeping the grid (along the x axis in my case)?
Removing the axes (via set_visible(False)) will also remove the grid.
However, there's a workaround setting both spines and tick marks/labels to be invisible individually:
fig, ax = plt.subplots()
data = np.random.rand(20,20)
ax.imshow(data)
ax.set_xticks(np.arange(20))
ax.set_xticklabels(np.arange(20))
ax.set_xticks(np.arange(20)+0.5, minor=True)
ax.grid(which='minor',color='w',axis='x',linewidth=6)
# set axis spines (the box around the plot) to be invisible
plt.setp(ax.spines.values(), alpha = 0)
# set both tick marks and tick labels to size 0
ax.tick_params(which = 'both', size = 0, labelsize = 0)
plt.show()
Gives you output as:
Note, you might need to adjust xlim/ylim and grid parameters to fit your needs.
This is not perfect, but you can just set the tick label as an empty list.
ax.axes.get_xaxis().set_ticks([])
ax.axes.get_yaxis().set_ticks([])
Only the minor xticks, used in the grid, remain.
Related
I created a cumulative histogram. Now I want to draw a line from top of any bin to the y-axis in that histogram and show the value of it like this:
Can you show me the way to do?
Below is my code to draw that histogram:
plt.rcParams['ytick.right'] = plt.rcParams['ytick.labelright'] = True
plt.rcParams['ytick.left'] = plt.rcParams['ytick.labelleft'] = False
plt.figure(figsize=[8, 6])
plt.hist(df['days'], bins=range(0, 50, 1), color="dodgerblue", edgecolor='black'
,cumulative=-1, density=True
,histtype='barstacked')
plt.xlabel('Number of Days')
plt.ylabel('Density')
Thank you so much!
Oneliner:
plt.axhline(y, color='k', linestyle='dashed', linewidth=1)
Use this to add a horizontal line to your histogram.
Place your mean or value of y in place of y in the above code snippet.
Simply drawing a horizontal line rises two problems:
The line will be drawn on top of the bars, from the left to the right. To have it behind the bars, use zorder=0.
The line will still be visible at the far left, as there are no bars there. Changing the x-axis to a "tight" layout with plt.autoscale(enable=True, axis='x', tight=True) solves that.
To add a new tick at the specific y-position, you can take the list of existing ticks, create a list including the new tick and set those as the new ticks.
To change the color of the newly added tick, you first find its index in the list, and then change the color of the tick with that index.
One problem with this approach, is that the new tick might overlap with an existing tick. This could be solved by looping through the list and if an existing tick is nearer than some epsilon to the new tick, remove the existing tick. This is not yet implemented in the code example.
Alternatively, the tick value could be displayed to the left of the axis, on top of the horizontal line. Of course, that would lead to a problem in case there wouldn't be enough place for the text.
You might want to round the value of the special tick to the nearest hundredths to prevent that the other ticks also get displayed with more digits.
I created an example with simulated data:
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
df = pd.DataFrame({"days": np.random.normal(25, 10, 10000)})
plt.rcParams['ytick.right'] = plt.rcParams['ytick.labelright'] = True
plt.rcParams['ytick.left'] = plt.rcParams['ytick.labelleft'] = False
plt.figure(figsize=[8, 6])
bin_heights, _, _ = plt.hist(df['days'], bins=range(0, 50, 1), color="dodgerblue", edgecolor='black',
cumulative=-1, density=True,
histtype='barstacked')
plt.autoscale(enable=True, axis='both', tight=True) # use axis='x' to only set the x axis tight
special_y = bin_heights[15]
# draw a horizontal line, use zorder=0 so it is drawn behind the bars
plt.axhline(special_y, 0, 1, color='red', linestyle='dashed', linewidth=1, zorder=0)
plt.yticks(list(plt.yticks()[0]) + [special_y]) # add a tick in y for special_y
# find the index of special_y in the new ticks (ticks are sorted automatically)
index_special_y = list(plt.yticks()[0]).index(special_y)
plt.gca().get_yticklabels()[index_special_y].set_color('red') # change the color of the special tick
plt.xlabel('Number of Days')
plt.ylabel('Density')
plt.show()
I've come across a number of posts such as this and this talking about how to align tick labels to avoid overlaps. In fact, the second link is basically exactly what I want to do, which is move the first and last tick (on both X and Y axis) into the plot area. Unfortunately, I'm encountering some odd behavior that I'm hoping someone can explain to me.
The code below generates 3 figures (also shown below). The first is a figure with 1 subplot, and everything works as intended. All tick labels are center-justified except the first and last on each axis, which is properly adjusted to be within the plot area.
Figure 2 has 2 subplots vertically stacked. In this plot the horizontal axis has tick labels properly justified, but on the vertical axis all the positive labels (0-10) have been justified "bottom" when only the last label (10) should have been justified "top". All others should be justified "center" still.
Figure 3 is similar to Figure 2, only with horizontally stacked subplots. In this case, it's the tick labels on the positive horizontal axis that are not correctly justified, with all labels justified "left" when only the final label should be justified "right".
Any clue why on a figure with multiple subplots the tick label justification is not being set correctly? I've made multiple versions of these plots, including embedded in a Tkinter window (my actual application) and I get the exact same result every time.
EDIT: I've added a screenshot of the plots from my actual application, which is a GUI made using Tkinter. The overall window size is 1024x768 and the plots are made using 2 figures, one for the top plot (XY) and one with two subplots for the bottom plots (XZ and YZ). This screenshot is without any resizing of the window.
import matplotlib.pyplot as plt
import matplotlib
def stylize_plot(ax=None, fig=None):
if ax is None:
ax = plt.gca()
if fig is None:
fig = plt.gcf()
ax.axis([-10, 10, -10, 10])
ax.grid(True)
fig.set_tight_layout(True)
ax.spines['left'].set_position('zero')
ax.spines['bottom'].set_position('zero')
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
# Appears to be shifting all tick labels on positive horizontal axis for Figure with 2 subplots
xTick_objects = ax.xaxis.get_major_ticks()
xTick_objects[0].label1.set_horizontalalignment('left') # left align first tick
xTick_objects[-1].label1.set_horizontalalignment('right') # right align last tick
yTick_objects = ax.yaxis.get_major_ticks()
yTick_objects[0].label1.set_verticalalignment('bottom') # bottom align first tick
yTick_objects[-1].label1.set_verticalalignment('top') # top align last tick
ax.xaxis.set_major_formatter(matplotlib.ticker.FormatStrFormatter('%0.1f'))
ax.yaxis.set_major_formatter(matplotlib.ticker.FormatStrFormatter('%0.1f'))
if __name__ == '__main__':
fig = plt.figure()
ax = fig.add_subplot(111)
stylize_plot(ax=ax, fig=fig)
fig2 = plt.figure()
ax2 = fig2.add_subplot(211)
ax3 = fig2.add_subplot(212)
stylize_plot(ax=ax2, fig=fig2)
stylize_plot(ax=ax3, fig=fig2)
fig3 = plt.figure()
ax4 = fig3.add_subplot(121)
ax5 = fig3.add_subplot(122)
stylize_plot(ax=ax4, fig=fig3)
stylize_plot(ax=ax5, fig=fig3)
plt.show()
I am plotting 2 shapefiles (converted to geopandas dataframe) using this. But there is too much whitespace. How can I reduce it to fill the box more with the map? The xlim and ylim doesn't seem to have any impact
f, ax = plt.subplots(1, figsize=(8, 8))
polydatx.plot(ax = ax, column = 'Elev_Avg', cmap='OrRd', scheme='quantiles')
segdatx.plot(ax = ax)
ax.grid(False)
ax.set_ylim(47, 47.3)
plt.axis('equal');
The problem lies in calling
plt.axis('equal')
after setting the new ylim.
From the docs:
axis('equal')
changes limits of x or y axis so that equal increments of x and y have the same length; a circle is circular.:
axis('scaled')
achieves the same result by changing the dimensions of the plot box instead of the axis data limits.
In your case I would adjust the figure size to some rectangle, not a square and use axis('scaled').
I am new to matplotlib and trying to create and save plots from pandas dataframes via a loop. Each plot should have an identical x-axis, but different y-axis lengths and labels. I have no problem creating and saving the plots with different y-axis lengths and labels, but when I create the plots, matplotlib rescales the x-axis depending on how much space is needed for the y-axis labels on the left side of the figure.
These figures are for a technical report. I plan to place one on each page of the report and I would like to have all of the x-axes take up the same amount of space on the page.
Here is an MSPaint version of what I'm getting and what I'd like to get.
Hopefully this is enough code to help. I'm sure there are lots of non-optimal parts of this.
import pandas as pd
import matplotlib.pyplot as plt
import pylab as pl
from matplotlib import collections as mc
from matplotlib.lines import Line2D
import seaborn as sns
# elements for x-axis
start = -1600
end = 2001
interval = 200 # x-axis tick interval
xticks = [x for x in range(start, end, interval)] # create x ticks
# items needed for legend construction
lw_bins = [0,10,25,50,75,90,100] # bins for line width
lw_labels = [3,6,9,12,15,18] # line widths
def make_proxy(zvalue, scalar_mappable, **kwargs):
color = 'black'
return Line2D([0, 1], [0, 1], color=color, solid_capstyle='butt', **kwargs)
# generic image ID
img_path = r'C:\\Users\\user\\chart'
img_ID = 0
for line_subset in data:
# create line collection for this run through loop
lc = mc.LineCollection(line_subset)
# create plot and set properties
sns.set(style="ticks")
sns.set_context("notebook")
fig, ax = pl.subplots(figsize=(16, len(line_subset)*0.5)) # I want the height of the figure to change based on number of labels on y-axis
# Figure width should stay the same
ax.add_collection(lc)
ax.set_xlim(left=start, right=end)
ax.set_xticks(xticks)
ax.set_ylim(0, len(line_subset)+1)
ax.margins(0.05)
sns.despine(left=True)
ax.xaxis.set_ticks_position('bottom')
ax.set_yticks(line_subset['order'])
ax.set_yticklabels(line_subset['ylabel'])
ax.tick_params(axis='y', length=0)
# legend
proxies = [make_proxy(item, lc, linewidth=item) for item in lw_labels]
ax.legend(proxies, ['0-10%', '10-25%', '25-50%', '50-75%', '75-90%', '90-100%'], bbox_to_anchor=(1.05, 1.0),
loc=2, ncol=2, labelspacing=1.25, handlelength=4.0, handletextpad=0.5, markerfirst=False,
columnspacing=1.0)
# title
ax.text(0, len(line_subset)+2, s=str(img_ID), fontsize=20)
# save as .png images
plt.savefig(r'C:\\Users\\user\\Desktop\\chart' + str(img_ID) + '.png', dpi=300, bbox_inches='tight')
Unless you use an axes of specifically defined aspect ratio (like in an imshow plot or by calling .set_aspect("equal")), the space taken by the axes should only depend on the figure size along that direction and the spacings set to the figure.
You are therefore pretty much asking for the default behaviour and the only thing that prevents you from obtaining that is that you use bbox_inches='tight' in the savefig command.
bbox_inches='tight' will change the figure size! So don't use it and the axes will remain constant in size. `
Your figure size, defined like figsize=(16, len(line_subset)*0.5) seems to make sense according to what I understand from the question. So what remains is to make sure the axes inside the figure are the size you want them to be. You can do that by manually placing it using fig.add_axes
fig.add_axes([left, bottom, width, height])
where left, bottom, width, height are in figure coordinates ranging from 0 to 1. Or, you can adjust the spacings outside the subplot using subplots_adjust
plt.subplots_adjust(left, bottom, right, top)
To get matching x axis for the subplots (same x axis length for each subplot) , you need to share the x axis between subplots.
See the example here https://matplotlib.org/examples/pylab_examples/shared_axis_demo.html
I'm trying to plot a figure without tickmarks or numbers on either of the axes (I use axes in the traditional sense, not the matplotlib nomenclature!). An issue I have come across is where matplotlib adjusts the x(y)ticklabels by subtracting a value N, then adds N at the end of the axis.
This may be vague, but the following simplified example highlights the issue, with '6.18' being the offending value of N:
import matplotlib.pyplot as plt
import random
prefix = 6.18
rx = [prefix+(0.001*random.random()) for i in arange(100)]
ry = [prefix+(0.001*random.random()) for i in arange(100)]
plt.plot(rx,ry,'ko')
frame1 = plt.gca()
for xlabel_i in frame1.axes.get_xticklabels():
xlabel_i.set_visible(False)
xlabel_i.set_fontsize(0.0)
for xlabel_i in frame1.axes.get_yticklabels():
xlabel_i.set_fontsize(0.0)
xlabel_i.set_visible(False)
for tick in frame1.axes.get_xticklines():
tick.set_visible(False)
for tick in frame1.axes.get_yticklines():
tick.set_visible(False)
plt.show()
The three things I would like to know are:
How to turn off this behaviour in the first place (although in most cases it is useful, it is not always!) I have looked through matplotlib.axis.XAxis and cannot find anything appropriate
How can I make N disappear (i.e. X.set_visible(False))
Is there a better way to do the above anyway? My final plot would be 4x4 subplots in a figure, if that is relevant.
Instead of hiding each element, you can hide the whole axis:
frame1.axes.get_xaxis().set_visible(False)
frame1.axes.get_yaxis().set_visible(False)
Or, you can set the ticks to an empty list:
frame1.axes.get_xaxis().set_ticks([])
frame1.axes.get_yaxis().set_ticks([])
In this second option, you can still use plt.xlabel() and plt.ylabel() to add labels to the axes.
If you want to hide just the axis text keeping the grid lines:
frame1 = plt.gca()
frame1.axes.xaxis.set_ticklabels([])
frame1.axes.yaxis.set_ticklabels([])
Doing set_visible(False) or set_ticks([]) will also hide the grid lines.
If you are like me and don't always retrieve the axes, ax, when plotting the figure, then a simple solution would be to do
plt.xticks([])
plt.yticks([])
I've colour coded this figure to ease the process.
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.add_subplot(111)
You can have full control over the figure using these commands, to complete the answer I've add also the control over the spines:
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
# X AXIS -BORDER
ax.spines['bottom'].set_visible(False)
# BLUE
ax.set_xticklabels([])
# RED
ax.set_xticks([])
# RED AND BLUE TOGETHER
ax.axes.get_xaxis().set_visible(False)
# Y AXIS -BORDER
ax.spines['left'].set_visible(False)
# YELLOW
ax.set_yticklabels([])
# GREEN
ax.set_yticks([])
# YELLOW AND GREEN TOGHETHER
ax.axes.get_yaxis().set_visible(False)
I was not actually able to render an image without borders or axis data based on any of the code snippets here (even the one accepted at the answer). After digging through some API documentation, I landed on this code to render my image
plt.axis('off')
plt.tick_params(axis='both', left=False, top=False, right=False, bottom=False, labelleft=False, labeltop=False, labelright=False, labelbottom=False)
plt.savefig('foo.png', dpi=100, bbox_inches='tight', pad_inches=0.0)
I used the tick_params call to basically shut down any extra information that might be rendered and I have a perfect graph in my output file.
Somewhat of an old thread but, this seems to be a faster method using the latest version of matplotlib:
set the major formatter for the x-axis
ax.xaxis.set_major_formatter(plt.NullFormatter())
One trick could be setting the color of tick labels as white to hide it!
plt.xticks(color='w')
plt.yticks(color='w')
or to be more generalized (#Armin Okić), you can set it as "None".
When using the object oriented API, the Axes object has two useful methods for removing the axis text, set_xticklabels() and set_xticks().
Say you create a plot using
fig, ax = plt.subplots(1)
ax.plot(x, y)
If you simply want to remove the tick labels, you could use
ax.set_xticklabels([])
or to remove the ticks completely, you could use
ax.set_xticks([])
These methods are useful for specifying exactly where you want the ticks and how you want them labeled. Passing an empty list results in no ticks, or no labels, respectively.
You could simply set xlabel to None, straight in your axis. Below an working example using seaborn
from matplotlib import pyplot as plt
import seaborn as sns
tips = sns.load_dataset("tips")
ax = sns.boxplot(x="day", y="total_bill", data=tips)
ax.set(xlabel=None)
plt.show()
Just do this in case you have subplots
fig, axs = plt.subplots(1, 2, figsize=(16, 8))
ax[0].set_yticklabels([]) # x-axis
ax[0].set_xticklabels([]) # y-axis