Custom ticks autoscaled when using imshow? - python

I am trying to change the value of the ticks on the x-axis an imshow plot using the following code:
import matplotlib.pyplot as plt
import numpy as np
def scale_xaxis(number):
return(number+1001)
data = np.array([range(10),range(10,20)])
fig = plt.figure(figsize=(3,5))
ax = fig.add_subplot(111)
ax.imshow(data,aspect='auto')
ax.autoscale(False)
xticks = ax.get_xticks()
ax.xaxis.set_ticklabels(scale_xaxis(xticks))
plt.savefig("test.png")
Resulting image http://ubuntuone.com/2Y5ujtlEkEnrlTcVUxvWLU
However the x-ticks overlap and have "non-round" values. Is there some way for matplotlib to automatically do this? Either by using set_ticklabels or some other way?

Also look into using extent (doc) to let matplotlib do all the thinking about how to put in the tick labels and add in an arbitrary shift:
data = np.array([range(10),range(10,20)])
fig = plt.figure(figsize=(3,5))
ax = fig.add_subplot(111)
ax.imshow(data,aspect='auto',extent=[10000,10010,0,1])
If you definitely want do to it my hand, you might be better off setting the formatter and locator of the axis to get what you want (doc).
import matplotlib.pyplot as plt
import numpy as np
def scale_xaxis(number):
return(number+1001)
def my_form(x,pos):
return '%d'%scale_xaxis(x)
data = np.array([range(10),range(10,20)])
fig = plt.figure(figsize=(3,5))
ax = fig.add_subplot(111)
ax.imshow(data,aspect='auto')
ax.autoscale(False)
ax.xaxis.set_major_locator(matplotlib.ticker.MultipleLocator(int(2)))
ax.xaxis.set_major_formatter(matplotlib.ticker.FuncFormatter(my_form))
The locator needs to be set to make sure that ticks don't get put at non-integer locations which are then forcible cast to integers by the formatter (which would leave them in the wrong place)
related questions:
matplotlib: format axis offset-values to whole numbers or specific number
removing leading 0 from matplotlib tick label formatting

There are several ways to do this.
You can:
Pass in an array of ints instead of an array of floats
Pass in an array of formatted strings
Use a custom tick formatter
The last option is overkill for something this simple.
As an example of the first option, you'd change your scale_xaxis function to be something like this:
def scale_xaxis(numbers):
return numbers.astype(int) + 1001
Note that what you're getting out of ax.get_xticks is a numpy array instead of a single value. Thus, we need to do number.astype(int) instead of int(number).
Alternately, we could return a series of formatted strings. set_xticklabels actually expects a sequence of strings:
def scale_xaxis(numbers):
return ['{:0.0f}'.format(item + 1001) for item in numbers]
Using a custom tick formatter is overkill here, so I'll leave it out for the moment. It's quite handy in the right situation, though.

Related

adjusting graph in maplotlib (python)

graph
how do I make this graph infill all the square around it? (I colored the part that I want to take off in yellow, for reference)
Normally I use two methods to adjust axis limits depending on a situation.
When a graph is simple, axis.set_ylim(bottom, top) method is a quick way to directly change y-axis (you might know this already).
Another way is to use matplotlib.ticker. It gives you more utilities to adjust axis ticks in your graph.
https://matplotlib.org/3.1.1/gallery/ticks_and_spines/tick-formatters.html
I'm guessing you're using a list of strings to set yaxis tick labels. You may want to set locations (float numbers) and labels (string) of y-axis ticks separatedly. Then set the limits on locations like the following snippet.
import matplotlib.pyplot as plt
import matplotlib.ticker as mt
fig, ax = plt.subplots(1,1)
ax.plot([0,1,2], [0,1,2])
ax.yaxis.set_major_locator(mt.FixedLocator([0,1,2]))
ax.yaxis.set_major_formatter(mt.FixedFormatter(["String1", "String2", "String3"]))
ax.set_ylim(bottom=0, top=2)
It gives you this: generated figure
Try setting the min and max of your x and y axes.

Format tick labels in scatter plot to % in matplotlib - python [duplicate]

