Smoother grid in a plot - python

I'm trying to plot a smoother grid in the background of this grid that's already plotted. This is what I've done so far. The grid follows my major ticks. I'd like this smoother grid to follow the minor ticks. Is this possible?
My code until now:
fig, ax = plt.subplots(figsize = (20,10))
ax.set_xticks(np.arange(0,round(max(datax)+1)))
ax.set_yticks(np.arange(0,round(max(datay)+1),step = 0.1))
ax.minorticks_on()
ax.grid(True)
plt.xlabel("Tensão (V)", fontsize = 14)
plt.ylabel("Corrente (mA)", fontsize = 14)
plt.title("Experimento 2", fontsize = 20)
ax.errorbar(datax,datay,xerr = sigmax, yerr = sigmay, fmt = ',')
ax.set(xlim= -1, ylim = 0)
P.S.: would you guys organize this code differently? I think it's a complete mess.
i want my grids to look like this
this is how they are now

What you want is the linestyle keyword argument for grid, along with the linewidth keyword argument.
Here's how you can use dotted lines for your grid, with thinner lines for the minor ticks:
ax.grid(True, which='major', linestyle=':', linewidth=1, color="black")
ax.grid(True, which='minor', linestyle=':', linewidth=0.5, color="black")
Here's the output (I used faked data since you did not provide a MWE):
You can fiddle with the linewidth parameter to have the lines appear thinner, or on the color to make them fainter.
You can also try other linestyles out, like dashed (linestyle='--').

Related

matplotlib supylabel on second axis of multiplot

I'm not finding it possible to add a second supylabel for a right-hand y-axis of a multiplot.
Can anyone please confirm 1) whether or not it can be done and/or 2)provide guidance on how?
I am trying to achieve this:
Because there are a variable number of subplots (sometimes an odd number, sometimes even) across the broader project, using subplot-level labelling to label the "middle" subplot would be problematic.
I'm presently accomplishing with figure level text. Which looks fine within python, but the right label gets cut-off by savefig. I can only get it to work if I dummy-in null ax-level y-labels " \n".
nrows = len(dftmp.GroupingCol.unique())
ncols = 1
fig, ax = plt.subplots(nrows=nrows, ncols=ncols, figsize=(14,10), constrained_layout=True,
sharex=True)
for e, ep in enumerate(dftmp.GroupingCol.unique(), start=1):
# define a figure axis and plot data
ax = plt.subplot(nrows, ncols, e)
dftmp["ValueCol"].loc[dftmp["GroupingCol"]==ep].plot(ax=ax, kind="bar", color=barcolor_lst) #, use_index=False)
# horizontal reference line (zero change)
zero_line = plt.axhline(0, color='k', linewidth=0.8)
# y-axis extent limits
ax.set_ylim([50*(-1.1), 50*1.1])
# create right-hand y-axis
ax2 = ax.twinx()
# y-axis extent limits
ax2.set_ylim([200*(-1), 200])
# null y-label placeholder to accommodate fig-level pseudo-supylabel
ax2.set_ylabel(" \n") # requires space and newline to work
# create supylabel for left-axis
supy_left = fig.supylabel("Left-hand y-axis super label", fontweight="bold") #, pad = 7)#, fontdict=fontdict) #fontweight='bold')
# use fig-level text as pseudo-supylable for right-axis
fig.text(x=0.97, y=0.5, s="Right-hand y-axis super label\n\n", size=13, fontweight='bold', rotation=270, ha='center', va='center')
# create super-label for x-axis
supx = fig.supxlabel("Bottom super label", fontweight="bold")
In the absence of the fig.text line I tried naming a second supylabel as a different object and the code runs, but doesn't produce the label.
supy_right = fig.supylabel("Cumulative net change (m^3)", fontweight="bold", position=(0.9,0.5))
I have found the suplabels to be a little unreliable in many cases, so I resort to low-level tricks in cases like these:
import matplotlib.pyplot as plt
fig = plt.figure(figsize=(4, 4))
# dummy axes 1
ax = fig.add_subplot(1, 1, 1)
ax.set_xticks([])
ax.set_yticks([])
[ax.spines[side].set_visible(False) for side in ('left', 'top', 'right', 'bottom')]
ax.patch.set_visible(False)
ax.set_xlabel('x label', labelpad=30)
ax.set_ylabel('y label left', labelpad=30)
# dummy axes 2 for right ylabel
ax = fig.add_subplot(1, 1, 1)
ax.set_xticks([])
ax.set_yticks([])
[ax.spines[side].set_visible(False) for side in ('left', 'top', 'right', 'bottom')]
ax.patch.set_visible(False)
ax.yaxis.set_label_position('right')
ax.set_ylabel('y label right', labelpad=30)
# actual data axes
num_rows = 4
for i in range(num_rows):
ax = fig.add_subplot(num_rows, 1, i + 1)
...
fig.tight_layout()
You need to adjust the labelpad values according to your liking. The rest can be taken care of by fig.tight_layout() (you might need to specify the rect though).
EDIT: having re-read your question, have you tried increasing the pad_inches value when calling savefig()?

