Matplotlib xticks labels: how to modify attributes value - python

Following this I know that I can extract the xticks labels and positions using:
import matplotlib.pyplot as plt
plt.scatter(x_data, y_data)
locs, labels=plt.xticks()
the new variable labels is a matplotlib.cbook.silent_list, which
doesn't behave like a normal list.
Is there a way to access and modify any attribute value of the labels elements?
Specifically I would like to know if I can select a subset of the labels (i.e. slice the silent_list) and modify a particular attribute for that subset.
Here is a toy example:
import numpy as np
import matplotlib.pyplot as plt
x=np.array([1,2,3,4,5,6,7,8])
y=np.random.normal(0, 1, (8, 1))
plt.scatter(x, y)
locs, labels=plt.xticks()
As an example, let say I want to change the labels color to red for all but the first and last element of labels; if I open one of the elements of the variable I can see that there is the attribute _color with value k, which I would like to change in r:
I tried to slice it:
labels[1:-1]
But it returns:
Out[]: [Text(2,0,'2'), Text(4,0,'4'), Text(6,0,'6'), Text(8,0,'8')]
and this is as far as I managed to go.
I couldn't figure out a way to access the attribute and change its value.
NB: I am looking for a general way to access these attributes and change the value, I do not care about changing the labels color specifically. That's just an example.

You might be interested in an alternative solution where you can choose which specific ticks you want to color. Here I have to loop from [1:-1] because the first and the last ticks do not appear on the graph here but they appear in the labels
import numpy as np; np.random.seed(134)
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
x=np.array([1,2,3,4,5,6,7,8])
y=np.random.normal(0, 1, (8, 1))
plt.scatter(x, y)
fig.canvas.draw()
xticks = ax.get_xticklabels()
target_ticks = [1, 3, 6, len(xticks)-2]
for i, lab in enumerate(xticks[1:-1]):
if i+1 in target_ticks:
lab.set_color('r')

Related

Understanding maplotlib and how to format matplotlib axis with a single digit?

