I have the current function which generates a simple chart in matplotlib but as we see in the image the alpha parameter not seems to work, this happen in vs code, if I test this in a notebook works fine
what i need is the same format in vs code
data:
,hora,id
0,0,10
1,1,3
2,2,2
3,3,3
4,4,5
5,5,3
6,6,11
7,7,32
8,8,41
9,9,71
10,10,75
11,11,70
12,12,57
13,13,69
14,14,50
15,15,73
16,16,47
17,17,64
18,18,73
19,19,54
20,20,45
21,21,43
22,22,34
23,23,27
code:
import pandas as pd
from matplotlib import pyplot as plt
dfhoras=pd.read_clipboard(sep=',')
def questionsHour(dfhoras):
x = dfhoras['hora']
y = dfhoras['id']
horas=[x for x in range(24)]
plt.figure(facecolor="w")
plt.figure(figsize=(15,3))
plt.rcParams['axes.spines.top'] = False
plt.bar(x, y,linewidth=3,color="#172a3d")
plt.xticks(horas,fontweight='bold',color="#e33e31",fontsize=9)
plt.yticks(fontweight='bold',color="#e33e31",fontsize=9)
plt.grid(color="#172a3d", linestyle='--',linewidth=1,axis='y',alpha=0.15)
#aca creo las etiquetas de los puntos
for x,y in zip(x,y):
label = "{:.2f}".format(y)
plt.annotate(label,
(x,y),
textcoords="offset points",
xytext=(0,5),
ha='center',
fontsize=9,
fontweight='bold',
color="#e33e31")
plt.savefig('questions1.png',dpi=600,transparent=True,bbox_inches='tight')
questionsHour(dfhoras)
this is the result in vs code
and this is the result in a notebook
Make sure the environment packages used by VSCode are updated, as they aren't necessarily the same as those being used by Jupyter.
Tested in python 3.8.11, pandas 1.3.2, matplotlib 3.4.3
I was originally testing with matplotlib 3.4.2, which seems to have a bug and would not set weight='bold', so if VSCode is using a different package version, there could be a bug with alpha=0.15.
The OP uses plt.figure(facecolor="w") and plt.figure(figsize=(15,3)), which creates two different figures (not noticeable with inline plots, but two windows will open if using interactive plots). It should be plt.figure(facecolor="w", figsize=(15, 3)).
The following code uses the object oriented approach with axes, which makes sure all methods are applied to the correct axes being plotted.
Plot the dataframe directly with pandas.DataFrame.plot, which uses matplotlib as the default backend, and returns an axes.
Annotations are made using matplotlib.pyplot.bar_label
import pandas as pd
# test data
data = {'hora': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23], 'id': [10, 3, 2, 3, 5, 3, 11, 32, 41, 71, 75, 70, 57, 69, 50, 73, 47, 64, 73, 54, 45, 43, 34, 27]}
df = pd.DataFrame(data)
# plot function
def questionsHour(df):
ax = df.plot(x='hora', y='id', figsize=(15, 3), linewidth=3, color="#172a3d", rot=0, legend=False, xlabel='', kind='bar', width=0.75)
ax.set_xticklabels(ax.get_xticklabels(), weight='bold', color="#e33e31", fontsize=9)
ax.set_yticks(ax.get_yticks()) # prevents warning for next line
ax.set_yticklabels(ax.get_yticks(), weight='bold', color="#e33e31", fontsize=9)
ax.grid(color="#172a3d", linestyle='--', linewidth=1, axis='y', alpha=0.15)
ax.spines['top'].set_visible(False)
ax.bar_label(ax.containers[0], fmt='%.2f', fontsize=9, weight='bold', color="#e33e31")
questionsHour(df)
Related
I'm trying to add a legend to my seaborn bar + line chart, but only getting the error "No handles with labels found to put in legend." whatever I try. How to go about this?
from pathlib import Path
import pandas as pd
import seaborn as sns
from matplotlib import pyplot as plt
import matplotlib.dates as mdates
import numpy as np
dfGroup = pd.DataFrame({
'Year': [1910, 1911, 1912, 1913, 1914, 1915, 1916, 1917, 1918, 1919, 1920],
'Total Deaths': [0, 0, 2, 3, 2, 3, 4, 5, 6, 7, 8],
'Total Affected': [0, 1, 0, 2, 3, 6, 9, 8, 12, 13, 15]
})
# Add 3-year rolling average
dfGroup['rolling_3years'] = dfGroup['Total Deaths'].rolling(3).mean().shift(0)
dfGroup = dfGroup.fillna(0)
# Make a smooth line from the 3-year rolling average
from scipy.interpolate import make_interp_spline
X_Y_Spline = make_interp_spline(dfGroup['Year'], dfGroup['rolling_3years'])
# Returns evenly spaced numbers over a specified interval.
X_ = np.linspace(dfGroup['Year'].min(), dfGroup['Year'].max(), 500)
Y_ = X_Y_Spline(X_)
# Plot the data
a4_dims = (15, 10)
fig, ax1 = plt.subplots(figsize=a4_dims)
ax1 = sns.barplot(x = "Year", y = "Total Deaths",
data = dfGroup, color='#42b7bd')
ax2 = ax1.twinx()
ax2 = sns.lineplot(X_, Y_, marker='o')
As seaborn's barplot uses categorical positions, internally numbered 0,1,2,... both plots can be drawn on the same ax. This can be accomplished by recalculating the x values for the lineplot.
To obtain a legend, the label= keyword should be used. (Creating a legend on twinx axes is a bit more complicated and would involve creating custom handles.) Seaborn often automatically creates a legend. If you want to change its appearance, you can call ax1.legend(...) with customization parameters.
Here is some example code:
from pathlib import Path
import pandas as pd
import seaborn as sns
from matplotlib import pyplot as plt
import matplotlib.dates as mdates
import numpy as np
dfGroup = pd.DataFrame({
'Year': [1910, 1911, 1912, 1913, 1914, 1915, 1916, 1917, 1918, 1919, 1920],
'Total Deaths': [0, 0, 2, 3, 2, 3, 4, 5, 6, 7, 8],
'Total Affected': [0, 1, 0, 2, 3, 6, 9, 8, 12, 13, 15]
})
# Add 3-year rolling average
dfGroup['rolling_3years'] = dfGroup['Total Deaths'].rolling(3).mean().shift(0)
dfGroup = dfGroup.fillna(0)
# Make a smooth line from the 3-year rolling average
from scipy.interpolate import make_interp_spline
X_Y_Spline = make_interp_spline(dfGroup['Year'], dfGroup['rolling_3years'])
# Returns evenly spaced numbers over a specified interval.
X_ = np.linspace(dfGroup['Year'].min(), dfGroup['Year'].max(), 500)
Y_ = X_Y_Spline(X_)
# Plot the data
a4_dims = (15, 10)
fig, ax1 = plt.subplots(figsize=a4_dims)
sns.barplot(x="Year", y="Total Deaths",
data=dfGroup, color='#42b7bd', label='Barplot label', ax=ax1)
x_plot = np.linspace(0, len(dfGroup) - 1, len(X_))
sns.lineplot(x=x_plot, y=Y_, marker='o', label='LinePlot label', ax=ax1)
ax1.set_ylim(ymin=0) # let bars touch the bottom of the plot
ax1.margins(x=0.02) # less margins left and right
# ax1.legend(title='legend title') # optionally change the legend
plt.show()
PS: If an ax is already created, it should be given as a parameter to seaborn's axes-level functions (so, sns.barplot(..., ax=ax1) instead of ax1 = sns.barplot(...).
I am trying to figure out how to fill between two lines that are on different scales & axes of subplot, however, I have not been able to figure out how to do this.
I have tried following the answer here for a similar question, but the formula supplied in the code doesn't work on my dataset and based on the responses from the author of that question the equation doesn't appear to work when the x limits are changed.
The following image is what I am after (created in Photoshop):
However, using the code below, I get:
Example Data & Code
import pandas as pd
import matplotlib.pyplot as plt
data = pd.DataFrame({'DEPTH':[4300, 4310, 4320, 4330, 4340, 4350, 4360, 4370, 4380, 4390],
'NEUT':[45, 40, 30, 12, 6, 12, 8, 10, 20, 18],
'DENS':[2.5, 2.55, 2.32, 2.35, 2.3, 2.55, 2.58, 2.6, 2.52, 2.53]})
fig = plt.subplots(figsize=(7,20))
ax1 = plt.subplot2grid((1,1), (0,0))
ax2 = ax1.twiny()
ax1.plot('DENS', 'DEPTH', data=data, color='red')
ax1.set_xlim(1.95, 2.95)
ax1.set_xlabel('Density')
ax1.xaxis.label.set_color("red")
ax1.tick_params(axis='x', colors="red")
ax1.spines["top"].set_edgecolor("red")
ax2.plot('NEUT', 'DEPTH', data=data, color='blue')
ax2.set_xlim(45, -15)
ax2.set_xlabel('Neutron')
ax2.xaxis.label.set_color("blue")
ax2.spines["top"].set_position(("axes", 1.04))
ax2.tick_params(axis='x', colors="blue")
ax2.spines["top"].set_edgecolor("blue")
ax1.fill_betweenx(data['DEPTH'], data['DENS'], data['NEUT'], where=data['DENS']>=data['NEUT'], interpolate=True, color='green')
ax1.fill_betweenx(data['DEPTH'], data['DENS'], data['NEUT'], where=data['DENS']<=data['NEUT'], interpolate=True, color='yellow')
for ax in [ax1, ax2]:
ax.set_ylim(4400, 4300)
ax.xaxis.set_ticks_position("top")
ax.xaxis.set_label_position("top")
Would anyone be able to help me with this please?
The difference between your code and the answer you linked is that your Neutron scale goes from the maximum value on the left to the minimum value on the right, which means the logic is slightly wrong. So we just need to switch a few min and max terms around.
Try this:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
data = pd.DataFrame({'DEPTH':[4300, 4310, 4320, 4330, 4340, 4350, 4360, 4370, 4380, 4390],
'NEUT':[45, 40, 30, 12, 6, 12, 8, 10, 20, 18],
'DENS':[2.5, 2.55, 2.32, 2.35, 2.3, 2.55, 2.58, 2.6, 2.52, 2.53]})
fig = plt.subplots(figsize=(6,8))
ax1 = plt.subplot2grid((1,1), (0,0))
ax2 = ax1.twiny()
ax1.plot('DENS', 'DEPTH', data=data, color='red')
ax1.set_xlim(1.95, 2.95)
ax1.set_xlabel('Density')
ax1.xaxis.label.set_color("red")
ax1.tick_params(axis='x', colors="red")
ax1.spines["top"].set_edgecolor("red")
ax2.plot('NEUT', 'DEPTH', data=data, color='blue')
ax2.set_xlim(45, -15)
ax2.set_xlabel('Neutron')
ax2.xaxis.label.set_color("blue")
ax2.spines["top"].set_position(("axes", 1.08))
ax2.tick_params(axis='x', colors="blue")
ax2.spines["top"].set_edgecolor("blue")
x = np.array(ax1.get_xlim())
z = np.array(ax2.get_xlim())
x1 = data['DENS']
x2 = data['NEUT']
nz=((x2-np.max(z))/(np.min(z)-np.max(z)))*(np.max(x)-np.min(x))+np.min(x)
ax1.fill_betweenx(data['DEPTH'], x1, nz, where=x1>=nz, interpolate=True, color='green')
ax1.fill_betweenx(data['DEPTH'], x1, nz, where=x1<=nz, interpolate=True, color='yellow')
for ax in [ax1, ax2]:
ax.set_ylim(4400, 4300)
ax.xaxis.set_ticks_position("top")
ax.xaxis.set_label_position("top")
plt.show()
(I changed the figure size so it would fit on my screen)
When I plot data using matplotlib I always have 5-9 ticks on my x-axis independent of the range I plot, and if I zoom on the x-axis the tick spacing decreases, so I still see 5-9 ticks.
however, I would like 20-30 ticks on my x-axis!
I can achieve this with the following:
from matplotlib import pyplot as plt
import numpy as np
x = [5, 10, 15, 20, 25, 30, 35, 40, 45, 50]
y = [1, 4, 3, 2, 7, 6, 9, 8, 10, 5]
number_of_ticks_on_x_axis = 20
plt.plot(x, y)
plt.xticks(np.arange(min(x), max(x)+1, (max(x) - min(x))/number_of_ticks_on_x_axis))
plt.show()
If I now zoom on the x-axis, no new ticks appear between the existing ones. I would like to still have ~20 ticks however much I zoom.
Assuming that you want to fix the no. of ticks on the X axis
...
from matplotlib.ticker import MaxNLocator
...
fig, ax = plt.subplots()
ax.xaxis.set_major_locator(MaxNLocator(15, min_n_ticks=15))
...
Please look at the docs for MaxNLocator
Example
In [36]: import numpy as np
...: import matplotlib.pyplot as plt
In [37]: from matplotlib.ticker import MaxNLocator
In [38]: fig, ax = plt.subplots(figsize=(10,4))
In [39]: ax.grid()
In [40]: ax.xaxis.set_major_locator(MaxNLocator(min_n_ticks=15))
In [41]: x = np.linspace(0, 1, 51)
In [42]: y = x*(1-x)
In [43]: plt.plot(x, y)
Out[43]: [<matplotlib.lines.Line2D at 0x7f9eab409e10>]
gives
and when I zoom into the maximum of the curve I get
You can link a callback function to an event in the canvas. In you case you can trigger a function that updates the axis when a redraw occurs.
from matplotlib import pyplot as plt
import numpy as np
x = [5, 10, 15, 20, 25, 30, 35, 40, 45, 50]
y = [1, 4, 3, 2, 7, 6, 9, 8, 10, 5]
n = 20
plt.plot(x, y)
plt.xticks(np.arange(min(x), max(x)+1, (max(x) - min(x))/n), rotation=90)
def on_zoom(event):
ax = plt.gca()
fig = plt.gcf()
x_min, x_max = ax.get_xlim()
ax.set_xticks(np.linspace(x_min, x_max, n))
# had to add flush_events to get the ticks to redraw on the last update.
fig.canvas.flush_events()
fig = plt.gcf()
fig.canvas.mpl_disconnect(cid)
cid = fig.canvas.mpl_connect('draw_event', on_zoom)
I updated Anaconda Python to the latest version (4.3), where they upgraded Matplotlib to version 2.
The upgrade has made some major changes to the default style (see here).
And, while I really like some of those changes, I am not in agreement with a few of them.
Hence I did some modifications, as suggested in the link above:
#%matplotlib inline
#%config InlineBackend.figure_format = 'svg'
import scipy as sc
import matplotlib.pyplot as plt
import matplotlib
# http://matplotlib.org/users/dflt_style_changes.html
params = {'legend.fontsize': 18,
'axes.labelsize': 18,
'axes.titlesize': 18,
'xtick.labelsize' :12,
'ytick.labelsize': 12,
'mathtext.fontset': 'cm',
'mathtext.rm': 'serif',
'grid.color': 'k',
'grid.linestyle': ':',
'grid.linewidth': 0.5,
}
matplotlib.rcParams.update(params)
x = sc.linspace(0,100)
y = x**2
fig = plt.figure('Fig')
ax = fig.add_subplot(1, 1, 1)
lines = ax.semilogy(x, y)
ax.set_yticks([300], minor=True)
ax.yaxis.grid(True, which='minor')
ax.yaxis.set_minor_formatter(matplotlib.ticker.ScalarFormatter())
ax.tick_params(axis='y', pad=10)
ax.set_xlabel(r'$\mathrm{R_L}$')
ax.set_ylabel(r'$\sigma \int_l \; dx$')
#fig.savefig('./PNG/test.png', dpi=300, bbox_inches='tight')
Using Latex as the axes labels, as in the code above, results in a figure with inconsistent text on axes (see the following image).
How to get back to the previous behaviour (see the image below) or to a consistent font scheme?
EDIT:
Using the Latex back-end I am able to get a good result, but it is extremely slow.
Anyway, I think the internal back-end should be able to get a consistent output and switching to a different back-end is not a real solution, but more a workaround.
To use the latex back-end:
#%matplotlib inline
#%matplotlib notebook
#%config InlineBackend.figure_format = 'svg'
import scipy as sc
import matplotlib.pyplot as plt
import matplotlib
# http://matplotlib.org/users/dflt_style_changes.html
params = {'legend.fontsize': 18,
'axes.labelsize': 18,
'axes.titlesize': 18,
'xtick.labelsize' :12,
'ytick.labelsize': 12,
'mathtext.fontset': 'cm',
'mathtext.rm': 'serif',
'grid.color': 'k',
'grid.linestyle': ':',
'grid.linewidth': 0.5,
}
matplotlib.rcParams.update(params)
matplotlib.rcParams.update({'text.usetex':True, 'text.latex.preamble':[r'\usepackage{amsmath, newtxmath}']})
x = sc.linspace(0,100)
y = x**2
fig = plt.figure('Fig')
ax = fig.add_subplot(1, 1, 1)
lines = ax.semilogy(x, y)
ax.set_yticks([300], minor=True)
ax.yaxis.grid(True, which='minor')
ax.yaxis.set_minor_formatter(matplotlib.ticker.ScalarFormatter())
ax.tick_params(axis='y', pad=10)
ax.set_xlabel(r'$\mathrm{R_L}$')
ax.set_ylabel(r'$\sigma \int_l \; dx$')
#fig.savefig('./PNG/test.png', dpi=300, bbox_inches='tight')
The result with matplotlib 2 is:
The resulting plot with the older version is (still a bit different, maybe due to some latex differences):
But again, the desired result is what obtained from an older version of matplotlib and in displayed in figure 2.
If consistency is the only issue, you can use a "Roman" style using the "Times" font. It is not necessary to use Latex via usetex. Instead simply use the STIX fontset, the Times font and serif mathtext.
import scipy as sc
import matplotlib.style
import matplotlib.pyplot as plt
params = {'legend.fontsize': 18,
'axes.labelsize': 18,
'axes.titlesize': 18,
'xtick.labelsize' :12,
'ytick.labelsize': 12,
'grid.color': 'k',
'grid.linestyle': ':',
'grid.linewidth': 0.5,
'mathtext.fontset' : 'stix',
'mathtext.rm' : 'serif',
'font.family' : 'serif',
'font.serif' : "Times New Roman", # or "Times"
}
matplotlib.rcParams.update(params)
x = sc.linspace(0,100)
y = x**2
fig = plt.figure('Fig')
ax = fig.add_subplot(1, 1, 1)
lines = ax.semilogy(x, y)
ax.yaxis.set_minor_formatter(matplotlib.ticker.ScalarFormatter())
ax.tick_params(axis='y', pad=10)
ax.set_yticks([300], minor=True)
ax.yaxis.grid(True, which='minor')
ax.set_xlabel(r'$\mathrm{R_L}$')
ax.set_ylabel(r'$\sigma \int_l \; dx$')
plt.tight_layout()
plt.show()
From the link you did provide:
A ‘classic’ style sheet is provided so reverting to the 1.x default values is a single line of python
mpl.style.use('classic')
Adding this line
matplotlib.style.use('classic')
to your script should solve your problem.
I tested it on my python2.7/matplotlib 2, and it worked fine (i.e. I get back the matplotlib 1.x fonts).
While trying to find a solution to my question, I tried comparing the dictionaries of the old and new rcParams and setting the elements which were different and related to mathtext font: the result is quite good.
The code is:
#%matplotlib inline
#%matplotlib notebook
#%config InlineBackend.figure_format = 'svg'
import scipy as sc
import matplotlib.pyplot as plt
import matplotlib
# http://matplotlib.org/users/dflt_style_changes.html
params = {'legend.fontsize': 18,
'axes.labelsize': 18,
'axes.titlesize': 18,
'xtick.labelsize' :12,
'ytick.labelsize': 12,
'mathtext.fontset': 'cm',
'mathtext.rm': 'serif',
'mathtext.bf': 'serif:bold',
'mathtext.it': 'serif:italic',
'mathtext.sf': 'sans\\-serif',
'grid.color': 'k',
'grid.linestyle': ':',
'grid.linewidth': 0.5,
}
matplotlib.rcParams.update(params)
#matplotlib.rcParams.update({'text.usetex':True, 'text.latex.preamble':[r'\usepackage{amsmath, newtxmath}']})
#matplotlib.rcParams.update({'text.usetex':True, 'text.latex.preamble':[r'\usepackage{amsmath, mathptmx}']})
#matplotlib.rcParams.update({'text.usetex':True, 'text.latex.preamble':[r'\usepackage{amsmath}']})
x = sc.linspace(0,100)
y = x**2
fig = plt.figure('Fig')
ax = fig.add_subplot(1, 1, 1)
lines = ax.semilogy(x, y)
ax.set_yticks([300], minor=True)
ax.yaxis.grid(True, which='minor')
ax.yaxis.set_minor_formatter(matplotlib.ticker.ScalarFormatter())
ax.tick_params(axis='y', pad=10)
ax.set_xlabel(r'$\mathrm{R_L}$')
ax.set_ylabel(r'$\sigma \int_l \; dx$')
fig.savefig('./PNG/test.png', dpi=300, bbox_inches='tight')
hence adding also:
'mathtext.rm': 'serif',
'mathtext.bf': 'serif:bold',
'mathtext.it': 'serif:italic',
'mathtext.sf': 'sans\\-serif',
which results in:
that I consider quite good and consistent in a Latex document.
The other answer in this thread from #ImportanceOfBeingErnest is also neat and nice.
I have values at x and y axis and trying to produce simple line graph on subplot. Here is the simple and basic example which shows the problem.
import matplotlib.pyplot as plt
x1 = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30,
31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58,
59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72]
y1 = [24.892730712890625, 25.268890380859375, 26.677642822265625, 28.294586181640625, 29.477203369140625,
30.61334228515625, 31.656219482421875, 32.371551513671875, 31.412261962890625, 31.973724365234375, 31.563812255859375,
30.72821044921875, 29.249237060546875, 26.759185791015625, 26.081024169921875, 25.27996826171875, 24.69805908203125,
24.92388916015625, 24.76177978515625, 24.385498046875, 24.093231201171875, 23.92156982421875, 23.788543701171875,
23.67657470703125, 23.581085205078125, 23.92095947265625, 25.90557861328125, 27.767333984375, 29.196136474609375,
30.25726318359375, 31.262786865234375, 32.2996826171875, 32.92620849609375, 33.32098388671875, 33.228057861328125,
30.495269775390625, 29.17010498046875, 28.04144287109375, 27.326202392578125, 24.904205322265625, 23.775054931640625,
24.1328125, 24.195343017578125, 23.751312255859375, 23.55316162109375, 23.459228515625, 23.304534912109375,
23.233062744140625, 23.093170166015625, 23.15887451171875, 25.13739013671875, 27.397430419921875, 28.923431396484375,
29.945037841796875, 30.976715087890625, 31.93109130859375, 32.665435791015625, 32.701324462890625, 31.212799072265625,
30.201507568359375, 29.591888427734375, 28.002410888671875, 27.72802734375, 27.371002197265625, 26.072509765625,
25.39373779296875, 25.196044921875, 25.2684326171875, 24.815582275390625, 24.27130126953125, 23.758575439453125,
23.49615478515625, 23.3907470703125]
plt.subplot(513)
plt.plot(x1, y1, 'b-')
plt.grid(True)
plt.show()
All works fine. But the output plot has some empty space on xaxis. Here is the image which shows the problem:-
Any help to solve the issue is appreciated.
=You could use xlim to overwrite this behaviour introduced by "AutoLocator" in the background:
plt.subplot(513, xlim=(0,72))
# or
plt.subplot(513, xlim=(x1[0], x1[-1]))
You could also ajust the Locator like shown in this example (and others):
http://matplotlib.org/examples/pylab_examples/major_minor_demo2.html
By default, in matplotlib versions <2.0, matplotlib will choose "even" limits for the axes. For example:
import numpy as np
import matplotlib.pyplot as plt
np.random.seed(1977)
x = np.linspace(2.1, 22.8, 1000)
y = np.random.normal(0, 1, x.size).cumsum()
fig, ax = plt.subplots()
ax.plot(x, y)
plt.show()
If you'd prefer to have the limits strictly set to the data limits, you can use ax.axis('tight'). However, this will set both the x and y-limits to be "tight", which often is not what you want.
In this case, you'd more likely want to set just the x-limits to be "tight". An easy way to do this is to use ax.margins(x=0). margins specifies that the autoscaling should pad things by a percentage of the data range. Therefore, by setting x=0, we're effectively making the x-limits identical to the data limits in the x-direction.
For example:
import numpy as np
import matplotlib.pyplot as plt
np.random.seed(1977)
x = np.linspace(2.1, 22.8, 1000)
y = np.random.normal(0, 1, x.size).cumsum()
fig, ax = plt.subplots()
ax.plot(x, y)
ax.margins(x=0)
plt.show()
You could also accomplish this by using ax.autoscale(axis='x', tight=True).
However, an additional advantage of margins is that you'll often want to have the y-axis pad by a percentage of the data range as well. Therefore, it's common to want to do something like:
import numpy as np
import matplotlib.pyplot as plt
np.random.seed(1977)
x = np.linspace(2.1, 22.8, 1000)
y = np.random.normal(0, 1, x.size).cumsum()
fig, ax = plt.subplots()
ax.plot(x, y)
ax.margins(x=0, y=0.05)
plt.show()