When I try to do a plot against a range with big enough numbers I get an axis with relative shift for all the ticks. For example:
plot([1000, 1001, 1002], [1, 2, 3])
I get these ticks on axis of abscissas:
0.0 0.5 1.0 1.5 2.0
+1e3
The question is how to remove +1e3 and get just:
1000.0 1000.5 1001.0 1001.5 1002.0
plot([1000, 1001, 1002], [1, 2, 3])
gca().get_xaxis().get_major_formatter().set_useOffset(False)
draw()
This grabs the current axes, gets the x-axis axis object and then the major formatter object and sets useOffset to false (doc).
In newer versions (1.4+) of matplotlib the default behavior can be changed via the axes.formatter.useoffset rcparam.
To disable relative shift everywhere, set the rc parameter:
import matplotlib
matplotlib.rc('axes.formatter', useoffset=False)
Related
I noticed in doing some line plots that Matplotlib exhibits strange behaviour (using Python 3.7 and the default TKAgg backend). I've created a program plotting lines of various widths to show the problem. The program creates a bunch of financial looking data and then runs through a loop showing line plots of various linewidths. At the beginning of each loop it asks the user to input the linewidth they would like to see. Just enter 0 to end the program.
import numpy as np
import matplotlib as mpl
from matplotlib.lines import Line2D
import matplotlib.pyplot as plt
# Initialize prices and arrays
initial_price = 35.24
quart_hour_prices = np.empty(32) # 32 15 min periods per days
day_prices = np.empty([100,2]) # 100 days [high,low]
quart_hour_prices[0] = initial_price
# Create Data
for day in range(100):
for t in range(1, 32):
quart_hour_prices[t] = quart_hour_prices[t-1] + np.random.normal(0, .2) # 0.2 stand dev in 15 min
day_prices[day,0] = quart_hour_prices.max()
day_prices[day,1] = quart_hour_prices.min()
quart_hour_prices[0] = quart_hour_prices[31]
# Setup Plot
fig, ax = plt.subplots()
# Loop through plots of various linewidths
while True:
lw = float(input("Enter linewidth:")) # input linewidth
if lw == 0: # enter 0 to exit program
exit()
plt.cla() # clear plot before adding new lines
plt.title("Linewidth is: " + str(round(lw,2)) + " points")
# loop through data to create lines on plot
for d in range(100):
high = day_prices[d,1]
low = day_prices[d,0]
hl_bar = Line2D(xdata=(d, d), ydata=(high, low), color='k', linewidth=lw, antialiased=False)
ax.add_line(hl_bar)
ax.autoscale_view()
plt.show(block=False)
Matplotlib defines linewidths in points and its default is to have 72ppi. It also uses a default of 100dpi. So this means each point of linewidth takes up .72 dots or pixels. Thus I would expect to see linewidths less than 0.72 to be one pixel wide, those from 0.72 - 1.44 to be two pixels wide, and so on. But this is not what was observed.
A 0.72 linewidth did indeed give me a line that was one pixel wide. And then when the linewidth is increased to 0.73 the line gets thicker as expected. But it is now three pixels wide, instead of the two I expected.
For linewidths less than 0.72 the plot remains the same all the way down to 0.36. But then when I enter a linewidth of 0.35 or less, the line suddenly gets thicker (2 pixels wide), as shown by the graph below. How can the line get thicker if I reduce the linewidth? This was very unexpected.
Continuing the same testing process for greater linewidths, the plot of the 0.73 linewidth remains the same all the way up until a width of 1.07. But then at 1.08 the linewidth mysteriously gets thinner (2 pixels wide) being the same as the 0.35 and below plots. How can the line get thinner if I increase the linewidth? This was also very unexpected.
This strange behavior continues with greater linewidths. Feel free to use the above code to try it for yourself. Here is a table to summarize the results :
Points Linewidth in pixels
0.01 - 0.35 2
0.36 - 0.72 1
0.73 - 1.07 3
1.08 - 1.44 2
1.45 - 1.79 4
1.80 - 2.16 3
2.17 - 2.51 5
2.52 - 2.88 4
The pattern is something like 1 step back, 2 steps forward. Does anyone know why Matplotlib produces these results?
The practical purpose behind this question is that I am trying to produce an algorithm to vary the linewidth depending upon the density of the data in the plot. But this is very difficult to do when the line thicknesses are jumping around in such a strange fashion.
Simple question and I tried a quick search before posting but could not find. I am trying to do a chart and axis Y consists of price.
However Y is scaled like attached image and has only 1 decimal. How do I make y axis more precise with 2 decimals and more entries with increment of 0.01?
::Update with code::
# Make the plot
fig, ax = plt.subplots(figsize=(48,32))
ax.scatter(x=times, y=tidy['Price'], c=colors, s=tidy['Volume'] / 4000, alpha=0.4)
ax.ticklabel_format(axis='y', style='plain')
ax.set(
xlabel='Time',
xlim=(xmin, xmax),
ylabel='Price'
)
ax.xaxis.set_major_formatter(DateFormatter('%H:%M'))
One method to increase the number of decimals is to use a formatter for your axis:
from matplotlib.ticker import FormatStrFormatter
ax.yaxis.set_major_formatter(FormatStrFormatter('%.2f'))
However, this method will not increase the number of ticks on your axis. You can set the yticks with .01 increments using the following but you might end up over-saturating the axis might want to increase the increment size.
ax.set_yticks(np.arange(108.30,108.71,.01))
Matplotlib version alongside Ubuntu 14.04 LTS, Python 2.7 and Jupyter 4.1.1:
>>> import matplotlib
>>> matplotlib.__version__
'2.0.2+4277.g3ecd965'
Create a log-log plot:
t = np.arange(0.0, 600.0, 2.0)
plt.loglog(t, 20 * np.exp(-t / 10.0), marker='o', markevery=0.1, linestyle='None')
Expected that:
markers will be spaced at approximately equal distances along the line
Yet, no marker was observed. When the starting point of the array was slightly shifted to the side of positive numbers, the markers appeared. Compare:
t = np.arange(0.01, 600.0, 2.0)
plt.loglog(t, 20 * np.exp(-t / 10.0), marker='o', markevery=0.1, linestyle='None')
Note that the markers disappeared when a negative starting value was used. Moreover, other markerevery options worked out nicely.
Any idea is highly appreciated.
PS: Here is a similar question: matplotlib 1.4.2 with Seaborn: line markers not functioning
.
The logarithm of 0 is minus infinity. Minus infinity plus some value (here plus 0.1) is still minus infinity. Therefore all points that are not minus infinity are not shown. But of course the points that are at minus infinity are also not shown because minus infinity is no numerical value. In total, no point is shown at all, as expected.
If you chose the first point to give some numerical value, markevery is of course working fine.
I am struggling to 'translate' the instructions I find for Python to the use of Pyplot in Julia. This must be a simple question, but do you know how to set the number of ticks in a plot in Julia using Pyplot?
If you have
x = [1,2,3,4,5]
y = [1,3,6,8,11]
you can
PyPlot.plot(x,y)
which draws the plot
and then do
PyPlot.xticks([1,3,5])
for tics at 1,3 and 5 on the x-axis
PyPlot.yticks([1,6,11])
for tics at 1,6 and 11 on the y-axis
Tic spacing
if you want fx 4 tics and want it evenly spaced and dont mind Floats, you can do
collect(linspace(x[1], x[end], 4).
If you need the tics to be integers and you want 4 tics, you can do
collect(x[1]:div(x[end],4):x[end])
Edit
Maybe this wont belong here but atleast you'll see it...
whenever you're looking for a method that's supposed to be in a module X you can find these methods by typing in the REPL X. + TAB key
to clarify, if you want to search a module for a method you suspect starts with an x, like xticts, in the REPL (terminal/shell) do
PyPlot.x
and press TAB twice and you'll see
julia> PyPlot.x
xkcd xlabel xlim xscale xticks
and if you're not sure exactly how the method works, fx its arguments, and there isnt any help available, you can call
methods(PyPlot.xticks)
to see every "version" that method has
Bonus
The module for all the standard methods, like maximum, vcat etc is Base
After some trying and searching, I found a way to do it. One can just set the number of bins that should be on each axis. Here is a minimal example:
using PyPlot
x = linspace(0, 10, 200)
y = sin(x)
fig, ax = subplots()
ax[:plot](x, y, "r-", linewidth=2, label="sine function", alpha=0.6)
ax[:legend](loc="upper center")
ax[:locator_params](axis ="y", nbins=4)
The last line specifies the number of bins that should be used on the y-axis. Leaving the argument axis unspecified will set that option for both axis at the same value.
Plotting a figure with a colorbar, like for example the ellipse collection of the matplotlib gallery, I'm trying to understand the geometry of the figure. If I add the following code in the source code (instead of plt.show()):
cc=plt.gcf().get_children()
print(cc[1].get_geometry())
print(cc[2].get_geometry())
I get
(1, 2, 1)
(3, 1, 2)
I understand the first one - 1 row, two columns, plot first (and presumably the second is the colorbar), but I don't understand the second one, which I would expect to be (1,2,2). What do these values correspond to?
Edit: It seems that the elements in cc do not have the same axes,which would explain the discrepancies. Somehow, I'm still confused with the geometries that are reported.
What's happening is when you call colorbar, use_gridspec defaults to True which then makes a call to matplotlib.colorbar.make_axes_gridspec which then creates a 1 by 2 grid to hold the plot and cbar axes then then cbar axis itself is actually a 3 by 1 grid that has its aspect ratio adjusted
the key line in matplotlib.colorbar.make_axes_gridspec which makes this happen is
gs2 = gs_from_sp_spec(3, 1, subplot_spec=gs[1], hspace=0.,
height_ratios=wh_ratios)
because wh_ratios == [0.0, 1.0, 0.0] by default so the other two subplots above and below are 0 times the size of the middle plot.
I've put what I did to figure this out into an IPython notebook