I am having an very hard time getting the ticklabels of a seaborn heatmap to show only single integers (i.e. no floating numbers). I have two lists that form the axes of a data frame that i plot using seaborn.
import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
import matplotlib.ticker as tkr
x = np.linspace(0, 15, 151)
y = np.linspace(0, 15, 151)
#substitute random data for my_data
df_map = pd.DataFrame(my_data, index = y, columns = x)
plt.figure()
ax = sns.heatmap(df_map, square = True, xticklabels = 20, yticklabels = 20)
ax.invert_yaxis()
I've reviewed many answers and the documents. My biggest problem is I have little experience and a very poor understanding of matplotlib and the docs feel like a separate language... Here are the things I've tried.
ATTEMPT 1: A slightly modified version of the solution to this question:
fmtr = tkr.StrMethodFormatter('{x:.0f}')
plt.gca().xaxis.set_major_formatter(fmtr)
I'm pretty sure tkr.StrMethodFormatter() is displaying every 20th index of the value it encounters in my axis string, which is probably due to my settings in sns.heatmap(). I tried different string inputs to tkr.StrMethodFormatter() without success. I looked at two other questions and tried different combinations of tkr classes that were used in answers for here and here.
ATTEMPT 2:
fmtr = tkr.StrMethodFormatter("{x:.0f}")
locator = tkr.MultipleLocator(50)
fstrform = tkr.FormatStrFormatter('%.0f')
plt.gca().xaxis.set_major_formatter(fmtr)
plt.gca().xaxis.set_major_locator(locator)
#plt.gca().xaxis.set_major_formatter(fstrform)
And now i'm at a complete loss. I've found out locator changes which nth indices to plot, and both fmtr and fstrform change the number of decimals being displayed, but i cannot for the life of me get the axes to display the integer values that exist in the axes lists!
Please help! I've been struggling for hours. It's probably something simple, and thank you!
As an aside:
Could someone please elaborate on the documentation excerpt in that question, specifically:
...and the field used for the position must be labeled pos.
Also, could someone please explain the differences between tkr.StrMethodFormatter("{x:.0f}") and tkr.FormatStrFormatter('%.0f')? I find it annoying there are two ways, each with their own syntax, to produce the same result.
UPDATE:
It took me a while to get around to implementing the solution provided by #ImportanceOfBeingErnest. I took an extra precaution and rounded the numbers in the x,y arrays. I'm not sure if this is necessary, but I've produced the result I wanted:
x = np.linspace(0, 15, 151)
y = np.linspace(0, 15, 151)
# round float numbers in axes arrays
x_rounded = [round(i,3) for i in x]
y_rounded = [round(i,3) for i in y]
#substitute random data for my_data
df_map = pd.DataFrame(my_data, index = y_rounded , columns = x_rounded)
plt.figure()
ax0 = sns.heatmap(df_map, square = True, xticklabels = 20)
ax0.invert_yaxis()
labels = [label.get_text() for label in ax0.get_xticklabels()]
ax0.set_xticklabels(map(lambda x: "{:g}".format(float(x)), labels))
Although I'm still not entirely sure why this worked; check the comments between me and them for clarification.
The sad thing is, you didn't do anything wrong. The problem is just that seaborn has a very perculiar way of setting up its heatmap.
The ticks on the heatmap are at fixed positions and they have fixed labels. So to change them, those fixed labels need to be changed. An option to do so is to collect the labels, convert them back to numbers, and then set them back.
labels = [label.get_text() for label in ax.get_xticklabels()]
ax.set_xticklabels(map(lambda x: "{:g}".format(float(x)), labels))
labels = [label.get_text() for label in ax.get_yticklabels()]
ax.set_yticklabels(map(lambda x: "{:g}".format(float(x)), labels))
A word of caution: One should in principle never set the ticklabels without setting the locations as well, but here seaborn is responsible for setting the positions. We just trust it do do so correctly.
If you want numeric axes with numeric labels that can be formatted as attempted in the question, one may directly use a matplotlib plot.
import numpy as np
import seaborn as sns # seaborn only imported to get its rocket cmap
import matplotlib.pyplot as plt
my_data = np.random.rand(150,150)
x = (np.linspace(0, my_data.shape[0], my_data.shape[0]+1)-0.5)/10
y = (np.linspace(0, my_data.shape[1], my_data.shape[1]+1)-0.5)/10
fig, ax = plt.subplots()
pc = ax.pcolormesh(x, y, my_data, cmap="rocket")
fig.colorbar(pc)
ax.set_aspect("equal")
plt.show()
While this already works out of the box, you may still use locators and formatters as attempted in the question.

What is the name of the matplotlib function that gets executed when initially plotting the data which sets all axes correctly?

When I plot some data with matplotlib without setting any parameters, the data gets plotted with both x and y axis limits set correctly, meaning that all data is shown and no space is wasted (case 1):
import matplotlib
matplotlib.use('QT5Agg')
import matplotlib.pyplot as plt
x = range(10)
plt.plot(x,'-o',markersize='10')
plt.tight_layout()
plt.show()
Result:
If I set some limits for e. g. the x axis, even using autoscale() does not autoscale the y axis anymore (case 2):
import matplotlib
matplotlib.use('QT5Agg')
import matplotlib.pyplot as plt
x = range(10)
plt.plot(x,'-o',markersize='10')
plt.autoscale(enable=True,axis='y')
plt.xlim(7.5,11)
plt.tight_layout()
plt.show()
Result:
Question: which function is used internally by matplotlib to determine the limits for both axes and update the plot in case 1?
Background: I want to use this function as a base for reimplementing / extending this functionality for case 2.
As #ImportanceOfBeingEarnest pointed out in the answer below, there is no such automatized way at the moment. So, in case you are interested in knowing how to rescale your y-axis, one way to do so is by recomputing the corresponding y-values and then reassigning the y-limits using the method specified in this unaccepted answer. I haven't marked this as a duplicate because there are certain different issues in your example:
First (major one), you have plotted only x-values. So, to apply the method in the other answer, I had to first get the y-values in an array. This is done using get_ydata()
Second, the x-values were changed from range() generator to a NumPy array, as the former does not support indexing.
Third, I had to use a variable for the x-limits to be consistent with the function.
import numpy as np
import matplotlib.pyplot as plt
x = np.arange(10)
plt.plot(x,'-o',markersize='10')
x_lims = [7.5, 11]
plt.xlim(x_lims)
ax = plt.gca()
y = ax.lines[0].get_ydata()
def find_nearest(array,value):
idx = (np.abs(array-value)).argmin()
return idx
y_low = y[find_nearest(x, x_lims[0])]
y_high = y[find_nearest(x, x_lims[1])]
ax.set_ylim(y_low, y_high)
plt.tight_layout()
plt.show()

