子图中每个标记中变量的数据值
Data value of a variable in each marker within subplots
我正在开发以下函数:extract_name_value() 生成一个步骤图,它采用 Python 中的 pandas DataFrame 的值,目前它工作正常,但是我想在每个标记中添加变量 points_axisyvalue 或 values_list 的值:Script Here
我尝试使用以下示例:Data value at each marker, Matplotlib scatter plot with different text at each data point or How to put individual tags for a matplotlib scatter plot?,这将是我想要的;我什至尝试过使用 plt.annotate()
,但是值的数据并没有按照我想要的方式出现,而且我认为它会掩盖很多图表并且不会很好地欣赏。下面是我使用 plt.annotate():
的代码
# Function to extract the Name and Value attributes
def extract_name_value(signals_df, rootXML):
# print(signals_df)
names_list = [name for name in signals_df['Name'].unique()]
num_names_list = len(names_list)
num_axisx = len(signals_df["Name"])
values_list = [value for pos, value in enumerate(signals_df["Value"])]
print(values_list)
points_axisy = signals_df["Value"]
print(len(points_axisy))
colors = ['b', 'g', 'r', 'c', 'm', 'y']
# Creation Graphic
fig, ax = plt.subplots(nrows=num_names_list, figsize=(20, 30), sharex=True)
plt.suptitle(f'File XML: {rootXML}', fontsize=16, fontweight='bold', color='SteelBlue', position=(0.75, 0.95))
plt.xticks(np.arange(-1, num_axisx), color='SteelBlue', fontweight='bold')
labels = ['value: {0}'.format(j) for j in values_list]
print(labels)
i = 1
for pos, name in enumerate(names_list):
# get data
data = signals_df[signals_df["Name"] == name]["Value"]
print(data)
# get color
j = random.randint(0, len(colors) - 1)
# get plots by index = pos
x = np.hstack([-1, data.index.values, len(signals_df) - 1])
y = np.hstack([0, data.values, data.iloc[-1]])
ax[pos].plot(x, y, drawstyle='steps-post', marker='o', color=colors[j], linewidth=3)
ax[pos].set_ylabel(name, fontsize=8, fontweight='bold', color='SteelBlue', rotation=30, labelpad=35)
ax[pos].yaxis.set_major_formatter(ticker.FormatStrFormatter('%0.1f'))
ax[pos].yaxis.set_tick_params(labelsize=6)
ax[pos].grid(alpha=0.4)
i += 1
for label, x, y in zip(labels, x, y):
plt.annotate(label, xy=(x, y), xytext=(-20, 20), textcoords='offset points', ha='right', va='bottom', bbox=dict(boxstyle='round,pad=0.5', fc='yellow', alpha=0.5),
arrowprops=dict(arrowstyle='->', connectionstyle='arc3,rad=0'))
plt.show()
我得到的是拼接在不同位置的注释。
但是,我的代码需要什么来显示每个点的每个值?
我也一直在尝试使用 Matplotlib 参考中的代码,但无法完成:Marker Reference。非常感谢您,任何评论都有帮助。
我想应该和你的要求很接近了。我随机生成数据,然后使用 matplotlib.text 对其进行注释。它不是很漂亮,您可能想添加一些填充和更多改进,但我希望它能提供一个好主意!
如果两点距离太近,您可能需要在左侧注释一个,在右侧注释另一个,就像我对第一个点所做的那样。在你举的例子中我没有看到这种情况,所以没有处理。
函数place_label(label, xy, position, ax, pad=0.01)
将标签放在您想要的位置。代码的其余部分使用随机生成的数据来证明它可以工作。
import random
import numpy as np
import matplotlib.pyplot as plt
# function that places the label give the desired position
def place_label(label, xy, position, ax, pad=0.01):
# annotate in the initial position, xy is the top right corner of the bounding box
t_ = ax.text(x=xy[0], y=xy[1], s=label, fontsize=16)
# find useful values
tbb = t_.get_window_extent(renderer=rend)
abb = ax.get_window_extent(renderer=rend)
a_xlim, a_ylim = ax.get_xlim(), a_.get_ylim()
# now adjust the position if needed
new_xy = [xy[0], xy[1]]
relative_width = tbb.width/abb.width * (a_xlim[1] - a_xlim[0])
pad_x = pad * (a_xlim[1] - a_xlim[0])
assert(position[0] in ['l', 'c', 'r'])
if position[0] == 'c':
new_xy[0] -= relative_width/2
elif position[0] == 'l':
new_xy[0] -= relative_width + pad_x
else:
new_xy[0] += pad_x
relative_height = tbb.height/abb.height * (a_ylim[1] - a_ylim[0])
pad_y = pad * (a_ylim[1] - a_ylim[0])
assert(position[1] in ['b', 'c', 't'])
if position[1] == 'c':
new_xy[1] -= relative_height/2
elif position[1] == 'b':
new_xy[1] -= relative_height + pad_y
else:
new_xy[1] += pad_y
t_.set_position(new_xy)
return t_
# generate data, plot it and annotate it!
axes_qty = 9
axes_gap = 0.035
fig = plt.figure(figsize=(10, 8))
ax = [plt.axes([axes_gap, axes_gap/2 + i*(1/axes_qty), 1-2*axes_gap, 1/axes_qty-axes_gap]) for i in range(axes_qty)]
rend = fig.canvas.get_renderer()
for a_ in ax:
x_ = [random.randint(0, 10) for _ in range(5)]
x_ = np.unique(x_)
y_ = [random.randint(0, 12) for _ in x_]
# as x is shared, we set the limits in advance, otherwise the adjustments won't be accurate
a_.set_xlim([-0.5, 10.5])
# plotting the data
data_ = [[x_[0], y_[0]]]
for i in range(1, len(x_)):
data_ += [[x_[i-1], y_[i]], [x_[i], y_[i]]]
a_.plot([d[0] for d in data_], [d[1] for d in data_])
mid_y = 0.5 * (a_.get_ylim()[0] + a_.get_ylim()[1])
# now let's label it
for i in range(len(x_)):
# decide what point we annotate
if i == 0:
xy = [x_ [0], y_[0]]
else:
xy = [x_[i-1], y_[i]]
# decide its position
position_0 = 'l' if i == 0 else 'r'
position_1 = 'b' if xy[1] > mid_y else 't'
place_label(label=str(xy[1]), xy=xy, position=position_0+position_1, ax=a_)
plt.show()
您可以在循环中使用 plt.annotate 函数来解决您的问题。
我随机生成了一些数据并将其绘制为单个图。你可以在子图中做同样的事情,功能是一样的。
# sample data points for the plot
x=np.arange(1,10)
y=np.linspace(20,40,9)
plt.figure(figsize=[15,5],dpi=200)
plt.plot(x,y,drawstyle='steps-post', marker='o')
# using annotate function to show the changepoints in a loop
for i in range(len(x)):
# I rounded the y values as string and used the same x and y coords as the locations
# next we can give a constant offset points to offset the annotation from each value
# here I used (-20,20) as the offset values
plt.annotate(f"{str(round((y[i])))}",(x[i],y[i]),xycoords='data',
xytext=(-20,20), textcoords='offset points',color="r",fontsize=12,
arrowprops=dict(arrowstyle="->", color='black'))
如果你不想要箭头,你可以移除箭头道具。
已编辑
我使用了您的 GitHub 存储库中的 example1.xml
文件并稍微编辑了函数。我所做的只是为您的函数添加一个循环和一个 if-else 条件。
# Initial part is same as yours
names_list = [name for name in signals_df['Name'].unique()]
num_names_list = len(names_list)
num_axisx = len(signals_df["Name"])
values_list = [value for pos, value in enumerate(signals_df["Value"])]
points_axisy = signals_df["Value"]
colors = ['b', 'g', 'r', 'c', 'm', 'y']
# start new figure
plt.figure(figsize=[20,28],dpi=200)
#start a loop with the subplots
for i in range(len(names_list)):
# subplot has 14 rows, 1 column and the i+1 represents the i'th plot
plt.subplot(num_names_list,1,i+1)
# choose color
col=np.random.randint(0, len(colors) - 1)
# get the locations of the values with the similar name in your list
locs=signals_df['Name']==names_list[i]
# get the values in those locations
data=signals_df['Value'][locs]
# arrange the x and y coordinates
x = np.hstack([-1, data.index.values, len(signals_df) - 1])
y = np.hstack([0, data.values, data.iloc[-1]])
# plot the values as usual
plt.plot(x, y, drawstyle='steps-post', marker='o', color=colors[col], linewidth=3)
plt.ylabel(names_list[i], fontsize=8, fontweight='bold', color='SteelBlue', rotation=30, labelpad=35)
plt.grid(alpha=0.4)
# this loop is for annotating the values
for j in range(len(x)):
# I found it is better to alternate the position of the annotations
# so that they wont overlap for the adjacent values
if j%2==0:
# In this condition the xytext position is (-20,20)
# this posts the annotation box over the plot value
plt.annotate(f"Val={round((y[j]))}",(x[j],y[j]),xycoords='data',
xytext=(-20,20), textcoords='offset points',color="r",fontsize=8,
arrowprops=dict(arrowstyle="->", color='black'),
bbox=dict(boxstyle='round', pad=0.5, fc='yellow', alpha=0.5))
else:
# In this condition the xytext position is (-20,-20)
# this posts the annotation box under the plot value
plt.annotate(f"Val={round((y[j]))}",(x[j],y[j]),xycoords='data',
xytext=(-20,-20), textcoords='offset points',color="r",fontsize=8,
arrowprops=dict(arrowstyle="->", color='black'),
bbox=dict(boxstyle='round', pad=0.5, fc='yellow', alpha=0.5))
新函数结果
希望有用。
我正在开发以下函数:extract_name_value() 生成一个步骤图,它采用 Python 中的 pandas DataFrame 的值,目前它工作正常,但是我想在每个标记中添加变量 points_axisyvalue 或 values_list 的值:Script Here
我尝试使用以下示例:Data value at each marker, Matplotlib scatter plot with different text at each data point or How to put individual tags for a matplotlib scatter plot?,这将是我想要的;我什至尝试过使用 plt.annotate()
,但是值的数据并没有按照我想要的方式出现,而且我认为它会掩盖很多图表并且不会很好地欣赏。下面是我使用 plt.annotate():
# Function to extract the Name and Value attributes
def extract_name_value(signals_df, rootXML):
# print(signals_df)
names_list = [name for name in signals_df['Name'].unique()]
num_names_list = len(names_list)
num_axisx = len(signals_df["Name"])
values_list = [value for pos, value in enumerate(signals_df["Value"])]
print(values_list)
points_axisy = signals_df["Value"]
print(len(points_axisy))
colors = ['b', 'g', 'r', 'c', 'm', 'y']
# Creation Graphic
fig, ax = plt.subplots(nrows=num_names_list, figsize=(20, 30), sharex=True)
plt.suptitle(f'File XML: {rootXML}', fontsize=16, fontweight='bold', color='SteelBlue', position=(0.75, 0.95))
plt.xticks(np.arange(-1, num_axisx), color='SteelBlue', fontweight='bold')
labels = ['value: {0}'.format(j) for j in values_list]
print(labels)
i = 1
for pos, name in enumerate(names_list):
# get data
data = signals_df[signals_df["Name"] == name]["Value"]
print(data)
# get color
j = random.randint(0, len(colors) - 1)
# get plots by index = pos
x = np.hstack([-1, data.index.values, len(signals_df) - 1])
y = np.hstack([0, data.values, data.iloc[-1]])
ax[pos].plot(x, y, drawstyle='steps-post', marker='o', color=colors[j], linewidth=3)
ax[pos].set_ylabel(name, fontsize=8, fontweight='bold', color='SteelBlue', rotation=30, labelpad=35)
ax[pos].yaxis.set_major_formatter(ticker.FormatStrFormatter('%0.1f'))
ax[pos].yaxis.set_tick_params(labelsize=6)
ax[pos].grid(alpha=0.4)
i += 1
for label, x, y in zip(labels, x, y):
plt.annotate(label, xy=(x, y), xytext=(-20, 20), textcoords='offset points', ha='right', va='bottom', bbox=dict(boxstyle='round,pad=0.5', fc='yellow', alpha=0.5),
arrowprops=dict(arrowstyle='->', connectionstyle='arc3,rad=0'))
plt.show()
我得到的是拼接在不同位置的注释。
但是,我的代码需要什么来显示每个点的每个值?
我也一直在尝试使用 Matplotlib 参考中的代码,但无法完成:Marker Reference。非常感谢您,任何评论都有帮助。
我想应该和你的要求很接近了。我随机生成数据,然后使用 matplotlib.text 对其进行注释。它不是很漂亮,您可能想添加一些填充和更多改进,但我希望它能提供一个好主意!
如果两点距离太近,您可能需要在左侧注释一个,在右侧注释另一个,就像我对第一个点所做的那样。在你举的例子中我没有看到这种情况,所以没有处理。
函数place_label(label, xy, position, ax, pad=0.01)
将标签放在您想要的位置。代码的其余部分使用随机生成的数据来证明它可以工作。
import random
import numpy as np
import matplotlib.pyplot as plt
# function that places the label give the desired position
def place_label(label, xy, position, ax, pad=0.01):
# annotate in the initial position, xy is the top right corner of the bounding box
t_ = ax.text(x=xy[0], y=xy[1], s=label, fontsize=16)
# find useful values
tbb = t_.get_window_extent(renderer=rend)
abb = ax.get_window_extent(renderer=rend)
a_xlim, a_ylim = ax.get_xlim(), a_.get_ylim()
# now adjust the position if needed
new_xy = [xy[0], xy[1]]
relative_width = tbb.width/abb.width * (a_xlim[1] - a_xlim[0])
pad_x = pad * (a_xlim[1] - a_xlim[0])
assert(position[0] in ['l', 'c', 'r'])
if position[0] == 'c':
new_xy[0] -= relative_width/2
elif position[0] == 'l':
new_xy[0] -= relative_width + pad_x
else:
new_xy[0] += pad_x
relative_height = tbb.height/abb.height * (a_ylim[1] - a_ylim[0])
pad_y = pad * (a_ylim[1] - a_ylim[0])
assert(position[1] in ['b', 'c', 't'])
if position[1] == 'c':
new_xy[1] -= relative_height/2
elif position[1] == 'b':
new_xy[1] -= relative_height + pad_y
else:
new_xy[1] += pad_y
t_.set_position(new_xy)
return t_
# generate data, plot it and annotate it!
axes_qty = 9
axes_gap = 0.035
fig = plt.figure(figsize=(10, 8))
ax = [plt.axes([axes_gap, axes_gap/2 + i*(1/axes_qty), 1-2*axes_gap, 1/axes_qty-axes_gap]) for i in range(axes_qty)]
rend = fig.canvas.get_renderer()
for a_ in ax:
x_ = [random.randint(0, 10) for _ in range(5)]
x_ = np.unique(x_)
y_ = [random.randint(0, 12) for _ in x_]
# as x is shared, we set the limits in advance, otherwise the adjustments won't be accurate
a_.set_xlim([-0.5, 10.5])
# plotting the data
data_ = [[x_[0], y_[0]]]
for i in range(1, len(x_)):
data_ += [[x_[i-1], y_[i]], [x_[i], y_[i]]]
a_.plot([d[0] for d in data_], [d[1] for d in data_])
mid_y = 0.5 * (a_.get_ylim()[0] + a_.get_ylim()[1])
# now let's label it
for i in range(len(x_)):
# decide what point we annotate
if i == 0:
xy = [x_ [0], y_[0]]
else:
xy = [x_[i-1], y_[i]]
# decide its position
position_0 = 'l' if i == 0 else 'r'
position_1 = 'b' if xy[1] > mid_y else 't'
place_label(label=str(xy[1]), xy=xy, position=position_0+position_1, ax=a_)
plt.show()
您可以在循环中使用 plt.annotate 函数来解决您的问题。
我随机生成了一些数据并将其绘制为单个图。你可以在子图中做同样的事情,功能是一样的。
# sample data points for the plot
x=np.arange(1,10)
y=np.linspace(20,40,9)
plt.figure(figsize=[15,5],dpi=200)
plt.plot(x,y,drawstyle='steps-post', marker='o')
# using annotate function to show the changepoints in a loop
for i in range(len(x)):
# I rounded the y values as string and used the same x and y coords as the locations
# next we can give a constant offset points to offset the annotation from each value
# here I used (-20,20) as the offset values
plt.annotate(f"{str(round((y[i])))}",(x[i],y[i]),xycoords='data',
xytext=(-20,20), textcoords='offset points',color="r",fontsize=12,
arrowprops=dict(arrowstyle="->", color='black'))
如果你不想要箭头,你可以移除箭头道具。
已编辑
我使用了您的 GitHub 存储库中的 example1.xml
文件并稍微编辑了函数。我所做的只是为您的函数添加一个循环和一个 if-else 条件。
# Initial part is same as yours
names_list = [name for name in signals_df['Name'].unique()]
num_names_list = len(names_list)
num_axisx = len(signals_df["Name"])
values_list = [value for pos, value in enumerate(signals_df["Value"])]
points_axisy = signals_df["Value"]
colors = ['b', 'g', 'r', 'c', 'm', 'y']
# start new figure
plt.figure(figsize=[20,28],dpi=200)
#start a loop with the subplots
for i in range(len(names_list)):
# subplot has 14 rows, 1 column and the i+1 represents the i'th plot
plt.subplot(num_names_list,1,i+1)
# choose color
col=np.random.randint(0, len(colors) - 1)
# get the locations of the values with the similar name in your list
locs=signals_df['Name']==names_list[i]
# get the values in those locations
data=signals_df['Value'][locs]
# arrange the x and y coordinates
x = np.hstack([-1, data.index.values, len(signals_df) - 1])
y = np.hstack([0, data.values, data.iloc[-1]])
# plot the values as usual
plt.plot(x, y, drawstyle='steps-post', marker='o', color=colors[col], linewidth=3)
plt.ylabel(names_list[i], fontsize=8, fontweight='bold', color='SteelBlue', rotation=30, labelpad=35)
plt.grid(alpha=0.4)
# this loop is for annotating the values
for j in range(len(x)):
# I found it is better to alternate the position of the annotations
# so that they wont overlap for the adjacent values
if j%2==0:
# In this condition the xytext position is (-20,20)
# this posts the annotation box over the plot value
plt.annotate(f"Val={round((y[j]))}",(x[j],y[j]),xycoords='data',
xytext=(-20,20), textcoords='offset points',color="r",fontsize=8,
arrowprops=dict(arrowstyle="->", color='black'),
bbox=dict(boxstyle='round', pad=0.5, fc='yellow', alpha=0.5))
else:
# In this condition the xytext position is (-20,-20)
# this posts the annotation box under the plot value
plt.annotate(f"Val={round((y[j]))}",(x[j],y[j]),xycoords='data',
xytext=(-20,-20), textcoords='offset points',color="r",fontsize=8,
arrowprops=dict(arrowstyle="->", color='black'),
bbox=dict(boxstyle='round', pad=0.5, fc='yellow', alpha=0.5))
新函数结果
希望有用。