如何在散点图周围均匀排布标注图像框?
散点图图像标注环形排布优化
我需要给散点图的每个数据点添加图像标注,但默认设置下图像会重叠,还会遮挡坐标轴、图例等关键元素。目标是让这些图像以圆形的形式排布在主散点图的外围。
当前使用的代码如下:
import matplotlib.cbook as cbook import pandas as pd import numpy as np from matplotlib import pyplot as plt from matplotlib.offsetbox import OffsetImage, AnnotationBbox import seaborn as sns #Generate n points around a 2d circle def generate_circle_points(n, centre_x, center_y, radius=1): """Generate n points around a circle. Args: n (int): Number of points to generate. centre_x (float): x-coordinate of circle centre. center_y (float): y-coordinate of circle centre. radius (float): Radius of circle. Returns: list: List of points. """ points = [] for i in range(n): angle = 2 * np.pi * i / n x = centre_x + radius * np.cos(angle) y = center_y + radius * np.sin(angle) points.append([x, y]) return points fig, ax = plt.subplots(1, 1, figsize=(7.5, 7.5)) data = pd.DataFrame(data={'x': np.random.uniform(0.5, 2.5, 20), 'y': np.random.uniform(10000, 50000, 20)}) with cbook.get_sample_data('grace_hopper.jpg') as image_file: image = plt.imread(image_file) # Set logarithmic scale for x and y axis ax.set(xscale="log", yscale='log') # Add grid ax.grid(True, which='major', ls="--", c='gray') coordianates = generate_circle_points(n=len(data), centre_x=0, center_y=0, radius=10) # Plot the scatter plot scatter = sns.scatterplot(data=data, x='x', y='y', ax=ax) for index, row in data.iterrows(): imagebox = OffsetImage(image, zoom=0.05) imagebox.image.axes = ax xy = np.array([row['x'], row['y']]) xybox = np.array(coordianates[index]) ab = AnnotationBbox(imagebox, xy, xycoords='data', boxcoords="offset points", xybox=xybox, pad=0) ax.add_artist(ab)
问题分析
当前代码的核心问题在于:
- 环形坐标生成时使用了数据坐标系的(0,0),但散点图的x/y轴是对数尺度,该点不在可视范围内
boxcoords="offset points"设置让环形坐标成为数据点的偏移量,而非围绕图中心的绝对位置
解决方案
要实现图像围绕图中心排布,需要切换到轴坐标系生成环形位置,并调整标注的坐标参数:
修改后的完整代码:
import matplotlib.cbook as cbook import pandas as pd import numpy as np from matplotlib import pyplot as plt from matplotlib.offsetbox import OffsetImage, AnnotationBbox import seaborn as sns def generate_circle_points(n, centre_x=0.5, center_y=0.5, radius=0.35): """基于轴坐标系生成环形点,轴坐标系范围[0,1]对应整个绘图区域""" points = [] for i in range(n): angle = 2 * np.pi * i / n x = centre_x + radius * np.cos(angle) y = center_y + radius * np.sin(angle) points.append([x, y]) return points fig, ax = plt.subplots(1, 1, figsize=(7.5, 7.5)) data = pd.DataFrame(data={'x': np.random.uniform(0.5, 2.5, 20), 'y': np.random.uniform(10000, 50000, 20)}) with cbook.get_sample_data('grace_hopper.jpg') as image_file: image = plt.imread(image_file) ax.set(xscale="log", yscale='log') ax.grid(True, which='major', ls="--", c='gray') # 基于轴坐标系生成环形坐标,半径适配图尺寸 coordianates = generate_circle_points(n=len(data)) scatter = sns.scatterplot(data=data, x='x', y='y', ax=ax) for index, row in data.iterrows(): imagebox = OffsetImage(image, zoom=0.05) imagebox.image.axes = ax # 数据点坐标(数据坐标系) xy_data = (row['x'], row['y']) # 图像环形位置(轴坐标系) xy_image = coordianates[index] # 创建标注:图像固定在轴坐标系的环形位置,连线到对应数据点 ab = AnnotationBbox(imagebox, xy_data, xycoords='data', boxcoords='axes fraction', xybox=xy_image, pad=0.3, arrowprops=dict(arrowstyle="-", color='gray', lw=0.8)) ax.add_artist(ab) plt.tight_layout() plt.show()
修改说明
- 环形坐标生成:改用轴坐标系的(0.5,0.5)作为中心,半径设为0.35,确保图像分布在绘图区域外围
- 坐标系统切换:
boxcoords='axes fraction'让图像位置不受数据尺度影响,固定在图的环形区域 - 添加关联连线:通过
arrowprops添加灰色连线,将图像与对应数据点关联,同时避免遮挡主图元素 - 调整间距:增加
pad值防止图像之间重叠
内容的提问来源于stack exchange,提问作者Harold Grosjean
相关产品推荐
相关产品推荐

