How to set the xtick position for secondary axis in matplotlib? - python

I want to create a secondary xaxis at the top which has an inverse relation with the major xaxis at the bottom. I followed the official tutorial here and have the following codes:
def forward(x):
return 10/x
def backward(y):
return 10/y
fig, ax = plt.subplots()
ax.set_xlim([0.14, 1.4])
secax = ax.secondary_xaxis('top', functions=(forward, backward))
secax.set_xticks(np.array([10,20,40,70])) # does not work!
plt.show()
The problem is that the xticks at the top are not at the right place. They are bunched together in the left due to the inverse function applied. How do I manually set the position of the xticks? (e.g. at 10,20,40,70)
Edit:
Just to make it more clear, the ticks are at the right place, but there are too many tickss as shown in the figure. In this case, I only want the ticks at 10, 20, 40, 70 (I don't want the ticks at 30, 50 and 60 as we can't see all the tick numbers clearly)

I believe either you missed import statement for numpy or you need to update you matplotlib. Below works fine for me -
import matplotlib.pyplot as plt
import numpy as np
def forward(x):
return 10/x
def backward(y):
return 10/y
fig, ax = plt.subplots()
ax.set_xlim([0.14, 1.4])
secax = ax.secondary_xaxis('top', functions=(forward, backward))
secax.set_xticks(np.array([10,20,40,70])) # does not work!
plt.show()
Check your version -
import matplotlib
print (matplotlib.__version__)
If above doesn't print 3.2.1. try below -
pip install matplotlib==3.2.1

It is not clear what you want to achieve.
If you want a linear relationship at the top, this might be relevant:
import matplotlib.pyplot as plt
import numpy as np
fig, ax = plt.subplots()
ax.set_xlim([0.14, 1.4])
secax = ax.secondary_xaxis('top', functions=(lambda x: 77 - 50 * x,
lambda y: (77 - y) / 50))
secax.set_xticks(np.array([10, 20, 40, 70]))
plt.show()

Related

Matplotlib + pandas change xtick label frequency when using period[Q-DEC] [duplicate]

