How to avoid congestion of ticks in y axis? - python

The figure attached here has too many ticks in y axis and is congested.I dont want to change the y axis to any other scale.I want to hide the every second number in y axis.Is this possible?,which means [5,15,25.....] will be hidden.
Is there any other approach to avoid congestion in y axis?
pl.figure(figsize=(10, 8))
pl.scatter(x=T1['Current_Sim_rcs_obj1'], y=T1['Final Mean_Range'])
pl.xlabel('Truth RCS [dBsm]')
pl.xlim(-40, 5)
pl.ylim(0, 280)
pl.grid()
pl.ylabel('RUT_Range[m]')
pl.xticks(np.arange(-45, 15, 5))
pl.yticks(np.arange(0, 281, 10))
pl.show()
I tried to add the below line to the above code,which didn't worked as expected.
pl.axes().yaxis.set_minor_locator(MultipleLocator(5))

You can use matplotlib's yticks() function to get all the ticks (locations) and labels. Then you can modify the two lists based on some criteria. For the criteria you give, we can ignore every second tick and label:
from matplotlib import pyplot as plt
plt.figure() # Create a figure
locs, labels = plt.yticks() # Get the current locations and labels
locs = locs[0::2] # Choose every other location
labels = labels[0::2] # Choose every other label
plt.yticks(locs, labels) # Set new yticks and labels
I could imagine other cases where where the criteria are based on the length of locs. In fact, that's probably what matplotlib is doing behind the scenes already. It has some heuristic which is trying to choose a good length for locs based on the size of the plot and the scale of the data.

Related

How to add yticks to one specific point in pyplot?

I have bunch of numbers (lets say all less than 500) and some 'inf' in my data. I want to plot them using integer numbers (I represent 'inf' with 1000). However, on y axis of plot, I want to write 'inf' instead of 1000. If I use 'plt.yticks', I can add label to all y points which is not what I want. How can I add a label to one specific point?
You can override the both the data position of the ticks and the labels of the ticks. Here is an example of a scatter plot with 3 extra points at "infinity". It does not look great because the extra points are at 1000, and there are ticks showing in the white space.
from matplotlib import pyplot as plt
import numpy as np
# create some data, plot it.
x = np.random.random(size=300)
y = np.random.randint(0,500, 300)
x_inf = [0,.1,.2]
y_inf = [1000,1000,1000]
plt.scatter(x,y)
plt.scatter(x_inf, y_inf)
First grab the axis. Then we can overwrite what data positions should have ticks, in this case 0 to 500 in steps of 100, and then 1000. Then we can also overwrite the labels of the ticks themselves.
ax = plt.gca()
# set the positions where we want ticks to appear
ax.yaxis.set_ticks([0,100,200,300,400,500,1000])
# set what will actually be displayed at each tick.
ax.yaxis.set_ticklabels([0,100,200,300,400,500,'inf'])
Check this out:
plt.plot(np.arange(5), np.arange(5))
plt.yticks(np.arange(5), [200, 400, 600, 800, 'inf'])
plt.show()

How to remove a particular grid line corresponding to a custom xtick on a log scale axis?

