I have an image:
Here in the y-axis I would like to get 5x10^-5 4x10^-5 and so on instead of 0.00005 0.00004.
What I have tried so far is:
fig = plt.figure()
ax = fig.add_subplot(111)
y_formatter = matplotlib.ticker.ScalarFormatter(useOffset=True)
ax.yaxis.set_major_formatter(y_formatter)
ax.plot(m_plot,densities1,'-ro',label='0.0<z<0.5')
ax.plot(m_plot,densities2, '-bo',label='0.5<z<1.0')
ax.legend(loc='best',scatterpoints=1)
plt.legend()
plt.show()
This does not seem to work. The document page for tickers does not seem to provide a direct answer.
You can use matplotlib.ticker.FuncFormatter to choose the format of your ticks with a function as shown in the example code below. Effectively all the function is doing is converting the input (a float) into exponential notation and then replacing the 'e' with 'x10^' so you get the format that you want.
import matplotlib.pyplot as plt
import matplotlib.ticker as tick
import numpy as np
x = np.linspace(0, 10, 1000)
y = 0.000001*np.sin(10*x)
fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(x, y)
def y_fmt(x, y):
return '{:2.2e}'.format(x).replace('e', 'x10^')
ax.yaxis.set_major_formatter(tick.FuncFormatter(y_fmt))
plt.show()
If you're willing to use exponential notation (i.e. 5.0e-6.0) however then there is a much tidier solution where you use matplotlib.ticker.FormatStrFormatter to choose a format string as shown below. The string format is given by the standard Python string formatting rules.
...
y_fmt = tick.FormatStrFormatter('%2.2e')
ax.yaxis.set_major_formatter(y_fmt)
...
Just a brief modification to the solution for better string formatting: I would recommend changing the format function to include latex formatting:
def y_fmt(x, y):
return '${:2.1e}'.format(x).replace('e', '\\cdot 10^{') + '}$'
Related
I would like to:
pylab.figure()
pylab.plot(x)
pylab.figure()
pylab.plot(y)
# ...
for i, figure in enumerate(pylab.MagicFunctionReturnsListOfAllFigures()):
figure.savefig('figure%d.png' % i)
What is the magic function that returns a list of current figures in pylab?
Websearch didn't help...
Pyplot has get_fignums method that returns a list of figure numbers. This should do what you want:
import matplotlib.pyplot as plt
import numpy as np
x = np.arange(100)
y = -x
plt.figure()
plt.plot(x)
plt.figure()
plt.plot(y)
for i in plt.get_fignums():
plt.figure(i)
plt.savefig('figure%d.png' % i)
The following one-liner retrieves the list of existing figures:
import matplotlib.pyplot as plt
figs = list(map(plt.figure, plt.get_fignums()))
Edit: As Matti Pastell's solution shows, there is a much better way: use plt.get_fignums().
import numpy as np
import pylab
import matplotlib._pylab_helpers
x=np.random.random((10,10))
y=np.random.random((10,10))
pylab.figure()
pylab.plot(x)
pylab.figure()
pylab.plot(y)
figures=[manager.canvas.figure
for manager in matplotlib._pylab_helpers.Gcf.get_all_fig_managers()]
print(figures)
# [<matplotlib.figure.Figure object at 0xb788ac6c>, <matplotlib.figure.Figure object at 0xa143d0c>]
for i, figure in enumerate(figures):
figure.savefig('figure%d.png' % i)
This should help you (from the pylab.figure doc):
call signature::
figure(num=None, figsize=(8, 6),
dpi=80, facecolor='w', edgecolor='k')
Create a new figure and return a
:class:matplotlib.figure.Figure
instance. If num = None, the
figure number will be incremented and
a new figure will be created.** The
returned figure objects have a
number attribute holding this number.
If you want to recall your figures in a loop then a good aproach would be to store your figure instances in a list and to call them in the loop.
>> f = pylab.figure()
>> mylist.append(f)
etc...
>> for fig in mylist:
>> fig.savefig()
Assuming you haven't manually specified num in any of your figure constructors (so all of your figure numbers are consecutive) and all of the figures that you would like to save actually have things plotted on them...
import matplotlib.pyplot as plt
plot_some_stuff()
# find all figures
figures = []
for i in range(maximum_number_of_possible_figures):
fig = plt.figure(i)
if fig.axes:
figures.append(fig)
else:
break
Has the side effect of creating a new blank figure, but better if you don't want to rely on an unsupported interface
I tend to name my figures using strings rather than using the default (and non-descriptive) integer. Here is a way to retrieve that name and save your figures with a descriptive filename:
import matplotlib.pyplot as plt
figures = []
figures.append(plt.figure(num='map'))
# Make a bunch of figures ...
assert figures[0].get_label() == 'map'
for figure in figures:
figure.savefig('{0}.png'.format(figure.get_label()))
I have an existing plot that was created with pandas like this:
df['myvar'].plot(kind='bar')
The y axis is format as float and I want to change the y axis to percentages. All of the solutions I found use ax.xyz syntax and I can only place code below the line above that creates the plot (I cannot add ax=ax to the line above.)
How can I format the y axis as percentages without changing the line above?
Here is the solution I found but requires that I redefine the plot:
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()
Link to the above solution: Pyplot: using percentage on x axis
This is a few months late, but I have created PR#6251 with matplotlib to add a new PercentFormatter class. With this class you just need one line to reformat your axis (two if you count the import of matplotlib.ticker):
import ...
import matplotlib.ticker as mtick
ax = df['myvar'].plot(kind='bar')
ax.yaxis.set_major_formatter(mtick.PercentFormatter())
PercentFormatter() accepts three arguments, xmax, decimals, symbol. xmax allows you to set the value that corresponds to 100% on the axis. This is nice if you have data from 0.0 to 1.0 and you want to display it from 0% to 100%. Just do PercentFormatter(1.0).
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.
Update
PercentFormatter was introduced into Matplotlib proper in version 2.1.0.
pandas dataframe plot will return the ax for you, And then you can start to manipulate the axes whatever you want.
import pandas as pd
import numpy as np
df = pd.DataFrame(np.random.randn(100,5))
# you get ax from here
ax = df.plot()
type(ax) # matplotlib.axes._subplots.AxesSubplot
# manipulate
vals = ax.get_yticks()
ax.set_yticklabels(['{:,.2%}'.format(x) for x in vals])
Jianxun's solution did the job for me but broke the y value indicator at the bottom left of the window.
I ended up using FuncFormatterinstead (and also stripped the uneccessary trailing zeroes as suggested here):
import pandas as pd
import numpy as np
from matplotlib.ticker import FuncFormatter
df = pd.DataFrame(np.random.randn(100,5))
ax = df.plot()
ax.yaxis.set_major_formatter(FuncFormatter(lambda y, _: '{:.0%}'.format(y)))
Generally speaking I'd recommend using FuncFormatter for label formatting: it's reliable, and versatile.
For those who are looking for the quick one-liner:
plt.gca().set_yticklabels([f'{x:.0%}' for x in plt.gca().get_yticks()])
this assumes
import: from matplotlib import pyplot as plt
Python >=3.6 for f-String formatting. For older versions, replace f'{x:.0%}' with '{:.0%}'.format(x)
I'm late to the game but I just realize this: ax can be replaced with plt.gca() for those who are not using axes and just subplots.
Echoing #Mad Physicist answer, using the package PercentFormatter it would be:
import matplotlib.ticker as mtick
plt.gca().yaxis.set_major_formatter(mtick.PercentFormatter(1))
#if you already have ticks in the 0 to 1 range. Otherwise see their answer
I propose an alternative method using seaborn
Working code:
import pandas as pd
import seaborn as sns
data=np.random.rand(10,2)*100
df = pd.DataFrame(data, columns=['A', 'B'])
ax= sns.lineplot(data=df, markers= True)
ax.set(xlabel='xlabel', ylabel='ylabel', title='title')
#changing ylables ticks
y_value=['{:,.2f}'.format(x) + '%' for x in ax.get_yticks()]
ax.set_yticklabels(y_value)
You can do this in one line without importing anything:
plt.gca().yaxis.set_major_formatter(plt.FuncFormatter('{}%'.format))
If you want integer percentages, you can do:
plt.gca().yaxis.set_major_formatter(plt.FuncFormatter('{:.0f}%'.format))
You can use either ax.yaxis or plt.gca().yaxis. FuncFormatter is still part of matplotlib.ticker, but you can also do plt.FuncFormatter as a shortcut.
Based on the answer of #erwanp, you can use the formatted string literals of Python 3,
x = '2'
percentage = f'{x}%' # 2%
inside the FuncFormatter() and combined with a lambda expression.
All wrapped:
ax.yaxis.set_major_formatter(FuncFormatter(lambda y, _: f'{y}%'))
Another one line solution if the yticks are between 0 and 1:
plt.yticks(plt.yticks()[0], ['{:,.0%}'.format(x) for x in plt.yticks()[0]])
add a line of code
ax.yaxis.set_major_formatter(ticker.PercentFormatter())
I want to set the formatting of the y-axis offset in my plot to non-scientific notation, but I can't find a setting to do this. Other questions and their solutions describe how to either remove the offset altogether, or set the y-ticks to scientific/plain notation; I haven't found an answer for setting the notation of the offset itself.
I've already tried using these two options, but I think they're meant for the y-ticks, not the offsets:
ax.ticklabel_format(axis='y', style='plain', useOffset=6378.1)
and
ax.get_yaxis().get_major_formatter().set_scientific(False)
So, the actual result is +6.3781e3, when I want +6378.1
Any way to do this?
Edit: Added example code and figure:
#!/usr/bin/env python
from matplotlib import pyplot as plt
from matplotlib import ticker
plt.rcParams['font.family'] = 'monospace'
import random
Date = range(10)
R = [6373.1+10*random.random() for i in range(10)]
fig, ax = plt.subplots(figsize=(9,6))
ax.plot(Date,R,'-D',zorder=2,markersize=3)
ax.ticklabel_format(axis='y', style='plain', useOffset=6378.1)
ax.set_ylabel('Mean R (km)',fontsize='small',labelpad=1)
plt.show()
You can subclass the default ScalarFormatter and replace the get_offset method, such that it would simply return the offset as it is. Note that if you wanted to make this compatible with the multiplicative "offset", this solution would need to be adapted (currently it just prints a warning).
from matplotlib import pyplot as plt
import matplotlib.ticker
import random
class PlainOffsetScalarFormatter(matplotlib.ticker.ScalarFormatter):
def get_offset(self):
if len(self.locs) == 0:
return ''
if self.orderOfMagnitude:
print("Your plot will likely be labelled incorrectly")
return self.offset
Date = range(10)
R = [6373.1+10*random.random() for i in range(10)]
fig, ax = plt.subplots(figsize=(9,6))
ax.plot(Date,R,'-D',zorder=2,markersize=3)
ax.yaxis.set_major_formatter(PlainOffsetScalarFormatter())
ax.ticklabel_format(axis='y', style='plain', useOffset=6378.1)
ax.set_ylabel('Mean R (km)',fontsize='small',labelpad=1)
plt.show()
A way to do this is to disable the offset text itself and add your custom ax.text there as follows
from matplotlib import pyplot as plt
import random
plt.rcParams['font.family'] = 'monospace'
offset = 6378.1
Date = range(10)
R = [offset+10*random.random() for i in range(10)]
fig, ax = plt.subplots(figsize=(9,6))
ax.plot(Date,R,'-D',zorder=2,markersize=3)
ax.ticklabel_format(axis='y', style='plain', useOffset=offset)
ax.set_ylabel('Mean R (km)',fontsize='small',labelpad=1)
ax.yaxis.offsetText.set_visible(False)
ax.text(x = 0.0, y = 1.01, s = str(offset), transform=ax.transAxes)
plt.show()
I am plotting over a period of seconds and have time as the labels on the x-axis. Here is the only way I could get the correct time stamps. However, there are a bunch of zeros on the end. Any idea how to get rid of them??
plt.style.use('seaborn-whitegrid')
df['timestamp'] = pd.to_datetime(df['timestamp'])
fig, ax = plt.subplots(figsize=(8,4))
seconds=MicrosecondLocator(interval=500000)
myFmt = DateFormatter("%S:%f")
ax.plot(df['timestamp'], df['vibration(g)_0'], c='blue')
ax.xaxis.set_major_locator(seconds)
ax.xaxis.set_major_formatter(myFmt)
plt.gcf().autofmt_xdate()
plt.show()
This produces this image. Everything looks perfect except for all of the extra zeros. How can I get rid of them while still keeping the 5?
I guess you would want to simply cut the last 5 digits out of the string. That's also what answers to python datetime: Round/trim number of digits in microseconds suggest.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.dates import MicrosecondLocator, DateFormatter
from matplotlib.ticker import FuncFormatter
x = np.datetime64("2018-11-30T00:00") + np.arange(1,4, dtype="timedelta64[s]")
fig, ax = plt.subplots(figsize=(8,4))
seconds=MicrosecondLocator(interval=500000)
myFmt = DateFormatter("%S:%f")
ax.plot(x,[2,1,3])
def trunc_ms_fmt(x, pos=None):
return myFmt(x,pos)[:-5]
ax.xaxis.set_major_locator(seconds)
ax.xaxis.set_major_formatter(FuncFormatter(trunc_ms_fmt))
plt.gcf().autofmt_xdate()
plt.show()
Note that this format is quite unusual; so make sure the reader of the plot understands it.
I am using matplotlib to plot some financial data. However, in its default configuration matplotlib inserts gaps in place of missing data. The documentation recommends using a date index formatter to resolve this.
However, can be seen in the examples provided on the page:
The formatting has changed from "Sept 03 2008" => "2008-09-03"
The chart no longer ends on the final sample, but rather is padded to "2008-10-14".
How can I retain this default behavior while still avoiding gaps in the data?
Edit
Sample code, from the documentation, with the desired ticks on top.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.mlab as mlab
import matplotlib.cbook as cbook
import matplotlib.ticker as ticker
datafile = cbook.get_sample_data('aapl.csv', asfileobj=False)
print 'loading', datafile
r = mlab.csv2rec(datafile)
r.sort()
r = r[-30:] # get the last 30 days
# first we'll do it the default way, with gaps on weekends
fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(r.date, r.adj_close, 'o-')
fig.autofmt_xdate()
# next we'll write a custom formatter
N = len(r)
ind = np.arange(N) # the evenly spaced plot indices
def format_date(x, pos=None):
thisind = np.clip(int(x+0.5), 0, N-1)
return r.date[thisind].strftime('%Y-%m-%d')
fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(ind, r.adj_close, 'o-')
ax.xaxis.set_major_formatter(ticker.FuncFormatter(format_date))
fig.autofmt_xdate()
plt.show()
Well, I'll answer the easy part: To get Sept 03 2008 instead of 2008-09-03 use strftime('%b %d %Y'):
def format_date(x, pos=None):
thisind = np.clip(int(x+0.5), 0, N-1)
result = r.date[thisind].strftime('%b %d %Y')
return result
PS. The last date in r.date is Oct 14 2008, so I don't think it is a bad thing to include a tick mark for it. Are you sure you don't want it?