I have a line chart based on a simple list of numbers. By default the x-axis is just the an increment of 1 for each value plotted. I would like to be a percentage instead but can't figure out how. So instead of having an x-axis from 0 to 5, it would go from 0% to 100% (but keeping reasonably spaced tick marks. Code below. Thanks!
from matplotlib import pyplot as plt
from mpl_toolkits.axes_grid.axislines import Subplot
data=[8,12,15,17,18,18.5]
fig=plt.figure(1,(7,4))
ax=Subplot(fig,111)
fig.add_subplot(ax)
plt.plot(data)
The code below will give you a simplified x-axis which is percentage based, it assumes that each of your values are spaces equally between 0% and 100%.
It creates a perc array which holds evenly-spaced percentages that can be used to plot with. It then adjusts the formatting for the x-axis so it includes a percentage sign using matplotlib.ticker.FormatStrFormatter. Unfortunately this uses the old-style string formatting, as opposed to the new style, the old style docs can be found here.
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.ticker as mtick
data = [8,12,15,17,18,18.5]
perc = np.linspace(0,100,len(data))
fig = plt.figure(1, (7,4))
ax = fig.add_subplot(1,1,1)
ax.plot(perc, data)
fmt = '%.0f%%' # Format you want the ticks, e.g. '40%'
xticks = mtick.FormatStrFormatter(fmt)
ax.xaxis.set_major_formatter(xticks)
plt.show()
This is a few months late, but I have created PR#6251 with matplotlib to add a new PercentFormatter class. With this class you can do as follows to set the axis:
import matplotlib.ticker as mtick
# Actual plotting code omitted
ax.xaxis.set_major_formatter(mtick.PercentFormatter(5.0))
This will display values from 0 to 5 on a scale of 0% to 100%. The formatter is similar in concept to what #Ffisegydd suggests doing except that it can take any arbitrary existing ticks into account.
PercentFormatter() accepts three arguments, max, decimals, and symbol. max allows you to set the value that corresponds to 100% on the axis (in your example, 5).
The other two parameters allow you to set the number of digits after the decimal point and the symbol. They default to None and '%', respectively. decimals=None will automatically set the number of decimal points based on how much of the axes you are showing.
Note that this formatter will use whatever ticks would normally be generated if you just plotted your data. It does not modify anything besides the strings that are output to the tick marks.
Update
PercentFormatter was accepted into Matplotlib in version 2.1.0.
Totally late in the day, but I wrote this and thought it could be of use:
def transformColToPercents(x, rnd, navalue):
# Returns a pandas series that can be put in a new dataframe column, where all values are scaled from 0-100%
# rnd = round(x)
# navalue = Nan== this
hv = x.max(axis=0)
lv = x.min(axis=0)
pp = pd.Series(((x-lv)*100)/(hv-lv)).round(rnd)
return pp.fillna(navalue)
df['new column'] = transformColToPercents(df['a'], 2, 0)

How to remove scientific notations from this bar plot? [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
How to remove relative shift in matplotlib axis
I'm plotting numbers with five digits (210.10, 210.25, 211.35, etc) against dates and I'd like to have the y-axis ticks show all digits ('214.20' rather than '0.20 + 2.14e2') and have not been able to figure this out. I've attempted to set the ticklabel format to plain, but it appears to have no effect.
plt.ticklabel_format(style='plain', axis='y')
Any hints on the obvious I'm missing?
The axis numbers are defined according to a given Formatter. Unfortunately (AFAIK), matplotlib does not expose a way to control the threshold to go from the numbers to a smaller number + offset. A brute force approach would be setting all the xtick strings:
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(100, 100.1, 100)
y = np.arange(100)
fig = plt.figure()
plt.plot(x, y)
plt.show() # original problem
# setting the xticks to have 3 decimal places
xx, locs = plt.xticks()
ll = ['%.3f' % a for a in xx]
plt.xticks(xx, ll)
plt.show()
This is actually the same as setting a FixedFormatter with the strings:
from matplotlib.ticker import FixedFormatter
plt.gca().xaxis.set_major_formatter(FixedFormatter(ll))
However, the problem of this approach is that the labels are fixed. If you want to resize/pan the plot, you have to start over again. A more flexible approach is using the FuncFormatter:
def form3(x, pos):
""" This function returns a string with 3 decimal places, given the input x"""
return '%.3f' % x
from matplotlib.ticker import FuncFormatter
formatter = FuncFormatter(form3)
gca().xaxis.set_major_formatter(FuncFormatter(formatter))
And now you can move the plot and still maintain the same precision. But sometimes this is not ideal. One doesn't always want a fixed precision. One would like to preserve the default Formatter behaviour, just increase the threshold to when it starts adding an offset. There is no exposed mechanism for this, so what I end up doing is to change the source code. It's pretty easy, just change one character in one line in ticker.py. If you look at that github version, it's on line 497:
if np.absolute(ave_oom - range_oom) >= 3: # four sig-figs
I usually change it to:
if np.absolute(ave_oom - range_oom) >= 5: # four sig-figs
and find that it works fine for my uses. Change that file in your matplotlib installation, and then remember to restart python before it takes effect.
You can also just turn the offset off: (almost exact copy of How to remove relative shift in matplotlib axis)
import matlplotlib is plt
plt.plot([1000, 1001, 1002], [1, 2, 3])
plt.gca().get_xaxis().get_major_formatter().set_useOffset(False)
plt.draw()
This grabs the current axes, gets the x-axis axis object and then the major formatter object and sets useOffset to false (doc).

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.

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