I'd like to remove the vertical grid line corresponding to the custom xtick (displayed at x = 71 in the below picture). I could remove a horizontal grid line corresponding to the ytick 701 in the below picture by using a hack : since I have no minor tick on the y axis, I defined the custom ytick corresponding to the line that points toward the maximum and crosses the y axis as a minor tick, and then I disabled grid lines for minor ticks on the y axis. Unfortunately I cannot use the same hack on the x axis without disabling the grid lines of the minor ticks and that's something I'd like to avoid at all costs.
Below is a not so minimal albeit still WE.
There are many things I don't understand, the 2 majors are why does
locs, labels = plt.xticks()
not return the locs and labels that are plotted and why I don't get xticks labels displayed as 10^x where x = 0, 1, 2 and 3 but that's outside the scope of the original question.
import matplotlib.pyplot as plt
plt.grid(True)
import numpy as np
# Generate data
x_data = np.arange(1, 1000 , 10)
y_data = np.random.lognormal(1e-5, 3, len(x_data))
y_max = max(y_data)
# plot
plt.xscale('log')
import math
ratio_log = math.log(x_data[np.argmax(y_data)]) / math.log(max(x_data)) # I need to do this in order to plot a horizontal red dashed line that points to the max and do not extend any further.
plt.axhline(y=y_max, xmin=0, xmax = ratio_log, color='r', linestyle='--') # horizontal line pointing to the max y value.
axes = plt.gca()
axes.set_xlim([1, max(x_data)]) # Limits for the x axis.
# custom ticks and labels
# First yticks because I'm able to achieve what I seek
axes.set_yticks([int(y_max)], minor=True) # Sets the custom ytick as a minor one.
from matplotlib.ticker import FormatStrFormatter
axes.yaxis.set_minor_formatter(FormatStrFormatter("%.0f"))
axes.yaxis.grid(False, which='minor') # Removes minor yticks grid. Since I only have my custom yticks as a minor one, this will disable only the grid line corresponding to that ytick. That's a hack.
import matplotlib.ticker as plticker
loc = plticker.MultipleLocator(base=y_max / 3.3) # this locator puts ticks at regular intervals. I ensure the y axis ticks look ok.
axes.yaxis.set_major_locator(loc)
# Now xticks. I'm having a lot of difficulty here, unable to remove the grid of a particular custom xticks.
locs, labels = plt.xticks() # Strangely, this doesn't return the locs and labels that are plotted. There are indeed 2 values that aren't displayed in the plot, here 1.00000000e-01 and 1.00000000e+04. I've got to remove them before I can append my custom loc and label.
# This means that if I do: plt.xticks(locs, labels) right here, it would enlarge both the lower and upper limits on the x axis... I fail to see how that's intuitive or useful at all. Might this be a bug?
locs = np.append(locs[1:-1], np.asarray(x_data[np.argmax(y_data)])) # One of the ugliest hack I have ever seen... to get correct ticks and labels.
labels = (str(int(loc)) for loc in locs) # Just visuals to get integers on the axis.
plt.xticks(locs, labels) # updates the xticks and labels.
plt.plot((x_data[np.argmax(y_data)], x_data[np.argmax(y_data)]), (0, y_max), 'r--') # vertical line that points to the max. Non OO way to do it, so a bad way.
plt.plot(x_data, y_data)
plt.savefig('grid_prob.png')
plt.close()
Example picture below (the code outputs a different picture each time it is executed, but the problem appears in all pictures).
Credit for the idea goes to #ImportanceOfBeingErnest to whom I am extremely grateful.
I removed the grid with
axes.xaxis.grid(False, which='both')
, then I added a grid correspond to each xtick except the custom one with the following loop:
for loc in locs[1:-1]:
if loc != x_data[np.argmax(y_data)]:
plt.axvline(x=loc, color = 'grey', linestyle = '-', linewidth = 0.4)
Insert this code just before the line
plt.xticks(locs, labels) # updates the xticks and labels.
Example of output picture below.

Python Matplotlib: Dual y-axis with same tick spacing and different scale [duplicate]