I am trying to fix how python plots my data.
Say:
x = [0,5,9,10,15]
y = [0,1,2,3,4]
matplotlib.pyplot.plot(x,y)
matplotlib.pyplot.show()
The x axis' ticks are plotted in intervals of 5. Is there a way to make it show intervals of 1?
You could explicitly set where you want to tick marks with plt.xticks:
plt.xticks(np.arange(min(x), max(x)+1, 1.0))
For example,
import numpy as np
import matplotlib.pyplot as plt
x = [0,5,9,10,15]
y = [0,1,2,3,4]
plt.plot(x,y)
plt.xticks(np.arange(min(x), max(x)+1, 1.0))
plt.show()
(np.arange was used rather than Python's range function just in case min(x) and max(x) are floats instead of ints.)
The plt.plot (or ax.plot) function will automatically set default x and y limits. If you wish to keep those limits, and just change the stepsize of the tick marks, then you could use ax.get_xlim() to discover what limits Matplotlib has already set.
start, end = ax.get_xlim()
ax.xaxis.set_ticks(np.arange(start, end, stepsize))
The default tick formatter should do a decent job rounding the tick values to a sensible number of significant digits. However, if you wish to have more control over the format, you can define your own formatter. For example,
ax.xaxis.set_major_formatter(ticker.FormatStrFormatter('%0.1f'))
Here's a runnable example:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker
x = [0,5,9,10,15]
y = [0,1,2,3,4]
fig, ax = plt.subplots()
ax.plot(x,y)
start, end = ax.get_xlim()
ax.xaxis.set_ticks(np.arange(start, end, 0.712123))
ax.xaxis.set_major_formatter(ticker.FormatStrFormatter('%0.1f'))
plt.show()
Another approach is to set the axis locator:
import matplotlib.ticker as plticker
loc = plticker.MultipleLocator(base=1.0) # this locator puts ticks at regular intervals
ax.xaxis.set_major_locator(loc)
There are several different types of locator depending upon your needs.
Here is a full example:
import matplotlib.pyplot as plt
import matplotlib.ticker as plticker
x = [0,5,9,10,15]
y = [0,1,2,3,4]
fig, ax = plt.subplots()
ax.plot(x,y)
loc = plticker.MultipleLocator(base=1.0) # this locator puts ticks at regular intervals
ax.xaxis.set_major_locator(loc)
plt.show()
I like this solution (from the Matplotlib Plotting Cookbook):
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker
x = [0,5,9,10,15]
y = [0,1,2,3,4]
tick_spacing = 1
fig, ax = plt.subplots(1,1)
ax.plot(x,y)
ax.xaxis.set_major_locator(ticker.MultipleLocator(tick_spacing))
plt.show()
This solution give you explicit control of the tick spacing via the number given to ticker.MultipleLocater(), allows automatic limit determination, and is easy to read later.
In case anyone is interested in a general one-liner, simply get the current ticks and use it to set the new ticks by sampling every other tick.
ax.set_xticks(ax.get_xticks()[::2])
if you just want to set the spacing a simple one liner with minimal boilerplate:
plt.gca().xaxis.set_major_locator(plt.MultipleLocator(1))
also works easily for minor ticks:
plt.gca().xaxis.set_minor_locator(plt.MultipleLocator(1))
a bit of a mouthfull, but pretty compact
This is a bit hacky, but by far the cleanest/easiest to understand example that I've found to do this. It's from an answer on SO here:
Cleanest way to hide every nth tick label in matplotlib colorbar?
for label in ax.get_xticklabels()[::2]:
label.set_visible(False)
Then you can loop over the labels setting them to visible or not depending on the density you want.
edit: note that sometimes matplotlib sets labels == '', so it might look like a label is not present, when in fact it is and just isn't displaying anything. To make sure you're looping through actual visible labels, you could try:
visible_labels = [lab for lab in ax.get_xticklabels() if lab.get_visible() is True and lab.get_text() != '']
plt.setp(visible_labels[::2], visible=False)
This is an old topic, but I stumble over this every now and then and made this function. It's very convenient:
import matplotlib.pyplot as pp
import numpy as np
def resadjust(ax, xres=None, yres=None):
"""
Send in an axis and I fix the resolution as desired.
"""
if xres:
start, stop = ax.get_xlim()
ticks = np.arange(start, stop + xres, xres)
ax.set_xticks(ticks)
if yres:
start, stop = ax.get_ylim()
ticks = np.arange(start, stop + yres, yres)
ax.set_yticks(ticks)
One caveat of controlling the ticks like this is that one does no longer enjoy the interactive automagic updating of max scale after an added line. Then do
gca().set_ylim(top=new_top) # for example
and run the resadjust function again.
I developed an inelegant solution. Consider that we have the X axis and also a list of labels for each point in X.
Example:
import matplotlib.pyplot as plt
x = [0,1,2,3,4,5]
y = [10,20,15,18,7,19]
xlabels = ['jan','feb','mar','apr','may','jun']
Let's say that I want to show ticks labels only for 'feb' and 'jun'
xlabelsnew = []
for i in xlabels:
if i not in ['feb','jun']:
i = ' '
xlabelsnew.append(i)
else:
xlabelsnew.append(i)
Good, now we have a fake list of labels. First, we plotted the original version.
plt.plot(x,y)
plt.xticks(range(0,len(x)),xlabels,rotation=45)
plt.show()
Now, the modified version.
plt.plot(x,y)
plt.xticks(range(0,len(x)),xlabelsnew,rotation=45)
plt.show()
Pure Python Implementation
Below's a pure python implementation of the desired functionality that handles any numeric series (int or float) with positive, negative, or mixed values and allows for the user to specify the desired step size:
import math
def computeTicks (x, step = 5):
"""
Computes domain with given step encompassing series x
# params
x - Required - A list-like object of integers or floats
step - Optional - Tick frequency
"""
xMax, xMin = math.ceil(max(x)), math.floor(min(x))
dMax, dMin = xMax + abs((xMax % step) - step) + (step if (xMax % step != 0) else 0), xMin - abs((xMin % step))
return range(dMin, dMax, step)
Sample Output
# Negative to Positive
series = [-2, 18, 24, 29, 43]
print(list(computeTicks(series)))
[-5, 0, 5, 10, 15, 20, 25, 30, 35, 40, 45]
# Negative to 0
series = [-30, -14, -10, -9, -3, 0]
print(list(computeTicks(series)))
[-30, -25, -20, -15, -10, -5, 0]
# 0 to Positive
series = [19, 23, 24, 27]
print(list(computeTicks(series)))
[15, 20, 25, 30]
# Floats
series = [1.8, 12.0, 21.2]
print(list(computeTicks(series)))
[0, 5, 10, 15, 20, 25]
# Step – 100
series = [118.3, 293.2, 768.1]
print(list(computeTicks(series, step = 100)))
[100, 200, 300, 400, 500, 600, 700, 800]
Sample Usage
import matplotlib.pyplot as plt
x = [0,5,9,10,15]
y = [0,1,2,3,4]
plt.plot(x,y)
plt.xticks(computeTicks(x))
plt.show()
Notice the x-axis has integer values all evenly spaced by 5, whereas the y-axis has a different interval (the matplotlib default behavior, because the ticks weren't specified).
Generalisable one liner, with only Numpy imported:
ax.set_xticks(np.arange(min(x),max(x),1))
Set in the context of the question:
import numpy as np
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
x = [0,5,9,10,15]
y = [0,1,2,3,4]
ax.plot(x,y)
ax.set_xticks(np.arange(min(x),max(x),1))
plt.show()
How it works:
fig, ax = plt.subplots() gives the ax object which contains the axes.
np.arange(min(x),max(x),1) gives an array of interval 1 from the min of x to the max of x. This is the new x ticks that we want.
ax.set_xticks() changes the ticks on the ax object.
xmarks=[i for i in range(1,length+1,1)]
plt.xticks(xmarks)
This worked for me
if you want ticks between [1,5] (1 and 5 inclusive) then replace
length = 5
Since None of the above solutions worked for my usecase, here I provide a solution using None (pun!) which can be adapted to a wide variety of scenarios.
Here is a sample piece of code that produces cluttered ticks on both X and Y axes.
# Note the super cluttered ticks on both X and Y axis.
# inputs
x = np.arange(1, 101)
y = x * np.log(x)
fig = plt.figure() # create figure
ax = fig.add_subplot(111)
ax.plot(x, y)
ax.set_xticks(x) # set xtick values
ax.set_yticks(y) # set ytick values
plt.show()
Now, we clean up the clutter with a new plot that shows only a sparse set of values on both x and y axes as ticks.
# inputs
x = np.arange(1, 101)
y = x * np.log(x)
fig = plt.figure() # create figure
ax = fig.add_subplot(111)
ax.plot(x, y)
ax.set_xticks(x)
ax.set_yticks(y)
# which values need to be shown?
# here, we show every third value from `x` and `y`
show_every = 3
sparse_xticks = [None] * x.shape[0]
sparse_xticks[::show_every] = x[::show_every]
sparse_yticks = [None] * y.shape[0]
sparse_yticks[::show_every] = y[::show_every]
ax.set_xticklabels(sparse_xticks, fontsize=6) # set sparse xtick values
ax.set_yticklabels(sparse_yticks, fontsize=6) # set sparse ytick values
plt.show()
Depending on the usecase, one can adapt the above code simply by changing show_every and using that for sampling tick values for X or Y or both the axes.
If this stepsize based solution doesn't fit, then one can also populate the values of sparse_xticks or sparse_yticks at irregular intervals, if that is what is desired.
You can loop through labels and show or hide those you want:
for i, label in enumerate(ax.get_xticklabels()):
if i % interval != 0:
label.set_visible(False)

Is there a way to properly invert the y-axis in Python's 3DAxes plot_surface without it interfering with the x-axis?

I'm using 3DAxes in Python to plot a surface plot. Im trying to manipulate the y-axis to go from [25 - 0] instead of going from [0-25] in the plot itself. So far I've tried to just use ax.invert_yaxis(), but my problem is that this changes the x-axis to have the same numerical values as the y-axis. My suspiscion is that this is because I use np.meshgrid earlier in the code.
Does anyone know how to invert just the y-axis?
I have this:
I want to change so the y-axis goes from 25 to 0, and naturally the surfaceplot will change as well then.
But when I use invert_yaxis I get this:
Which is wrong cause the x-axis is changed in numerically values as well...
CODE:
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits import mplot3d
Tcm_0 = 17.9 #K
def t(T):
return T/Tcm_0
my_0 = 1.25663706212e-6
Hcm_0 = 29.7 / my_0 #A/m
def Hc2(T):
return Hcm_0 * (1 - np.power(t(T),1.52))
def h(H, T):
return H/Hc2(T)
C_1 = 350e3 #AT/mm^2
def Jc(H, T):
return (C_1/(my_0*H))*(1 - np.power(t(T),1.52))*(1 - np.power(t(T),2))*np.power(h(H,T),0.5)*np.power(1-h(H,T),2)
fig = plt.figure()
ax = plt.axes(projection='3d')
T = np.linspace(0,17,100)
B = np.linspace(0.1,28,100)
H = B/my_0
T, H = np.meshgrid(T,H)
J = Jc(H,T)
B = H*my_0
surface = ax.plot_surface(T,B,J, cmap='winter', linewidth=0)
ax.set_xbound(0, Tcm_0)
ax.set_xlabel('Temperature [K]')
ax.set_ybound(0, Hcm_0*my_0)
ax.invert_yaxis() <- This us what I dont get to work.
plt.show()
You can do it by manually setting the limits for the axis, no need to invert anything.
ax.set_ylim(25, 0)

Matplotlib : How to populate the below chart having all the x-axis labels and grid lines accordingly?

data = {'tenor': ['1w','1m','3m','6m','12m','1y','2y','3y','4y','5y','6y','7y','10y','15y','20y','25y','30y','40y','50y'],'rate_s': [0.02514, 0.026285, 0.0273, 0.0279, 0.029616, 0.026526, 0.026028, 0.024, 0.025958,0.0261375, 0.026355, 0.026, 0.026898, 0.0271745, 0.02741, 0.027, 0.0275, 0.0289,0.0284],'rate_t':[ 0.02314, 0.024285, 0.0253,0.0279, 0.028616, 0.026526,0.027028, 0.024, 0.025958,0.0271375, 0.02355, 0.026, 0.024898, 0.0271745, 0.02641,0.027, 0.0255, 0.0289,0.0284]}
I want to produce the chart in blue with the same format like below. I tried this piece of code but results are not satisfactory (chart in white). It also not showing all x-axis labels. Please suggest.
ax = plt.gca()
df.plot(kind='line',x='tenor',y='rate_s',marker='o',color='green',ax=ax)
df.plot(kind='line',x='tenor',y='rate_y',marker='o', color='red', ax=ax)
ax.minorticks_on()
ax.grid(which='major',linestyle='-', linewidth='0.5', color='blue')
ax.grid(which='minor', linestyle=':', linewidth='0.5', color='black')
plt.show()
This is following the discussions in the comments.
There are a couple parts, the full example is at the bottom.
Style
One of your questions was how to change the style of the plot. This can be done with the following code:
import matplotlib.pyplot as plt
plt.style.use('seaborn-darkgrid')
there are many possible styles, and you can even create your own style if you wish. To see all possible styles see: the documentation. To list all styles use plt.style.available
Custom Ticker
For the custom tickers: you can use FixedLocator or if you know it is log or symlog, then matplotlib has a built-in locator. See the matplotlib doc for scales
You can use FixedLocator to set up the axis, to be separated. i.e. the following code will give you what you want.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker
X = np.arange(0, 2000)
Y = np.arange(0, 2000)
def convert(date):
if 'w' in date:
return 7*int(date[:-1])
if 'm' in date:
return 30*int(date[:-1])
if 'y' in date:
return 30*int(date[:-1]) + 360
ticks = [convertdate(d) for d in tenor]
plt.style.use('seaborn-darkgrid')
ax = plt.axes()
t = ticker.FixedLocator(locs=ticks)
ax.xaxis.set_ticklabels(tenor)
ax.xaxis.set_major_locator(t)
# ax.xaxis.set_minor_locator(ticker.MultipleLocator(3))
plt.plot(X, Y, c = 'k')
plt.show()
Which produces:
Specific Case
For your specific case, you probably want the custom tickers to be on a specific interval (i.e. smallest of rate_t, biggest of rate_t).
Thus you would need to change the convert function to be as following:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker
x = data['rate_t']
y = data['rate_s']
def get_indices(date):
if 'w' in date:
return 7*int(date[:-1])
if 'm' in date:
return 30*int(date[:-1])
if 'y' in date:
return 30*int(date[:-1]) + 360
def convert(indices):
x = np.linspace(min(data['rate_t']), max(data['rate_t']), indices[-1] + 1)
return x[indices]
indices = [get_indices(d) for d in tenor]
ticks = convert(indices)
plt.style.use('seaborn-darkgrid')
ax = plt.axes()
t = ticker.FixedLocator(locs=ticks)
ax.xaxis.set_ticklabels(tenor)
ax.xaxis.set_major_locator(t)
# ax.xaxis.set_minor_locator(ticker.MultipleLocator(3))
plt.plot(x, y, c = 'k')
plt.show()
(assuming the data['rate_s'] and data['rate_t'] are as is and without processing)
Which would produce this:
Let me know if you have any questions.

How to create a basic legend to a multicolored line?

I am currently finishing a bigger project and the last part is to add a simple legend to a plot of a multicolored line. The line only contains two different colors.
The following image shows the plot when created.
The next image shows the same plot with higher resolution.
The plot displays the distance between Earth and Mars over time. For the months March to August the line is orange, for the other months it's blue. The legend should come in a simple box in the upper right corner of the plot showing a label each for the used colors. Something like this would be nice.
The data for the plot comes from a huge matrix I named master_array. It contains a lot more information that is necessary for some tasks prior to show the plot this question is regarding to.
Important for the plot I am struggling with are the columns 0, 1 and 6 which are containing the date, distance between the planets at related date and in column 6 I set a flag to determine whether the given point belongs to the 'March to August' set or not (0 is for Sep-Feb / "winter", 1 is for Mar-Aug / "summer"). The master_array is a numpy array, dtype is float64. It contains approximately 45k data points.
It looks like:
In [3]: master_array
Out[3]:
array([[ 1.89301010e+07, 1.23451036e+00, -8.10000000e+00, ...,
1.00000000e+00, 1.00000000e+00, 1.89300000e+03],
[ 1.89301020e+07, 1.24314818e+00, -8.50000000e+00, ...,
2.00000000e+00, 1.00000000e+00, 1.89300000e+03],
[ 1.89301030e+07, 1.25179997e+00, -9.70000000e+00, ...,
3.00000000e+00, 1.00000000e+00, 1.89300000e+03],
...,
[ 2.01903100e+07, 1.84236878e+00, 7.90000000e+00, ...,
1.00000000e+01, 3.00000000e+00, 2.01900000e+03],
[ 2.01903110e+07, 1.85066892e+00, 5.50000000e+00, ...,
1.10000000e+01, 3.00000000e+00, 2.01900000e+03],
[ 2.01903120e+07, 1.85894904e+00, 9.40000000e+00, ...,
1.20000000e+01, 3.00000000e+00, 2.01900000e+03]])
This is the function to get the plot I described in the beginning:
def md_plot3(dt64=np.array, md=np.array, swFilter=np.array):
""" noch nicht fertig """
y, m, d = dt64.astype(int) // np.c_[[10000, 100, 1]] % np.c_[[10000, 100, 100]]
dt64 = y.astype('U4').astype('M8') + (m-1).astype('m8[M]') + (d-1).astype('m8[D]')
cmap = ListedColormap(['b','darkorange'])
plt.figure('zeitlich-global betrachtet')
plt.title("Marsdistanz unter Berücksichtigung der Halbjahre der steigenden und sinkenden Temperaturen",
loc='left', wrap=True)
plt.xlabel("Zeit in Jahren\n")
plt.xticks(rotation = 45)
plt.ylabel("Marsdistanz in AE\n(1 AE = 149.597.870,7 km)")
# plt.legend(loc='upper right', frameon=True) # worked formerly
ax=plt.gca()
plt.style.use('seaborn-whitegrid')
#convert dates to numbers first
inxval = mdates.date2num(dt64)
points = np.array([inxval, md]).T.reshape(-1,1,2)
segments = np.concatenate([points[:-1],points[1:]], axis=1)
lc = LineCollection(segments, cmap=cmap, linewidth=3)
# set color to s/w values
lc.set_array(swFilter)
ax.add_collection(lc)
loc = mdates.AutoDateLocator()
ax.xaxis.set_major_locator(loc)
ax.xaxis.set_major_formatter(mdates.AutoDateFormatter(loc))
ax.autoscale_view()
In the bigger script there is also another function (scatter plot) to mark the minima and maxima of the curve, but I guess this is not so important here.
I already tried this resulting in a legend, that shows a vertical colorbar and only one label and also both options described in the answers to this question because it looks more like what I am aiming for but couldn't make it work for my case.
Maybe I should add that I am only a beginner in python, this is my first project so I am not familiar with the deeper functionality of matplotlib what is probably the reason why I am not able to customize the mentioned answers to get it to work in my case.
UPDATE
Thanks to the help of the user ImportanceOfBeingErnest I made some improvements:
import matplotlib.dates as mdates
from matplotlib.collections import LineCollection
from matplotlib.colors import ListedColormap
from matplotlib.lines import Line2D
def md_plot4(dt64=np.array, md=np.array, swFilter=np.array):
y, m, d = dt64.astype(int) // np.c_[[10000, 100, 1]] % np.c_[[10000, 100, 100]]
dt64 = y.astype('U4').astype('M8') + (m-1).astype('m8[M]') + (d-1).astype('m8[D]')
z = np.unique(swFilter)
cmap = ListedColormap(['b','darkorange'])
fig = plt.figure('Test')
plt.title("Test", loc='left', wrap=True)
plt.xlabel("Zeit in Jahren\n")
plt.xticks(rotation = 45)
plt.ylabel("Marsdistanz in AE\n(1 AE = 149.597.870,7 km)")
# plt.legend(loc='upper right', frameon=True) # worked formerly
ax=plt.gca()
plt.style.use('seaborn-whitegrid')
#plt.style.use('classic')
#convert dates to numbers first
inxval = mdates.date2num(dt64)
points = np.array([inxval, md]).T.reshape(-1,1,2)
segments = np.concatenate([points[:-1],points[1:]], axis=1)
lc = LineCollection(segments, array=z, cmap=plt.cm.get_cmap(cmap),
linewidth=3)
# set color to s/w values
lc.set_array(swFilter)
ax.add_collection(lc)
fig.colorbar(lc)
loc = mdates.AutoDateLocator()
ax.xaxis.set_major_locator(loc)
ax.xaxis.set_major_formatter(mdates.AutoDateFormatter(loc))
ax.autoscale_view()
def make_proxy(zvalue, scalar_mappable, **kwargs):
color = scalar_mappable.cmap(scalar_mappable.norm(zvalue))
return Line2D([0, 1], [0, 1], color=color, **kwargs)
proxies = [make_proxy(item, lc, linewidth=2) for item in z]
ax.legend(proxies, ['Winter', 'Summer'])
plt.show()
md_plot4(dt64, md, swFilter)
+What is good about it:
Well it shows a legend and it shows the right colors according to the labels.
-What is still to optimize:
1) The legend is not in a box and the 'lines' of the legend are interfering with the bottom layers of the plot. As the user ImportanceOfBeingErnest stated out this is caused by using plt.style.use('seaborn-whitegrid'). So if there's a way to use plt.style.use('seaborn-whitegrid') together with the legend style of plt.style.use('classic') that might would help.
2) The bigger issue is the colorbar. I added the fig.colorbar(lc) line to the original code to achieve what I was looking for according to this answer.
So I tried some other changes:
I used the plt.style.use('classic') to get a legend in the way I need it but this costs me the nice style of plt.style.use('seaborn-whitegrid') as mentioned before. Moreover I disabled the colorbar line I added prior according to the mentioned answer.
This is what I got:
import matplotlib.dates as mdates
from matplotlib.collections import LineCollection
from matplotlib.colors import ListedColormap
from matplotlib.lines import Line2D
def md_plot4(dt64=np.array, md=np.array, swFilter=np.array):
y, m, d = dt64.astype(int) // np.c_[[10000, 100, 1]] % np.c_[[10000, 100, 100]]
dt64 = y.astype('U4').astype('M8') + (m-1).astype('m8[M]') + (d-1).astype('m8[D]')
z = np.unique(swFilter)
cmap = ListedColormap(['b','darkorange'])
#fig =
plt.figure('Test')
plt.title("Test", loc='left', wrap=True)
plt.xlabel("Zeit in Jahren\n")
plt.xticks(rotation = 45)
plt.ylabel("Marsdistanz in AE\n(1 AE = 149.597.870,7 km)")
# plt.legend(loc='upper right', frameon=True) # worked formerly
ax=plt.gca()
#plt.style.use('seaborn-whitegrid')
plt.style.use('classic')
#convert dates to numbers first
inxval = mdates.date2num(dt64)
points = np.array([inxval, md]).T.reshape(-1,1,2)
segments = np.concatenate([points[:-1],points[1:]], axis=1)
lc = LineCollection(segments, array=z, cmap=plt.cm.get_cmap(cmap),
linewidth=3)
# set color to s/w values
lc.set_array(swFilter)
ax.add_collection(lc)
#fig.colorbar(lc)
loc = mdates.AutoDateLocator()
ax.xaxis.set_major_locator(loc)
ax.xaxis.set_major_formatter(mdates.AutoDateFormatter(loc))
ax.autoscale_view()
def make_proxy(zvalue, scalar_mappable, **kwargs):
color = scalar_mappable.cmap(scalar_mappable.norm(zvalue))
return Line2D([0, 1], [0, 1], color=color, **kwargs)
proxies = [make_proxy(item, lc, linewidth=2) for item in z]
ax.legend(proxies, ['Winter', 'Summer'])
plt.show()
md_plot4(dt64, md, swFilter)
+What is good about it:
It shows the legend in the way I need it.
It doesn't show a colorbar anymore.
-What is to optimize:
The plot isn't multicolored anymore.
Neither is the legend.
The classic style is not what I was looking for as I explained before...
So if anyone has a good advice please let me know!
I am using numpy version 1.16.2 and matplotlib version 3.0.3
To get a multicoloured plot in matplotlib, label your plots and then call the legend() function. The following sample code is taken from a link, but as links break, here's the post..
The chart used here is a line, but the same principle applies to other chart types, as you can see from this other SO answer
import matplotlib.pyplot as plt
import numpy as np
y = [2,4,6,8,10,12,14,16,18,20]
y2 = [10,11,12,13,14,15,16,17,18,19]
x = np.arange(10)
fig = plt.figure()
ax = plt.subplot(111)
ax.plot(x, y, label='$y = numbers')
ax.plot(x, y2, label='$y2 = other numbers')
plt.title('Legend inside')
ax.legend()
plt.show()
This code will show the following image (with the legend inside the chart)
Hope this helps
So here is the answer how to create a basic legend to a multicolored line, containing multiple labels for each used color and without showing a colorbar next to the plot (standard colorbar, nothing inside the legend; see update of original question for more information about the issues):
Thanks to a lot of helpful comments I figured out to add a norm to the LineCollection() to avoid ending up with a monocolored line when removing the colorbar by disabling fig.colorbar() (also see this)
The additional argument (in this case "norm") to add was norm=plt.Normalize(z.min(), z.max()), where z is the array that contains the information responsible for the different colors of the segments. Note that z only needs to hold one single element for each different color. This is why I wrapped my swFilter array, consisting of one flag per data point, into np.unique().
To get a proper legend inside a box not touching the plt.style.use(), I simply had to add the right arguments to ax.legend(). In my case a simple frameon=True did the job.
The result is the following:
Here is the code:
import matplotlib.dates as mdates
from matplotlib.collections import LineCollection
from matplotlib.colors import ListedColormap
from matplotlib.lines import Line2D
def md_plot4(dt64=np.array, md=np.array, swFilter=np.array):
y, m, d = dt64.astype(int) // np.c_[[10000, 100, 1]] % np.c_[[10000, 100, 100]]
dt64 = y.astype('U4').astype('M8') + (m-1).astype('m8[M]') + (d-1).astype('m8[D]')
z = np.unique(swFilter)
cmap = ListedColormap(['b','darkorange'])
#fig =
plt.figure('Test')
plt.title("Marsdistanz unter Berücksichtigung der Halbjahre der steigenden und sinkenden Temperaturen\n",
loc='left', wrap=True)
plt.xlabel("Zeit in Jahren\n")
plt.xticks(rotation = 45)
plt.ylabel("Marsdistanz in AE\n(1 AE = 149.597.870,7 km)")
plt.tight_layout()
ax=plt.gca()
plt.style.use('seaborn-whitegrid')
#convert dates to numbers first
inxval = mdates.date2num(dt64)
points = np.array([inxval, md]).T.reshape(-1,1,2)
segments = np.concatenate([points[:-1],points[1:]], axis=1)
lc = LineCollection(segments, array=z, cmap=plt.cm.get_cmap(cmap),
linewidth=3, norm=plt.Normalize(z.min(), z.max()))
# set color to s/w values
lc.set_array(swFilter)
ax.add_collection(lc)
loc = mdates.AutoDateLocator()
ax.xaxis.set_major_locator(loc)
ax.xaxis.set_major_formatter(mdates.AutoDateFormatter(loc))
ax.autoscale_view()
def make_proxy(zvalue, scalar_mappable, **kwargs):
color = scalar_mappable.cmap(scalar_mappable.norm(zvalue))
return Line2D([0, 1], [0, 1], color=color, **kwargs)
proxies = [make_proxy(item, lc, linewidth=2) for item in z]
ax.legend(proxies, ['Halbjahr der sinkenden \nTemperaturen',
'Halbjahr der steigenden \nTemperaturen'], frameon=True)
plt.show()
md_plot4(dt64, md, swFilter)
Note that I added plt.tight_layout() to ensure the title of the plot and the description of the axes are shown without any cut-offs in the window mode.
New issue now (resulting from adding tight_layout()) is that the plot gets horizontal compressed, even though there is much space available on the right side of the plot (the place where a colorbar would appear when called).
This requires another fix but currently I don't know how. So if anyone knows how to prevent the plots title and description of the axes from getting cut-off in window mode, I would be very grateful if you leave a comment.

