gnuplot 如何将跨日期数据的 x 轴限定为每天 04:00 至 22:00?
数据小许
结论
如果要把不同日期的数据放在同一条 04
至 22 的时间轴上,先按完整格式读取日期时间,再将其换算成从当天午夜起经过的秒数。原来的 set xrange [14400:79200] 可以用于日内秒数坐标。主要问题是 timecolumn(1,"%H:%M") 与第一列的实际内容不匹配;字符串范围报错还与全局 timefmt 的格式有关。
为什么会出现错误
第一列的实际内容是:
2024-04-05T15:50:51+02
原来的绘图表达式是:
timecolumn(1,"%H:%M")
timecolumn 从指定列的开头解析,不会自动跳过日期去寻找小时。显式传入的 "%H:%M" 会覆盖全局 set timefmt,所以之前设置的完整日期格式无法修正这次读取。使用错误格式得到的 20
set format x "%H:%M" 只控制刻度显示,不会删除 x 坐标中的日期部分。字符串形式的范围端点使用全局 timefmt 解析,而完整日期格式与 "09:00"、"22:00" 不匹配,因此需要分别设置数据和范围的解析格式。gnuplot 时间数据文档
可直接使用的代码
以下代码按输入中写出的时刻绘图,将不同日期映射到同一天的时间轴:
$Data << EOD
2024-04-05T15:50:51+02 2.459
2025-06-06T21:30:51+02 2.339
2026-04-06T09:15:10+02 3.219
EOD
set terminal wxt enhanced
set encoding utf8
set xlabel "Time"
set ylabel "Price"
# Parse the full timestamp from column 1.
datafmt = "%Y-%m-%dT%H:%M:%S+02"
# Convert a timestamp to seconds since midnight.
dayseconds(t) = 3600*tm_hour(t) + 60*tm_min(t) + tm_sec(t)
set xdata time
# Parse string range endpoints as hours and minutes.
set timefmt "%H:%M"
set xrange ["04:00":"22:00"]
set format x "%H:%M"
set xtics 7200
set yrange [0:]
set grid
plot $Data using (dayseconds(timecolumn(1,datafmt))):2 \
with points pt 5 lc -1 notitle
tm_hour()、tm_min() 和 tm_sec() 分别提取时间戳中的小时、分钟和秒,转换后的坐标只保留一天中的时刻。gnuplot 的时间分量函数文档
按上述方法转换后,三个数据点的 x 坐标分别为:
| 输入时刻 | 从午夜起经过的秒数 |
|---|---|
| 15:50 | 57051 |
| 21:30 | 77451 |
| 09:15 | 33310 |
这些数值都在 14400 至 79200 之间。
如何使用数值形式的范围
上述代码中的范围也可以直接替换为:
set xrange [14400:79200]
它对应 04
至 22,前提是绘图表达式已经将完整日期时间转换为日内秒数。直接绘制完整时间戳时,不能使用这一范围。如果希望从 09
开始,字符串写法为:set timefmt "%H:%M"
set xrange ["09:00":"22:00"]
日期、时区和连线需要注意什么
- 示例中的
+02在datafmt中是固定匹配文本。代码按原字符串中的小时绘图,没有执行时区换算;若输入包含不同偏移,需先确定要比较的是各地时刻还是统一时区的时刻。 - 转换后,日期信息不再参与 x 坐标。若需要保留跨年时间顺序,应直接绘制完整时间戳,并设置包含日期的范围;仅将刻度显示为
%H:%M无法实现日内映射。 - 示例使用散点,方便比较不同日期的日内价格。改为
with linespoints后,数据点会按输入记录顺序连接,原数据会从 21:30 连回 09:15。需要日内折线时,应先按日内时刻排序,并确认跨日期连线符合需求。
备注:内容仅供参考。