I created a matplotlib plot that has 2 y-axes. The y-axes have different scales, but I want the ticks and grid to be aligned. I am pulling the data from excel files, so there is no way to know the max limits beforehand. I have tried the following code.
# creates double-y axis
ax2 = ax1.twinx()
locs = ax1.yaxis.get_ticklocs()
ax2.set_yticks(locs)
The problem now is that the ticks on ax2 do not have labels anymore. Can anyone give me a good way to align ticks with different scales?
Aligning the tick locations of two different scales would mean to give up on the nice automatic tick locator and set the ticks to the same positions on the secondary axes as on the original one.
The idea is to establish a relation between the two axes scales using a function and set the ticks of the second axes at the positions of those of the first.
import matplotlib.pyplot as plt
import matplotlib.ticker
fig, ax = plt.subplots()
# creates double-y axis
ax2 = ax.twinx()
ax.plot(range(5), [1,2,3,4,5])
ax2.plot(range(6), [13,17,14,13,16,12])
ax.grid()
l = ax.get_ylim()
l2 = ax2.get_ylim()
f = lambda x : l2[0]+(x-l[0])/(l[1]-l[0])*(l2[1]-l2[0])
ticks = f(ax.get_yticks())
ax2.yaxis.set_major_locator(matplotlib.ticker.FixedLocator(ticks))
plt.show()
Note that this is a solution for the general case and it might result in totally unreadable labels depeding on the use case. If you happen to have more a priori information on the axes range, better solutions may be possible.
Also see this question for a case where automatic tick locations of the first axes is sacrificed for an easier setting of the secondary axes tick locations.
To anyone who's wondering (and for my future reference), the lambda function f in ImportanceofBeingErnest's answer maps the input left tick to a corresponding right tick through:
RHS tick = Bottom RHS tick + (% of LHS range traversed * RHS range)
Refer to this question on tick formatting to truncate decimal places:
from matplotlib.ticker import FormatStrFormatter
ax2.yaxis.set_major_formatter(FormatStrFormatter('%.2f')) # ax2 is the RHS y-axis

Adding secondary X-axis with user-defined list of coordinates

