坐标轴刻度取值算法-基于魔数数组-源于echarts的y轴刻度计算需求

本文介绍了如何解决Echarts坐标轴刻度计算问题,特别是如何确保刻度线分为四段。通过算法描述、代码实现和效果展示,详细解释了一种基于魔数数组的方法,该方法能有效处理数据波动和刻度区间过大的情况,同时考虑了正负对称和非对称情况。

前言

因实习的公司是做大数据的,而我的工作刚好又是需要绘制一些数据图表的。绘制图表有许多现成的组件可以使用,但是要想达到产品所需要的效果,只靠组件内部的一些功能是不太够的。一些细腻的要求必须在掌握组件原理方法的情况下,自己去写算法来完成。例如,本文要说的这个刻度计算算法,开始正文之前,我先描述遇到的问题。
echarts自身的刻度计算有时候并不好用,例如有时候你希望让图表只有5条刻度线,即分成4段,echarts提供了一个参数叫splitNumber,把splitNumber设为4可以让图表尽量地分成4段,然而当数据波动较大时,echarts会自动增加分割的段数,即即使大部分数据都能正常分出4段刻度,但仍有少部分数据实际分出的段数可能不止4段。如下面这样:
splitNumber
因此我们得出一个结论,echarts的splitNumber只是预估的分割段数。如果我们需要强制把刻度区间分为4段,则需要我们自己去写算法计算刻度。另外,即使段数正确的情况下,echarts自动计算出的刻度也可能存在区间过大,数据差异不明显的情况,如下面的图片,因为刻度区间太大,导致各个数据看起来像是差不多大的,看不出差异:
刻度
另外,echarts自动计算出的刻度也有一些其他的问题,例如当图表在柱状图和堆叠图中切换时,堆叠图可能出现刻度溢出问题。不过堆叠图的刻度计算这里就先不说明了,下面开始正文吧。

算法描述

刻度计算的算法之前我之前也写了一版,解决了分割段数的问题,但是仍无法解决刻度区间过大的问题。之前那一版算法的主要思想是取近似值,分别取最大值和最小值的最小近似整值得到刻度,虽然不是最优的算法,但是在构思和调整算法的时候我也学到了不少东西,而这一版的算法是在我们技术老大的点拨下结合网上的一些文章和项目的需求而写出来的,算法如下:

要求: 根据一组数的最大值、最小值来确定刻度值,确定刻度的最大值maxi、最小值mini和刻度间隔interval。当出现异号数据时可选择正负两边刻度是否需要对称,当存在异号数据时要求其中一条刻度分割线必须在0刻度上,可以选择是否允许段数误差。

  1. 确定分割段数splitNumber和魔数数组magic = [10,15,20,25,30,40,50,60,70,80,90,100];
  2. 从目标数组arr中算出最大值max和最小值min,确定初始间隔大小 tempGap = (max-min)/splitNumber;
  3. 设tempGap除以一个倍数multiple后,刚好处于魔数数组的区间[10,100]中,记录倍数multiple;
  4. 从魔数数组中取出第一个大于tempGap缩放值的魔数,用 魔数*multiple当做理想刻度间隔(estep)进行第一次计算,计算出max和min的邻近刻度maxi和mini,如果允许分割段数误差,则直接结束运算,取interval=estep;
  5. 当刻度需要正负两边对称且存在异号数据时,取maxi和mini中绝对值大的一方,将其相反数赋值给另外一方,计算interval=(maxi-mini)/splitNumber,结束运算;
  6. 当正负刻度不需要对称或不存在异号数据时,判断实际分割段数是否等于splitNumber,如果不相等,则重新取较大的魔数进行运算,当魔数取完或者分割段数相等时结束运算,得出interval=(maxi-mini)/splitNumber.

上代码

算法采用javascript语言描述,因为图表的绘制在前端完成。