Only color some tick labels [duplicate]

Using matplotlib, is there an option to change the color of specific tick labels on the axis?
I have a simple plot that show some values by days, and I need to mark some days as 'special' day so I want to mark these with a different color but not all ticks just some specific.
You can get a list of tick labels using ax.get_xticklabels(). This is actually a list of text objects. As a result, you can use set_color() on an element of that list to change the color:
import matplotlib.pyplot as plt
fig, ax = plt.subplots(figsize=(5,4))
ax.plot([1,2,3])
ax.get_xticklabels()[3].set_color("red")
plt.show()
Alternatively, you can get the current axes using plt.gca(). The below code will give the same result
import matplotlib.pyplot as plt
plt.figure(figsize=(5,4))
plt.plot([1, 2, 3])
plt.gca().get_xticklabels()[3].set_color("red")
plt.show()

Formatting string ticklabels matplotlib

I have a set of ticklabels that are strings on my x axis, and I want to be able to get -> modify -> set them. Say for example I have a plot that looks like this:
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
ax.plot(range(1,6), range(5))
plt.xticks(range(1,6), ['a','b','c','d','e']
and I want to change the labels on the x axis to ['(a)','(b)','(c)','(d)','(e)']
what is the simplest/best way to do this? I've tried things like:
labels = ['(%s)' % l for l in ax.xaxis.get_ticklabels()]
ax.xaxis.set_ticklabels(labels)
but ax.xaxis.get_ticklabels() returns matplotlib Text objects as opposed to a list of strings and I'm not sure how to go about modifying them. I also tried using matplotlib.ticker.FuncFormatter but could only get a hold of the numeric positions not the labels themselves. Any would be appreciated.
One more layer to unpeel:
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
ax.plot(range(1,6), range(5))
plt.xticks(range(1,6), ['a','b','c','d','e'])
labels = ['(%s)' % l.get_text() for l in ax.xaxis.get_ticklabels()]
ax.xaxis.set_ticklabels(labels)
your code but with l.get_text() in the list comp where there was a l.

manipulate tick labels on a colorbar

I want to access the tick labels on my matplotlib colobar, so that I can manipulate them.
My starting labels may be [-2,-1,0,1,2] for example.
I have used:
locs,oldlabels = plt.xticks()
newlabels = ['a','b','c','d','e']
plt.xticks(locs, newlabels)
This works. But I don't want to manually write in the new labels. I want to access the oldlabels, so that I can have the newlabels as say [2*(-2), 2*(-1), 2*0, 2*1, 2*2].
I just don't know how to 'get at' the oldlabels. I googled everything and tried lots of things, but I'm doing something fundamentally wrong.
I tried to print oldlabels[0], but I get Text(0,0,u'\u22122.0').
EDIT:
I'm currently doing:
new_labels = [1,2,3,4,5,6,7,8,9]
colorbarname.ax.set_xticklabels(new_labels)
which works. But I want to set them as 2 x their old value. How can I do this automatically? I need to extract the old label values, multiply by (say) 2, update the axis labels with the new values.
If your data is not confined to [0,1], I'd recommend using a norm when you pass the data to the colormap instead of changing the data and relabeling the colorbar: http://matplotlib.org/api/cm_api.html?highlight=norm%20colormap#matplotlib.cm.ScalarMappable.norm
However, you can relabel the colorbar by manipulating the underlying axis directly:
import numpy as np
import pylab as plt
A = np.random.random((10,10))
plt.subplot(121)
plt.imshow(A,interpolation='nearest')
cb = plt.colorbar()
oldlabels = cb.ax.get_yticklabels()
print(map(lambda x: x.get_text(),oldlabels))
newlabels = map(lambda x: str(2 * float(x.get_text())), oldlabels)
print(newlabels)
cb.ax.set_yticklabels(newlabels)
plt.show()
oh, and now I find the matplotlib gallery example, nearly the same: http://matplotlib.org/examples/pylab_examples/colorbar_tick_labelling_demo.html

Categories

Resources