I have a function for plotting amount of graphics using pyplot. The code is here:
def plot_results(results, expers):
"""
type results: list[list[float]]
type expers: list[int]
"""
label_builder = lambda index: 'experiment ' + str(index + 1)
colors = ('green', 'blue')
x_indices = list(map(compute_filesize, list(range(np.shape(results)[1]))))
x_percents = list(map(compute_percent, list(range(np.shape(results)[1]))))
fig, ax1 = plt.subplots()
for i in range(expers):
ax1.plot(x_indices, results[i], color=colors[i], lw=2, label=label_builder(i))
ax1.legend()
plt.show()
For each value of expers list my function plots a chart.
For example, if len(results) == len (expers) == 2, I will get such graph:
I need to create the secondary X-axis (similarly to this, but it may be X-axis and it will be located on the top of graph).
Another difference is that I need to set list of coordinates manually (e.g. ax2.set_coords(x_percents)).
I created new axis using ax2 = ax1.twinx(). Then, I established coordinates list using ax2.set_xticks(x_percents).
But because of each x_percents[i] < x_indices[i], I got such picture:
(as you can see, all coordinates of new axis are located in left-bottom corner)
How can I change my function to make new X-axis:
located on the top side of the graph,
has its own scale, i.e. each value of x_percents corresponds to value of results[i] and
x_percents dispersed throughout interval?
Your code suggests that x_indices and x_percents are linearly related. To keep things clear and useful for others, I'll assume the following for these 2 variables:
x_indices = [0, 5, 10, 25, 50]
max_size = max(x_indices)
x_percents = [ n/max_size * 100 for n in x_indices]
One way you could achieve creating these dual axes that relate to the same data, but just have different labels goes like this: first create an axes, then create another one over that (the twinx/twiny methods could be used but not strictly necessary, I'll use them here for convenience and explaining an important issue that resulted in you setting the xticks for your first axes). Then ensure the limits of both x-axes are the same, set the position of the x-ticks the same as in the first axes and finally change the labels:
import matplotlib.pyplot as plt
vals = [1, 100, 14, 76, 33] # random data, aligned to `x_indices`
fig, ax1 = plt.subplots(1,1)
ax1.plot(x_indices, vals)
ax2 = ax1.twiny() # Remark: twiny will create a new axes
# where the y-axis is shared with ax1,
# but the x-axis is independant - important!
ax2.set_xlim(ax1.get_xlim()) # ensure the independant x-axes now span the same range
ax2.set_xticks(x_indices) # copy over the locations of the x-ticks from the first axes
ax2.set_xticklabels(x_percents) # But give them a different meaning
A graph like this is frequently encountered in physics, where e.g. wavelength and energy are inversely proportionate. On one axis, you would be able to read off the units in one scale (e.g. nanometers) whereas the other would represent the same data in a different scale (e.g. electron volts).

Matplotlib: Add strings as custom x-ticks but also keep existing (numeric) tick labels? Alternatives to matplotlib.pyplot.annotate?

I am trying to produce a graph and I am having some issues annotating it.
My graph has a log scale on the x-axis, showing time. What I want to be able to do is keep the existing (but not predictable) numeric tick labels at 100 units, 1000 units, 10000 units, etc but also add custom tick labels to the x-axis that make it clear where more "human readable" time intervals occur---for instance I want to be able to label 'one week', 'one month', '6 months', etc.
I can use matplotlib.pyplot.annotate() to mark the points but it doesn't really do what I want. I don't really want text and arrows on top of my graph, I just want to add a few extra custom tick marks. Any ideas?
If you really want to add extra ticks, you can get the existing ones using axis.xaxis.get_majorticklocs(), add whatever you want to add, and then set the ticks using axis.xaxis.set_ticks(<your updated array>).
An alternative would be to add vertical lines using axvline. The advantage is that you don't have to worry about inserting your custom tick into the existing array, but you'll have to annotate the lines manually.
Yet another alternative would be to add a linked axis with your custom ticks.
From http://matplotlib.sourceforge.net/api/pyplot_api.html#matplotlib.pyplot.xticks:
# return locs, labels where locs is an array of tick locations and
# labels is an array of tick labels.
locs, labels = xticks()
So all you should need to do is obtain the locs and labels and then modify labels to your liking (dummy example):
labels = ['{0} (1 day)','{0} (1 weak)', '{0} (1 year)']
new_labels = [x.format(locs[i]) for i,x in enumerate(labels)]
and then run:
xticks(locs, new_labels)
This is my solution. The main advantages are:
You can specify the axes (useful for twin axes or if working with multiple axes simultaneously)
You can specify the axis (put ticks on x-axis or y-axis)
You can easily add new ticks while keeping the automatic ones
It automatically replaces if you add a tick that already exists.
Code:
#!/usr/bin/python
from __future__ import division
import matplotlib.pyplot as plt
import numpy as np
#Function to add ticks
def addticks(ax,newLocs,newLabels,pos='x'):
# Draw to get ticks
plt.draw()
# Get existing ticks
if pos=='x':
locs = ax.get_xticks().tolist()
labels=[x.get_text() for x in ax.get_xticklabels()]
elif pos =='y':
locs = ax.get_yticks().tolist()
labels=[x.get_text() for x in ax.get_yticklabels()]
else:
print("WRONG pos. Use 'x' or 'y'")
return
# Build dictionary of ticks
Dticks=dict(zip(locs,labels))
# Add/Replace new ticks
for Loc,Lab in zip(newLocs,newLabels):
Dticks[Loc]=Lab
# Get back tick lists
locs=list(Dticks.keys())
labels=list(Dticks.values())
# Generate new ticks
if pos=='x':
ax.set_xticks(locs)
ax.set_xticklabels(labels)
elif pos =='y':
ax.set_yticks(locs)
ax.set_yticklabels(labels)
#Get numpy arrays
x=np.linspace(0,2)
y=np.sin(4*x)
#Start figure
fig = plt.figure()
ax=fig.add_subplot(111)
#Plot Arrays
ax.plot(x,y)
#Add a twin axes
axr=ax.twinx()
#Add more ticks
addticks(ax,[1/3,0.75,1.0],['1/3','3/4','Replaced'])
addticks(axr,[0.5],['Miguel'],'y')
#Save figure
plt.savefig('MWE.pdf')
I like Miguel's answer above. Worked quite well. However, a small adjustment has to be made. The following:
# Get back tick lists
locs=Dticks.keys()
labels=Dticks.values()
must be changed to
# Get back tick lists
locs=list(Dticks.keys())
labels=list(Dticks.values())
since, in Python 2.7+/3, Dict.keys() and Dict.values() return dict_keys and dict_values objects, which matplotlib does not like (apparently). More about those two objects in PEP 3106.

Categories