How to set the ticks on a logarithmic axis in matplotlib - python

I'm trying to draw nice ticks (scalar not exponential) on a logarithmic y-axis in matplotlib. In general I want to include the first value (100in this example) an work from there. But in some cases I get different tickers like below. I have found no clue as how to manage this. Is there an uncomplicated way to force matplotlib to start with a specific value and automatically select sensible tickers thereafter (in this example 120, 110, 100, 90, 80, 70, 60, 50, 40, 30, 20 would be nice).
My code:
from matplotlib.ticker import ScalarFormatter, MaxNLocator
x = range(11)
y = [ 100., 91.3700879 , 91.01104689, 58.91189746,
46.99501432, 55.3816625 , 37.49715841, 26.55818469,
36.34538328, 37.7811044 , 47.45953131]
fig = plt.figure()
ax = fig.add_subplot(111)
ax.set_yscale('log')
ax.yaxis.set_major_locator(MaxNLocator(nbins=11, steps=[1,2,3,4,5,6,7,8,9,10]))
ax.yaxis.set_major_formatter(ScalarFormatter())
ax.plot(x,y)
Result:

You can use set_ylim():
ax.set_ylim(20, 120)
This could be one way to make the limits depend on the y-data instead of hard-wiring them:
ymax = round(max(y), -1) + 10
ymin = max(round(min(y), -1) - 10, 0)
ax.set_ylim(ymin, ymax)
You can force the tick locations with ax.set_yticks():
ymax = round(max(y), -1) + 20
ymin = max(round(min(y), -1) - 10, 0)
ax.set_ylim(ymin, ymax)
ax.set_yticks(range(int(ymin), int(ymax) + 1, 10))
ax.plot(x,y)
For:
y = [ 100. , 114.088362 , 91.14833261, 109.33399855, 73.34902925,
76.43091996, 56.84863363, 65.34297117, 78.99411287, 70.93280065,
55.03979689]
it produces this plot:

Related

Re-arranging order of longitude values on the x-axis