How to plot two series with very different scales in python

I'm a beginner in python. I have to plot two graphs in the same plot. One of my graphs is velocity, which ranges between (-1,1), and the other one is groundwater, which ranges between (10,12). When I use the following code, the graphs become very small.
ax1 = plt.subplot(111)
ax2 = ax1.twinx()
df=pd.read_excel ('final-all-filters-0.6.xlsx')
df['Date']=pd.to_datetime(df['Date'])
date = df['Date']
gwl = df['gwl']
v =df['v']
plt.plot(date,gwl, color='deepskyblue',linewidth=2)
plt.plot(date,v, color='black',linewidth=2)
ax1.grid(axis='y')
ax1.xaxis.set_major_locator(matplotlib.dates.YearLocator())
ax1.xaxis.set_minor_locator(matplotlib.dates.MonthLocator((1,3,5,7,9,11)))
ax1.xaxis.set_major_formatter(matplotlib.dates.DateFormatter("\n%Y"))
ax1.xaxis.set_minor_formatter(matplotlib.dates.DateFormatter("%b"))
ax1.grid(which='minor', alpha=0.3, linestyle='--')
ax1.grid(which='major', alpha=2)
for spine in ax1.spines.values():
spine.set_edgecolor('gray')
ax1.tick_params(axis='x', which='both', colors='gray')
ax1.tick_params(axis='y', colors='gray')
ax1.set_ylabel('v', color='g')
ax2.set_ylabel('GWL', color='b')
plt.show()
But when I add the ax1.set_ylim(-1, 1)and ax2.set_ylim(10, 12) to my code, one of the graph was disappered!
I think it does plot the black graph, but it's out of range. You can check that by adding 11 or something to the black plot value.
Maybe you can try using ax2.set_yticks(np.arange(-1, 1, 0.5)) instead of set_ylim and/or using ax2.autoscale(enable=True, axis=y)

matplotlib: axes border and tick mark/label locations