How to remove axis in pyplot.bar?

Is there any possibility to do a bar plot without y-(x-)axis? In presentations all redundant informations have to be erased, so I would like to begin to delete the axis. I did not see helpful informations in the matplotlib documentation. Maybe you have better solutions than pyplot..?
Edit: I would like to have lines around the bars except the axis at the bottom. Is this possible
#!/usr/bin/env python
import matplotlib.pyplot as plt
ind = (1,2,3)
width = 0.8
fig = plt.figure(1)
p1 = plt.bar(ind,ind)
# plt.show()
fig.savefig("test.svg")
Edit: I did not see using plt.show()
that there is still the yaxis without ticks.
To make the axes not visible, try something like
import matplotlib.pyplot as plt
ind = (1,2,3)
width = 0.8
fig,a = plt.subplots()
p1 = a.bar(ind,ind)
a.xaxis.set_visible(False)
a.yaxis.set_visible(False)
plt.show()
Is this what you meant?
Here is the code I used at the end. It is not minimal anymore. Maybe it helps.
import matplotlib.pyplot as plt
import numpy as np
def adjust_spines(ax,spines):
for loc, spine in ax.spines.items():
if loc in spines:
spine.set_smart_bounds(True)
else:
spine.set_color('none') # don't draw spine
# turn off ticks where there is no spine
if 'left' in spines:
ax.yaxis.set_ticks_position('left')
else:
# no yaxis ticks
ax.yaxis.set_ticks([])
def nbar(samples, data, err, bWidth=0.4, bSafe=True, svgName='out'):
fig,a = plt.subplots(frameon=False)
if len(data)!=len(samples):
print("length(data) must be equal to length(samples)!")
return
ticks = np.arange(len(data))
p1 = plt.bar(ticks, data, bWidth, yerr=err)
plt.xticks(ticks+bWidth/2., samples )
adjust_spines(a,['bottom'])
a.xaxis.tick_bottom()
if bSafe:
fig.savefig(svgName+".svg")
samples = ('Sample1', 'Sample2','Sample3')
qyss = (91, 44, 59)
qysserr = (1,5,4)
nbar(samples,qyss,qysserr,svgName="test")
Thx to all contributors.

Categories