I have a time-longitude array which I am plotting using the matplotlib contourf function. My longitude values span from [-180, 180] and as such appear on the x-axis in this order.
I would like my x-axis to run from 0 degrees to 0 degrees, so my x-axis ticks would be (0, 60, 120, 180, -120, -60, 0). Is there an easy way to do this?
My current code is:
levels = np.arange(0, 5+0.5, 0.5)
lon_ticks = np.array([0, 60, 120, 180, -120, -60, 0])
for i in range(3):
fig = plt.figure(figsize = (15, 15))
ax = fig.add_subplot(1, 1, 1)
im = ax.contourf(lon,date_list,TRMM_lat_mean[:,:,i],
levels = levels, extend = 'both', cmap = 'gist_ncar')
cb = plt.colorbar(im)
plt.savefig("C:/Users/amcna/Desktop/fig{number}.png".format(number = i))
Which outputs:
!(https://imgur.com/epedcTu)
As you can see my longitude array spans from [-180, 180], however I wish it to be arranged in the order I specified above.
Since your data is cyclic, a representation through polar coordinates might work:
Example:
def f(x, y):
return np.sin(x) ** 10 + np.cos(10 + y * x) * np.cos(x)
x = np.radians([0, 60, 120, 180, -120, -60, 0])
y = np.arange(0, 5+0.5, 0.5)
X, Y = np.mesh
grid(x, y)
Z = f(X, Y)
#-- Plot... ------------------------------------------------
fig, ax = plt.subplots(subplot_kw=dict(projection='polar'))
ax.contourf(Y, X, Z)
plt.show()
If you don't want to do that, this thread might help you: Handling cyclic data with matplotlib contour/contourf

Setting color of area in Matplotlib

I'm creating a chart with matplotlib, here is my code:
fig = plt.figure(facecolor='#131722',dpi=155, figsize=(8, 4))
ax1 = plt.subplot2grid((1,2), (0,0), facecolor='#131722')
Colors = [['#0400ff', '#FF0000'], ['#09ff00', '#ff8c00']]
for x in List:
Index = List.index(x)
rate_buy = []
total_buy = []
for y in x['data']['bids']:
rate_buy.append(y[0])
total_buy.append(y[1])
rBuys = pd.DataFrame({'buy': rate_buy})
tBuys = pd.DataFrame({'total': total_buy})
ax1.plot(rBuys.buy, tBuys.total, color=Colors[Index][0], linewidth=0.5, alpha=0.8)
ax1.fill_between(rBuys.buy, 0, tBuys.total, facecolor=Colors[Index][0], alpha=1)
And here is the output:
The problem with the current output is that the colors of the two areas are "merging": basically the area BELOW the blue line should be blue, but instead it's green. How can i set it to be blue, for example, like in my example?
Example List data:
[[9665, 0.07062500000000001], [9666, 0.943708], [9667, 5.683787000000001], [9668, 9.802289], [9669, 11.763305], [9670, 14.286004], [9671, 16.180122], [9672, 23.316723000000003], [9673, 30.915156000000003], [9674, 33.44226200000001], [9675, 36.14526200000001], [9676, 45.76024100000001], [9677, 51.85294700000001], [9678, 58.79529300000001], [9679, 59.05322900000001], [9680, 60.27704500000001], [9681, 60.743885000000006], [9682, 66.75103700000001], [9683, 71.86412600000001], [9684, 73.659636], [9685, 78.08502800000001], [9686, 78.19614200000001], [9687, 79.98396400000001], [9688, 90.55855800000002]]
I guess the hint of #JohanC is correct, you are plotting in the wrong order and overlay your previous plots with new ones.
I tried to recreate a small example where total_buy1 > total_buy0, so in order to get the desired result you first have to plot total_buy1
and then total_buy0:
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
Colors = [['#0400ff', '#FF0000'],
['#09ff00', '#ff8c00']]
n = 100
rate_buy = np.linspace(0, 1000, 100)
total_buy0 = np.linspace(0, 300, n)[::-1] + np.random.normal(scale=10, size=n)
total_buy1 = np.linspace(0, 600, n)[::-1] + np.random.normal(scale=10, size=n)
ax.plot(rate_buy, total_buy1, color=Colors[1][1], linewidth=0.5, alpha=0.8)
ax.fill_between(rate_buy, 0, total_buy1, facecolor=Colors[1][0], alpha=1)
ax.plot(rate_buy, total_buy0, color=Colors[0][1], linewidth=0.5, alpha=0.8)
ax.fill_between(rate_buy, 0, total_buy0, facecolor=Colors[0][0], alpha=1)
I noticed that you use Colors[Index][0] for both plotting calls, so the line and the area will not have different colors.

Matplotlib center/align ticks in imshow plot

I have been trying to center the x and y ticks of my imshow but without success.
The desired yticks should be: [ 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000] and xticks: [ 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55] but aligned/centered. E.g. line 1 should have the 100 value exactly in the middle of the line space (middle of the yellow box/pixel).
import numpy as np
import matplotlib.pyplot as plt
X = np.random.rand(10,11)
plt.figure(dpi=130)
plt.imshow(X, cmap = 'jet', interpolation=None, extent=[5,55,1000,100], aspect='auto')
Here, the values 5 does not appear at all in the x axis.
I have also tried the following, x axis if fine but not the y axis
plt.figure(dpi=130)
X = np.random.rand(10,11)
plt.imshow(X, cmap = 'jet', interpolation=None, extent=[2.5,57.5,1000,100], aspect='auto')
ax = plt.gca()
xticks = cluster_space
yticks = space_segment
ax.set_xticks(xticks)
ax.set_yticks(yticks)
In general, to have the pixels centered, you need to set the extent to range from the lowest pixel coordinate minus half the pixel width to the highest pixel coordinate plus half the pixel width.
import numpy as np; np.random.seed(42)
import matplotlib.pyplot as plt
X = np.random.rand(10,11)
plt.figure()
centers = [5,55,1000,100]
dx, = np.diff(centers[:2])/(X.shape[1]-1)
dy, = -np.diff(centers[2:])/(X.shape[0]-1)
extent = [centers[0]-dx/2, centers[1]+dx/2, centers[2]+dy/2, centers[3]-dy/2]
plt.imshow(X, cmap = 'jet', interpolation=None, extent=extent, aspect='auto')
plt.xticks(np.arange(centers[0], centers[1]+dx, dx))
plt.yticks(np.arange(centers[3], centers[2]+dy, dy))
plt.show()

Replicate Log10 Scaling with Matplotlib

I'm trying to recreate a plot that has the y-axis styled as so:
But can't seem to figure out how to get the axis breaks and labels lined up how I want them. I am currently doing this in my code:
# plot lines
for key, group in grouped:
plt.plot(group.x * 950, np.log10(group.y), label=key)
# plot points
exp_group = exp_data.groupby('Experiment')
for key, group in exp_group:
plt.plot(group.x, np.log10(group.y), label=key, marker='o')
plt.yticks(np.arange(-3, 3), label=10.0**np.arange(-3,3))
plt.show()
A solution is to use plt.yticks:
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(0, 10, 1000)
y = np.exp(x) - x**3 + x**2 - x**(1/2)
plt.figure()
plt.semilogy(x, y)
locs, labels = plt.yticks()
print(locs)
print(labels)
lst_10 = [1, 10, 100, 1000, 10000]
plt.yticks(lst_10, lst_10)
plt.title('Different label on y-axis')
plt.figure()
plt.semilogy(x, y)
plt.title('Default label')
plt.show()
The function plt.yticks takes two arguments, the locations and the labels. I want the labels in the locations 1, 10, 100, 1000, 10000; I want the label (in location 1) to have a label 1, the label (in localtion 10) to have a label 10, and so on. I have also used plt.semilogy to get that semi-log axis.

How to plot bar graphs with same X coordinates side by side ('dodged')

import matplotlib.pyplot as plt
gridnumber = range(1,4)
b1 = plt.bar(gridnumber, [0.2, 0.3, 0.1], width=0.4,
label="Bar 1", align="center")
b2 = plt.bar(gridnumber, [0.3, 0.2, 0.2], color="red", width=0.4,
label="Bar 2", align="center")
plt.ylim([0,0.5])
plt.xlim([0,4])
plt.xticks(gridnumber)
plt.legend()
plt.show()
Currently b1 and b2 overlap each other. How do I plot them separately like so:
There is an example in the matplotlib site. Basically, you just shift the x values by width. Here is the relevant bit:
import numpy as np
import matplotlib.pyplot as plt
N = 5
menMeans = (20, 35, 30, 35, 27)
menStd = (2, 3, 4, 1, 2)
ind = np.arange(N) # the x locations for the groups
width = 0.35 # the width of the bars
fig = plt.figure()
ax = fig.add_subplot(111)
rects1 = ax.bar(ind, menMeans, width, color='royalblue', yerr=menStd)
womenMeans = (25, 32, 34, 20, 25)
womenStd = (3, 5, 2, 3, 3)
rects2 = ax.bar(ind+width, womenMeans, width, color='seagreen', yerr=womenStd)
# add some
ax.set_ylabel('Scores')
ax.set_title('Scores by group and gender')
ax.set_xticks(ind + width / 2)
ax.set_xticklabels( ('G1', 'G2', 'G3', 'G4', 'G5') )
ax.legend( (rects1[0], rects2[0]), ('Men', 'Women') )
plt.show()
Below answer will explain each and every line of code in the simplest manner possible:
# Numbers of pairs of bars you want
N = 3
# Data on X-axis
# Specify the values of blue bars (height)
blue_bar = (23, 25, 17)
# Specify the values of orange bars (height)
orange_bar = (19, 18, 14)
# Position of bars on x-axis
ind = np.arange(N)
# Figure size
plt.figure(figsize=(10,5))
# Width of a bar
width = 0.3
# Plotting
plt.bar(ind, blue_bar , width, label='Blue bar label')
plt.bar(ind + width, orange_bar, width, label='Orange bar label')
plt.xlabel('Here goes x-axis label')
plt.ylabel('Here goes y-axis label')
plt.title('Here goes title of the plot')
# xticks()
# First argument - A list of positions at which ticks should be placed
# Second argument - A list of labels to place at the given locations
plt.xticks(ind + width / 2, ('Xtick1', 'Xtick3', 'Xtick3'))
# Finding the best position for legends and putting it
plt.legend(loc='best')
plt.show()
Sometimes could be tricky to find the right bar width. I usually use this np.diff to find the right dimension.
import numpy as np
import matplotlib.pyplot as plt
#The data
womenMeans = (25, 32, 34, 20, 25)
menMeans = (20, 35, 30, 35, 27)
indices = [5.5,6,7,8.5,8.9]
#Calculate optimal width
width = np.min(np.diff(indices))/3
fig = plt.figure()
ax = fig.add_subplot(111)
# matplotlib 3.0 you have to use align
ax.bar(indices-width,womenMeans,width,color='b',label='-Ymin',align='edge')
ax.bar(indices,menMeans,width,color='r',label='Ymax',align='edge')
ax.set_xlabel('Test histogram')
plt.show()
# matplotlib 2.0 (you could avoid using align)
# ax.bar(indices-width,womenMeans,width,color='b',label='-Ymin')
# ax.bar(indices,menMeans,width,color='r',label='Ymax')
This is the result:
What if my indices on my x axis are nominal values like names:
#
import numpy as np
import matplotlib.pyplot as plt
# The data
womenMeans = (25, 32, 34, 20, 25)
menMeans = (20, 35, 30, 35, 27)
indices = range(len(womenMeans))
names = ['Asian','European','North Amercian','African','Austrailian','Martian']
# Calculate optimal width
width = np.min(np.diff(indices))/3.
fig = plt.figure()
ax = fig.add_subplot(111)
ax.bar(indices-width/2.,womenMeans,width,color='b',label='-Ymin')
ax.bar(indices+width/2.,menMeans,width,color='r',label='Ymax')
#tiks = ax.get_xticks().tolist()
ax.axes.set_xticklabels(names)
ax.set_xlabel('Test histogram')
plt.show()
Here are two examples of creating a side-by-side bar chart when you have more than two "categories" in a group.
Manual Method
Manually set the position and width of each bar.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import ticker
coins = ['penny', 'nickle', 'dime', 'quarter']
worth = np.array([.01, .05, .10, .25])
# Coin values times *n* coins
# This controls how many bars we get in each group
values = [worth*i for i in range(1,6)]
n = len(values) # Number of bars to plot
w = .15 # With of each column
x = np.arange(0, len(coins)) # Center position of group on x axis
for i, value in enumerate(values):
position = x + (w*(1-n)/2) + i*w
plt.bar(position, value, width=w, label=f'{i+1}x')
plt.xticks(x, coins);
plt.ylabel('Monetary Value')
plt.gca().yaxis.set_major_formatter(ticker.FormatStrFormatter('$%.2f'))
plt.legend()
Pandas Method
If you put the data into a pandas DataFrame, pandas will do the hard stuff for you.
import pandas as pd
coins = ['penny', 'nickle', 'dime', 'quarter']
worth = [0.01, 0.05, 0.10, 0.25]
df = pd.DataFrame(worth, columns=['1x'], index=coins)
df['2x'] = df['1x'] * 2
df['3x'] = df['1x'] * 3
df['4x'] = df['1x'] * 4
df['5x'] = df['1x'] * 5
from matplotlib import ticker
import matplotlib.pyplot as plt
df.plot(kind='bar')
plt.ylabel('Monetary Value')
plt.gca().yaxis.set_major_formatter(ticker.FormatStrFormatter('$%.2f'))
plt.gca().xaxis.set_tick_params(rotation=0)
Pandas creates a similar figure...

Categories