The end result I'm attempting to achieve is to have a "thicker" black boarder around my plot, along xmin, xmax, ymin, & ymax. I've tried a couple of different things (such as just drawing a rectangle on the plot, see below), but I have not been able to achieve the desired results for a few reasons.
Because I cannot just use the spines (I've set 2 of them to always be at 0), I need to add some other line or rectangle to create the desired border.
By default the first and last tick labels overhang the axes. I "overcame" this by changing the horizontal or vertical alignment, but they could still use some more padding. I know this is possible, but requires a transform and is a bit clunky.
Now I'd like to remove the first and last tick marks on both axis. This is because given the way the rectangle is drawn it is always inside the plot area, but the first and last tick mark are always outside it, regardless of how thick the rectangle is. Making the rectangle thicker only causes it to overlap the first and last tick label more, which the actual tick mark remains outside the rectangle.
Any other suggestions on how to achieve this kind of border while always maintaining an axis at 0, 0 would be welcomed. That is the overall desired result.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib
from matplotlib.patches import Rectangle
X = np.random.randint(low=-9, high=9, size=10)
Y = np.random.randint(low=-9, high=9, size=10)
fig, ax = plt.subplots()
ax.axis([-10, 10, -10, 10])
ax.spines['left'].set_position('zero')
ax.spines['bottom'].set_position('zero')
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
plt.setp(ax.xaxis.get_majorticklabels()[0], ha='left')
plt.setp(ax.xaxis.get_majorticklabels()[-1], ha='right')
plt.setp(ax.yaxis.get_majorticklabels()[0], va='bottom')
plt.setp(ax.yaxis.get_majorticklabels()[-1], va='top')
patPlotBorder = ax.add_artist(Rectangle((-10, -10), 20, 20, fill=False, color='k', linewidth=2))
ax.grid(True)
fig.set_tight_layout(True)
ax.scatter(X, Y, c="b", marker="o", s=40)
plt.show()
Without changing much of your code, you can set the clip_on to False, such that the complete rectangle is shown.
border = Rectangle((-10, -10), 20, 20, fill=False, color='k', linewidth=3, clip_on=False)
ax.add_artist(border)
Since the gridlines are shown above the axes content, you have some grey line within the rectangle border.
Alternatively, you can use two axes. One with all the content and the modified spine positions etc., and one where you just make the spines bold and remove all the rest.
import numpy as np
import matplotlib.pyplot as plt
X = np.random.randint(low=-9, high=9, size=10)
Y = np.random.randint(low=-9, high=9, size=10)
fig, ax = plt.subplots()
ax2 = fig.add_subplot(111)
ax2.patch.set_visible(False)
ax2.tick_params(left=False, bottom=False, labelleft=False, labelbottom=False)
for _, sp in ax2.spines.items():
sp.set_linewidth(3)
ax.axis([-10, 10, -10, 10])
ax.spines['left'].set_position('zero')
ax.spines['bottom'].set_position('zero')
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
plt.setp(ax.xaxis.get_majorticklabels()[0], ha='left')
plt.setp(ax.xaxis.get_majorticklabels()[-1], ha='right')
plt.setp(ax.yaxis.get_majorticklabels()[0], va='bottom')
plt.setp(ax.yaxis.get_majorticklabels()[-1], va='top')
ax.grid(True)
fig.set_tight_layout(True)
ax.scatter(X, Y, c="b", marker="o", s=40)
plt.show()
You can access the individual grid lines by calling get_{x|y}gridlines(). Each grid line is an object of type Line2D, and you can change any of their properties, such as thickness, color, etc.
ax.get_xgridlines()[0].set_linewidth(5)
ax.get_xgridlines()[-1].set_linewidth(5)
ax.get_ygridlines()[0].set_linewidth(5)
ax.get_ygridlines()[-1].set_linewidth(5)

matplotlib separate ticks from spine

Is there a way to define a distance between the ticks and the spine on a line plot? I have managed to create the following (yticks are separated from the spine and the grid lines)
With the following code:
params_1 = {
'axes.spines.top': False,
'axes.spines.left': False,
'axes.spines.right': False,
'ytick.major.size': 10,
}
plt.rcParams.update(params_1)
fig = plt.figure(figsize=(13,6.5))
ax = fig.add_subplot(111, facecolor='w')
ax.set_ylim([5,15])
ax.set_xticks([5,10,15,20,25])
ax.yaxis.grid()
ax_xgrid = ax.xaxis.grid(linestyle=':', linewidth=1.5)
[i.set_marker('o') for i in ax.yaxis.get_ticklines()]
[i.set_markeredgecolor('w') for i in ax.yaxis.get_ticklines()]
[i.set_markeredgewidth(4) for i in ax.yaxis.get_ticklines()]
This looks is exactly what I would like but if is save the figure with transparency then I see the white circles around the yticks. Any ideas how to solve this?
Thanks,
If I understand correctly what you are asking, this can be easily done using the tick_params() helper function.
fig, ax = plt.subplots()
ax.spines['right'].set_visible(False)
ax.spines['top'].set_visible(False)
ax.spines['left'].set_visible(False)
ax.tick_params(axis='y', color='none', pad=50)
ax.grid()
plt.show()
EDIT I did not understand what you were trying to do. What you want to increase the distance between the left axis and the main part of the plot. To do so, use the Spine.set_position() function. The following should work:
fig, ax = plt.subplots()
ax.spines['right'].set_visible(False)
ax.spines['top'].set_visible(False)
ax.spines['left'].set_visible(False)
ax.spines['left'].set_position(('outward', 20))
[i.set_marker('o') for i in ax.yaxis.get_ticklines()]
ax.grid()
plt.show()

Matplotlib with dual scale mouse over

I try to plot some curves with matplotlib using the default gui component and have some trouble to select which of the two y-axes that the mouse over functionality should select. The default case seems to be that ax2 gets selected but I would like to use ax1 instead. Is this possible to fix in some easy way?
This is the code I use at the moment to plot my curves.
Best regards Anders Olme
delta=np.median(np.diff(measurementvalues.measvalues))
myscale=10
myrange=(measurementvalues.lowerlimit - delta*myscale, measurementvalues.upperlimit + delta*myscale)
figure = plt.figure()
ax1 = figure.add_subplot(111)
(n, bins, patches) = ax1.hist(measurementvalues.measvalues, 10, range=myrange, normed=0, facecolor='green', alpha=0.75)
ax2 = ax1.twinx()
mean = np.average(measurementvalues.measvalues)
sigma = np.std(measurementvalues.measvalues)
y = mlab.normpdf(bins, mean, sigma)
ax2.plot(bins, y, 'r-', linewidth=1)
ax1.set_xlabel('Measvlues')
ax2.set_ylabel('Probability')
ax1.set_title(r'$\mathrm{Histogram\ of\ measvalues:}\ \mu=$'+str(mean)+r'$,\ \sigma=$'+str(sigma)+r'$$')
plt.grid(True)
plt.show()
Add the following after calling twinx
ax3 = ax1.figure.add_axes(ax1.get_position(True), sharex=ax1, sharey=ax1,
frameon=False)
ax3.xaxis.set_visible(False)
ax3.yaxis.set_visible(False)
You will also need to change plt.grid(True) to ax1.grid(True)

Categories