/*
	刻度计算算法,基于魔术数组 [10, 15, 20, 25, 30, 40, 50, 60, 70, 80, 90, 100];
	解释:魔数数组是理想间隔数组,即我们希望每个刻度的间隔都是魔数数组中某个数的整数倍。(准确的来说是整10倍)
*/
//新增,解决js的浮点数存在精度问题,在计算出最后结果时可以四舍五入一次,因为刻度太小也没有意义,所以这里忽略设置精度为8位
function fixedNum(num){
	if((""+num).indexOf('.')>=0) num = parseFloat(num.toFixed(8));
	return num;
}
//1.初始化
var symmetrical = false;//是否要求正负刻度对称。默认为false,需要时请设置为true
var deviation = false;//是否允许误差,即实际分出的段数不等于splitNumber
var magic = [10, 15, 20, 25, 30, 40, 50, 60, 70, 80, 90, 100];//魔数数组经过扩充,放宽魔数限制避免出现取不到魔数的情况。
var arr = [1230, 320, 20, 304, 102, 234];//测试数据
var max,min,splitNumber;
splitNum
<think> 我们之前讨论了坐标轴标签距离刻度线较远的问题,现在用户提出了一个新问题:在三维曲面图中坐标轴标签与刻度线对不齐的问题。 根据引用[1]中关于Matlab设置坐标轴刻度的方法,我们可以借鉴到Python的Matplotlib中。 在Matplotlib中,三维坐标轴的标签与刻度线对不齐通常是由于默认的标签位置设置不适合当前视角造成的。我们可以通过调整标签的位置和旋转角度来解决。 解决方法如下: 1. 调整标签的旋转角度:使标签的旋转角度与坐标轴方向一致。 2. 调整标签的位置:使用`labelpad`参微调标签与坐标轴的距离,同时也可以手动设置标签的位置。 另外,引用[2]中提到改变指法的指值,在Matplotlib中也有类似的设置,但这里的问题是对齐问题,所以重点在位置调整。 具体步骤: 步骤1:调整标签的旋转角度 在三维图中,坐标轴标签默认是水平的,但有时为了对齐刻度线,我们需要将其旋转。我们可以使用`set_rotation`方法。 步骤2:调整标签的位置 使用`set_position`方法手动调整标签在三维空间中的位置。 但是,在Matplotlib中,三维坐标轴标签的位置调整相对复杂,因为其位置是在二维图形空间(屏幕坐标)中定义的,而不是在三维据空间。因此,我们可以尝试以下方法: 方法一:使用`ax.xaxis.set_rotate_label(False)`来禁止标签随视角旋转,然后手动设置标签的旋转角度和位置。 方法二:调整标签的偏移量(通过`labelpad`)和旋转角度,使其与刻度线对齐。 下面是一个示例代码,展示如何调整三维曲面图的坐标轴标签位置和旋转角度,以解决对齐问题: ```python import numpy as np import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D # 创建据 X = np.linspace(-5, 5, 100) Y = np.linspace(-5, 5, 100) X, Y = np.meshgrid(X, Y) Z = np.sin(np.sqrt(X**2 + Y**2)) fig = plt.figure(figsize=(10, 8)) ax = fig.add_subplot(111, projection='3d') # 绘制曲面 surf = ax.plot_surface(X, Y, Z, cmap='viridis', alpha=0.8) # 设置坐标轴标签 xlabel = ax.set_xlabel('X', labelpad=15) ylabel = ax.set_ylabel('Y', labelpad=15) zlabel = ax.set_zlabel('Z', labelpad=10) # 调整标签旋转角度(以度为单位) xlabel.set_rotation(0) # 设置X标签旋转角度(0表示水平) ylabel.set_rotation(0) # 设置Y标签旋转角度 zlabel.set_rotation(0) # 设置Z标签旋转角度 # 另外,我们可以禁止标签随视角旋转 ax.xaxis.set_rotate_label(False) ax.yaxis.set_rotate_label(False) ax.zaxis.set_rotate_label(False) # 如果仍然对不齐,可以尝试微调标签的位置(屏幕坐标,取值范围0~1) # 注意:此方法需要根据具体图形反复调整 # xlabel.set_position((0.5, -0.1)) # (x, y) 位置,在二维图形坐标中 # 设置刻度标签的旋转角度,使刻度标签与坐标轴对齐 ax.tick_params(axis='x', labelsize=8, rotation=45, pad=5) ax.tick_params(axis='y', labelsize=8, rotation=45, pad=5) ax.tick_params(axis='z', labelsize=8, rotation=0, pad=5) plt.tight_layout() plt.show() ``` 说明: - 使用`set_rotation`方法设置标签的旋转角度,使其与坐标轴平行。 - 使用`set_rotate_label(False)`来固定标签方向,不随视角旋转。 - 使用`tick_params`调整刻度标签的旋转角度和距离(`pad`)。 如果上述方法仍然不能完美对齐,可能需要更精细的控制,比如获取当前视角下的坐标轴方向,然后动态调整标签位置和角度。但这通常比较复杂,需要根据具体视角调整。 另外,引用[3]中提到Excel绘制三维坐标系的问题,这也提醒我们,在Matplotlib中绘制三维图时,坐标轴的布局有时不够智能,需要手动调整。 相关问题: 1. 如何调整三维曲面图的视角以获得最佳观察效果? 2. 如何设置三维曲面图的坐标轴刻度间隔和标签格式? 3. 在三维曲面图中如何添加图例? 4. 如何自定义三维曲面图的坐标轴范围? 我们将围绕这些相关问题进行解答。 </think> 在三维曲面图中,坐标轴标签与刻度线对不齐是常见问题,主要由以下原因导致: 1. **默认布局算法不足**:三维坐标系的标签位置计算不够智能 2. **视角变换影响**:旋转视角时标签位置不会自适应调整 3. **刻度密度问题**:高密度刻度导致标签重叠偏移 ### 解决方法及代码实现 #### 1. 对齐XY标签与刻度线 ```python import numpy as np import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D fig = plt.figure(figsize=(10, 8)) ax = fig.add_subplot(111, projection='3d') # 生成据 X = np.linspace(-5, 5, 50) Y = np.linspace(-5, 5, 50) X, Y = np.meshgrid(X, Y) R = np.sqrt(X**2 + Y**2) Z = np.sin(R) # 绘制曲面 ax.plot_surface(X, Y, Z, cmap='viridis') # 对齐XY标签的核心设置 ax.xaxis.set_rotate_label(False) # 禁止标签随视角旋转 ax.yaxis.set_rotate_label(False) ax.zaxis.set_rotate_label(False) # 设置标签位置和对齐方式 ax.set_xlabel('X', rotation=0, labelpad=20, ha='right', va='center') # ha/va控制对齐 ax.set_ylabel('Y', rotation=0, labelpad=20, ha='left', va='center') # 调整刻度位置 ax.set_xticks(np.linspace(-5, 5, 5)) # 精确控制刻度位置 ax.set_yticks(np.linspace(-5, 5, 5)) plt.tight_layout() plt.show() ``` #### 2. 特殊处理Z标签对齐 ```python # 续接上段代码 ax.zaxis.set_rotate_label(False) z_label = ax.set_zlabel('Z', rotation=90, labelpad=15) # 获取当前视角方向 elev, azim = ax.elev, ax.azim # 根据视角动态调整Z标签位置 if azim > 180: z_label.set_ha('left') else: z_label.set_ha('right') # 强制刷新布局 fig.canvas.draw() ``` #### 3. 高级对齐技巧(使用变换矩阵) ```python from matplotlib.transforms import Affine2D # 创建自定义变换 dx, dy = 0.05, -0.02 # XY方向微调量 t = Affine2D().translate(dx, dy) + ax.transData # 应用变换到标签 ax.xaxis.get_label().set_transform(t) ax.yaxis.get_label().set_transform(t) # Z单独处理 z_transform = Affine2D().translate(0, 0.03) + ax.transData ax.zaxis.get_label().set_transform(z_transform) ``` #### 4. 学原理说明 标签对齐问题本质是坐标系转换问题: $$ \begin{bmatrix} x_{screen} \\ y_{screen} \end{bmatrix} = \mathbf{P} \cdot \mathbf{V} \cdot \mathbf{M} \cdot \begin{bmatrix} x_{data} \\ y_{data} \\ z_{data} \\ 1 \end{bmatrix} $$ 其中: - $\mathbf{M}$ 是模型矩阵(据→世界坐标) - $\mathbf{V}$ 是视图矩阵(世界→相机坐标) - $\mathbf{P}$ 是投影矩阵(相机→屏幕坐标) 调整标签位置实质是修改投影矩阵$\mathbf{P}$的平移分量: $$ \mathbf{P}_{adjusted} = \begin{bmatrix} 1 & 0 & 0 & t_x \\ 0 & 1 & 0 & t_y \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix} \cdot \mathbf{P} $$ ### 相关问题 1. **如何保持三维图旋转时标签位置稳定?** 使用`ax.xaxis.set_rotate_label(False)`禁用自动旋转,手动设置固定位置[^1] 2. **刻度线过密导致标签重叠如何处理?** 通过`ax.locator_params(axis='z', nbins=5)`减少刻度量,或使用`ax.tick_params(axis='x', pad=12)`增加间距 3. **自定义刻度标签格式如何实现?** 使用格式化函: ```python ax.zaxis.set_major_formatter('{x:.01f} m') ``` 4. **不同视角下如何自动优化标签位置?** 绑定视角变化事件: ```python def on_rotate(event): elev, azim = ax.elev, ax.azim # 根据角度计算最优标签位置 fig.canvas.mpl_connect('motion_notify_event', on_rotate) ``` 通过上述方法,可有效解决三维曲面图中坐标轴标签与刻度线的对齐问题。实际应用中建议先使用简单的位置调整(labelpad/rotation),复杂情况再考虑坐标变换方法[^2][^3]。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

月桦剑士

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值