Matplotlib - 固定 x 轴刻度和自动缩放 y 轴
Matplotlib - fixing x axis scale and autoscale y axis
我只想绘制数组的一部分,固定 x 部分,但让 y 部分自动缩放。我尝试了如下所示,但它不起作用。
有什么建议吗?
import numpy as np
import matplotlib.pyplot as plt
data=[np.arange(0,101,1),300-0.1*np.arange(0,101,1)]
plt.figure()
plt.scatter(data[0], data[1])
plt.xlim([50,100])
plt.autoscale(enable=True, axis='y')
plt.show()
自动缩放始终使用数据的全部范围,因此 y 轴按 y 数据的全部范围缩放,而不仅仅是 x 限制内的内容。
如果您想显示数据的一个子集,那么只绘制该子集可能是最简单的方法:
import numpy as np
import matplotlib.pyplot as plt
x, y = np.arange(0,101,1) ,300 - 0.1*np.arange(0,101,1)
mask = (x >= 50) & (x <= 100)
fig, ax = plt.subplots()
ax.scatter(x[mask], y[mask])
plt.show()
而Joe Kington certainly proposes the most sensible answer when he recommends that only the necessary data be plotted, there are situations where it would be best to plot all of the data and just zoom to a certain section. Additionally, it would be nice to have an "autoscale_y" function that only requires the axes object (i.e., unlike the answer here,需要直接使用数据。)
这里是一个函数,它只根据可见 x 区域中的数据重新缩放 y 轴:
def autoscale_y(ax,margin=0.1):
"""This function rescales the y-axis based on the data that is visible given the current xlim of the axis.
ax -- a matplotlib axes object
margin -- the fraction of the total height of the y-data to pad the upper and lower ylims"""
import numpy as np
def get_bottom_top(line):
xd = line.get_xdata()
yd = line.get_ydata()
lo,hi = ax.get_xlim()
y_displayed = yd[((xd>lo) & (xd<hi))]
h = np.max(y_displayed) - np.min(y_displayed)
bot = np.min(y_displayed)-margin*h
top = np.max(y_displayed)+margin*h
return bot,top
lines = ax.get_lines()
bot,top = np.inf, -np.inf
for line in lines:
new_bot, new_top = get_bottom_top(line)
if new_bot < bot: bot = new_bot
if new_top > top: top = new_top
ax.set_ylim(bot,top)
这有点像 hack,在很多情况下可能行不通,但对于简单的情节来说,它很管用。
下面是一个使用这个函数的简单例子:
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(-100,100,1000)
y = x**2 + np.cos(x)*100
fig,axs = plt.subplots(1,2,figsize=(8,5))
for ax in axs:
ax.plot(x,y)
ax.plot(x,y*2)
ax.plot(x,y*10)
ax.set_xlim(-10,10)
autoscale_y(axs[1])
axs[0].set_title('Rescaled x-axis')
axs[1].set_title('Rescaled x-axis\nand used "autoscale_y"')
plt.show()
我以@DanHickstein 的回答为基础,涵盖了绘图、散点和 axhline/axvline 缩放 x 轴或 y 轴的情况。它可以像 autoscale()
一样简单地调用以在最近的轴上工作。如需编辑请fork it on gist.
def autoscale(ax=None, axis='y', margin=0.1):
'''Autoscales the x or y axis of a given matplotlib ax object
to fit the margins set by manually limits of the other axis,
with margins in fraction of the width of the plot
Defaults to current axes object if not specified.
'''
import matplotlib.pyplot as plt
import numpy as np
if ax is None:
ax = plt.gca()
newlow, newhigh = np.inf, -np.inf
for artist in ax.collections + ax.lines:
x,y = get_xy(artist)
if axis == 'y':
setlim = ax.set_ylim
lim = ax.get_xlim()
fixed, dependent = x, y
else:
setlim = ax.set_xlim
lim = ax.get_ylim()
fixed, dependent = y, x
low, high = calculate_new_limit(fixed, dependent, lim)
newlow = low if low < newlow else newlow
newhigh = high if high > newhigh else newhigh
margin = margin*(newhigh - newlow)
setlim(newlow-margin, newhigh+margin)
def calculate_new_limit(fixed, dependent, limit):
'''Calculates the min/max of the dependent axis given
a fixed axis with limits
'''
if len(fixed) > 2:
mask = (fixed>limit[0]) & (fixed < limit[1])
window = dependent[mask]
low, high = window.min(), window.max()
else:
low = dependent[0]
high = dependent[-1]
if low == 0.0 and high == 1.0:
# This is a axhline in the autoscale direction
low = np.inf
high = -np.inf
return low, high
def get_xy(artist):
'''Gets the xy coordinates of a given artist
'''
if "Collection" in str(artist):
x, y = artist.get_offsets().T
elif "Line" in str(artist):
x, y = artist.get_xdata(), artist.get_ydata()
else:
raise ValueError("This type of object isn't implemented yet")
return x, y
它和它的前身一样,有点老套,但这是必要的,因为集合和线有不同的方法来返回 xy 坐标,而且 axhline/axvline 很难处理,因为它只有两个数据点。
这是实际操作:
fig, axes = plt.subplots(ncols = 4, figsize=(12,3))
(ax1, ax2, ax3, ax4) = axes
x = np.linspace(0,100,300)
noise = np.random.normal(scale=0.1, size=x.shape)
y = 2*x + 3 + noise
for ax in axes:
ax.plot(x, y)
ax.scatter(x,y, color='red')
ax.axhline(50., ls='--', color='green')
for ax in axes[1:]:
ax.set_xlim(20,21)
ax.set_ylim(40,45)
autoscale(ax3, 'y', margin=0.1)
autoscale(ax4, 'x', margin=0.1)
ax1.set_title('Raw data')
ax2.set_title('Specificed limits')
ax3.set_title('Autoscale y')
ax4.set_title('Autoscale x')
plt.tight_layout()
我想补充@TomNorway 的出色答案(这为我节省了很多时间)来处理一些艺术家部分完全由 NaN 组成的情况。
我所做的所有更改都在
if len(fixed) > 2:
干杯!
def autoscale(ax=None, axis='y', margin=0.1):
'''Autoscales the x or y axis of a given matplotlib ax object
to fit the margins set by manually limits of the other axis,
with margins in fraction of the width of the plot
Defaults to current axes object if not specified.
'''
if ax is None:
ax = plt.gca()
newlow, newhigh = np.inf, -np.inf
for artist in ax.collections + ax.lines:
x,y = get_xy(artist)
if axis == 'y':
setlim = ax.set_ylim
lim = ax.get_xlim()
fixed, dependent = x, y
else:
setlim = ax.set_xlim
lim = ax.get_ylim()
fixed, dependent = y, x
low, high = calculate_new_limit(fixed, dependent, lim)
newlow = low if low < newlow else newlow
newhigh = high if high > newhigh else newhigh
margin = margin*(newhigh - newlow)
setlim(newlow-margin, newhigh+margin)
def calculate_new_limit(fixed, dependent, limit):
'''Calculates the min/max of the dependent axis given
a fixed axis with limits
'''
if len(fixed) > 2:
mask = (fixed>limit[0]) & (fixed < limit[1]) & (~np.isnan(dependent)) & (~np.isnan(fixed))
window = dependent[mask]
try:
low, high = window.min(), window.max()
except ValueError: # Will throw ValueError if `window` has zero elements
low, high = np.inf, -np.inf
else:
low = dependent[0]
high = dependent[-1]
if low == 0.0 and high == 1.0:
# This is a axhline in the autoscale direction
low = np.inf
high = -np.inf
return low, high
def get_xy(artist):
'''Gets the xy coordinates of a given artist
'''
if "Collection" in str(artist):
x, y = artist.get_offsets().T
elif "Line" in str(artist):
x, y = artist.get_xdata(), artist.get_ydata()
else:
raise ValueError("This type of object isn't implemented yet")
return x, y
- 最简单的方法可能是使用
pandas
,这使得使用 Boolean indexing 选择数据非常容易。
- 将
x
和y
加载到DataFrame中,使用pandas.Series.between(left, right, inclusive=True)
, and directly plot with pandas.DataFrame.plot
的布尔选择,它使用matplotlib
.
import numpy as np # for the test data
import pandas as pd
# load the data into the dataframe; there are many ways to do this
df = pd.DataFrame({'x': np.arange(0,101,1), 'y': 300-0.1*np.arange(0,101,1)})
# select and plot the data
ax = df[df.x.between(50, 100)].plot(x='x', y='y', kind='scatter', figsize=(5, 4))
我只想绘制数组的一部分,固定 x 部分,但让 y 部分自动缩放。我尝试了如下所示,但它不起作用。
有什么建议吗?
import numpy as np
import matplotlib.pyplot as plt
data=[np.arange(0,101,1),300-0.1*np.arange(0,101,1)]
plt.figure()
plt.scatter(data[0], data[1])
plt.xlim([50,100])
plt.autoscale(enable=True, axis='y')
plt.show()
自动缩放始终使用数据的全部范围,因此 y 轴按 y 数据的全部范围缩放,而不仅仅是 x 限制内的内容。
如果您想显示数据的一个子集,那么只绘制该子集可能是最简单的方法:
import numpy as np
import matplotlib.pyplot as plt
x, y = np.arange(0,101,1) ,300 - 0.1*np.arange(0,101,1)
mask = (x >= 50) & (x <= 100)
fig, ax = plt.subplots()
ax.scatter(x[mask], y[mask])
plt.show()
而Joe Kington certainly proposes the most sensible answer when he recommends that only the necessary data be plotted, there are situations where it would be best to plot all of the data and just zoom to a certain section. Additionally, it would be nice to have an "autoscale_y" function that only requires the axes object (i.e., unlike the answer here,需要直接使用数据。)
这里是一个函数,它只根据可见 x 区域中的数据重新缩放 y 轴:
def autoscale_y(ax,margin=0.1):
"""This function rescales the y-axis based on the data that is visible given the current xlim of the axis.
ax -- a matplotlib axes object
margin -- the fraction of the total height of the y-data to pad the upper and lower ylims"""
import numpy as np
def get_bottom_top(line):
xd = line.get_xdata()
yd = line.get_ydata()
lo,hi = ax.get_xlim()
y_displayed = yd[((xd>lo) & (xd<hi))]
h = np.max(y_displayed) - np.min(y_displayed)
bot = np.min(y_displayed)-margin*h
top = np.max(y_displayed)+margin*h
return bot,top
lines = ax.get_lines()
bot,top = np.inf, -np.inf
for line in lines:
new_bot, new_top = get_bottom_top(line)
if new_bot < bot: bot = new_bot
if new_top > top: top = new_top
ax.set_ylim(bot,top)
这有点像 hack,在很多情况下可能行不通,但对于简单的情节来说,它很管用。
下面是一个使用这个函数的简单例子:
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(-100,100,1000)
y = x**2 + np.cos(x)*100
fig,axs = plt.subplots(1,2,figsize=(8,5))
for ax in axs:
ax.plot(x,y)
ax.plot(x,y*2)
ax.plot(x,y*10)
ax.set_xlim(-10,10)
autoscale_y(axs[1])
axs[0].set_title('Rescaled x-axis')
axs[1].set_title('Rescaled x-axis\nand used "autoscale_y"')
plt.show()
我以@DanHickstein 的回答为基础,涵盖了绘图、散点和 axhline/axvline 缩放 x 轴或 y 轴的情况。它可以像 autoscale()
一样简单地调用以在最近的轴上工作。如需编辑请fork it on gist.
def autoscale(ax=None, axis='y', margin=0.1):
'''Autoscales the x or y axis of a given matplotlib ax object
to fit the margins set by manually limits of the other axis,
with margins in fraction of the width of the plot
Defaults to current axes object if not specified.
'''
import matplotlib.pyplot as plt
import numpy as np
if ax is None:
ax = plt.gca()
newlow, newhigh = np.inf, -np.inf
for artist in ax.collections + ax.lines:
x,y = get_xy(artist)
if axis == 'y':
setlim = ax.set_ylim
lim = ax.get_xlim()
fixed, dependent = x, y
else:
setlim = ax.set_xlim
lim = ax.get_ylim()
fixed, dependent = y, x
low, high = calculate_new_limit(fixed, dependent, lim)
newlow = low if low < newlow else newlow
newhigh = high if high > newhigh else newhigh
margin = margin*(newhigh - newlow)
setlim(newlow-margin, newhigh+margin)
def calculate_new_limit(fixed, dependent, limit):
'''Calculates the min/max of the dependent axis given
a fixed axis with limits
'''
if len(fixed) > 2:
mask = (fixed>limit[0]) & (fixed < limit[1])
window = dependent[mask]
low, high = window.min(), window.max()
else:
low = dependent[0]
high = dependent[-1]
if low == 0.0 and high == 1.0:
# This is a axhline in the autoscale direction
low = np.inf
high = -np.inf
return low, high
def get_xy(artist):
'''Gets the xy coordinates of a given artist
'''
if "Collection" in str(artist):
x, y = artist.get_offsets().T
elif "Line" in str(artist):
x, y = artist.get_xdata(), artist.get_ydata()
else:
raise ValueError("This type of object isn't implemented yet")
return x, y
它和它的前身一样,有点老套,但这是必要的,因为集合和线有不同的方法来返回 xy 坐标,而且 axhline/axvline 很难处理,因为它只有两个数据点。
这是实际操作:
fig, axes = plt.subplots(ncols = 4, figsize=(12,3))
(ax1, ax2, ax3, ax4) = axes
x = np.linspace(0,100,300)
noise = np.random.normal(scale=0.1, size=x.shape)
y = 2*x + 3 + noise
for ax in axes:
ax.plot(x, y)
ax.scatter(x,y, color='red')
ax.axhline(50., ls='--', color='green')
for ax in axes[1:]:
ax.set_xlim(20,21)
ax.set_ylim(40,45)
autoscale(ax3, 'y', margin=0.1)
autoscale(ax4, 'x', margin=0.1)
ax1.set_title('Raw data')
ax2.set_title('Specificed limits')
ax3.set_title('Autoscale y')
ax4.set_title('Autoscale x')
plt.tight_layout()
我想补充@TomNorway 的出色答案(这为我节省了很多时间)来处理一些艺术家部分完全由 NaN 组成的情况。
我所做的所有更改都在
if len(fixed) > 2:
干杯!
def autoscale(ax=None, axis='y', margin=0.1):
'''Autoscales the x or y axis of a given matplotlib ax object
to fit the margins set by manually limits of the other axis,
with margins in fraction of the width of the plot
Defaults to current axes object if not specified.
'''
if ax is None:
ax = plt.gca()
newlow, newhigh = np.inf, -np.inf
for artist in ax.collections + ax.lines:
x,y = get_xy(artist)
if axis == 'y':
setlim = ax.set_ylim
lim = ax.get_xlim()
fixed, dependent = x, y
else:
setlim = ax.set_xlim
lim = ax.get_ylim()
fixed, dependent = y, x
low, high = calculate_new_limit(fixed, dependent, lim)
newlow = low if low < newlow else newlow
newhigh = high if high > newhigh else newhigh
margin = margin*(newhigh - newlow)
setlim(newlow-margin, newhigh+margin)
def calculate_new_limit(fixed, dependent, limit):
'''Calculates the min/max of the dependent axis given
a fixed axis with limits
'''
if len(fixed) > 2:
mask = (fixed>limit[0]) & (fixed < limit[1]) & (~np.isnan(dependent)) & (~np.isnan(fixed))
window = dependent[mask]
try:
low, high = window.min(), window.max()
except ValueError: # Will throw ValueError if `window` has zero elements
low, high = np.inf, -np.inf
else:
low = dependent[0]
high = dependent[-1]
if low == 0.0 and high == 1.0:
# This is a axhline in the autoscale direction
low = np.inf
high = -np.inf
return low, high
def get_xy(artist):
'''Gets the xy coordinates of a given artist
'''
if "Collection" in str(artist):
x, y = artist.get_offsets().T
elif "Line" in str(artist):
x, y = artist.get_xdata(), artist.get_ydata()
else:
raise ValueError("This type of object isn't implemented yet")
return x, y
- 最简单的方法可能是使用
pandas
,这使得使用 Boolean indexing 选择数据非常容易。 - 将
x
和y
加载到DataFrame中,使用pandas.Series.between(left, right, inclusive=True)
, and directly plot withpandas.DataFrame.plot
的布尔选择,它使用matplotlib
.
import numpy as np # for the test data
import pandas as pd
# load the data into the dataframe; there are many ways to do this
df = pd.DataFrame({'x': np.arange(0,101,1), 'y': 300-0.1*np.arange(0,101,1)})
# select and plot the data
ax = df[df.x.between(50, 100)].plot(x='x', y='y', kind='scatter', figsize=(5, 4))