
本文介绍如何基于车辆行程数据(含起止时间与单程距离),绘制一条连续的累计距离-时间曲线,准确反映行驶(斜线)与静止(水平线)状态,解决 `plt.plot` 直接连接离散点导致逻辑断裂的问题。
要绘制符合物理意义的“累计距离 vs. 时间”折线图(即:行程中匀速运动 → 斜线段;停车等待 → 水平线段),关键在于显式构造时间-距离事件序列,而非仅对起点/终点分别绘图。原始尝试失败的根本原因在于:plt.plot() 默认将输入点按顺序连线,但未插入“行程结束即下一程开始前”的静止时段节点,导致斜线直接跳转、缺失水平段。
下面提供一个健壮、可扩展的解决方案,使用 pandas + matplotlib 实现,并支持真实时间解析(推荐使用 datetime 类型提升精度与可读性):
✅ 正确做法:构建完整事件时间线
我们需为每一段行程生成两个关键事件点:
- 行程开始:时间 = Trip_Start,累计距离 = 当前累计值(即前序总和)
- 行程结束:时间 = Trip_End,累计距离 = 当前累计值 + 本段距离
同时,为体现“停车”状态,还需在上一段结束与下一段开始之间插入静止段——即若 Trip_End[i]
立即学习“Python免费学习笔记(深入)”;
以下是完整实现代码(已适配你的示例数据,并增强鲁棒性):
import pandas as pd
import matplotlib.pyplot as plt
from datetime import datetime
# 原始数据(转为 DataFrame,便于处理)
df = pd.DataFrame({
'veh_ID': [102, 102],
'Trip_ID': [1, 2],
'Trip_Start': ['08:00', '11:00'],
'Trip_end': ['10:00', '11:10'],
'Travel distance': [12, 1]
})
# ✅ 步骤1:转换时间为 datetime 对象(支持跨天、更精确)
def to_timedelta(t_str):
return pd.to_timedelta(t_str)
df['Trip_Start_dt'] = df['Trip_Start'].apply(to_timedelta)
df['Trip_end_dt'] = df['Trip_end'].apply(to_timedelta)
# ✅ 步骤2:按时间排序(确保时序正确)
df = df.sort_values('Trip_Start_dt').reset_index(drop=True)
# ✅ 步骤3:构建事件序列(time, cumulative_distance)
events = []
cum_dist = 0
for idx, row in df.iterrows():
start_t = row['Trip_Start_dt']
end_t = row['Trip_end_dt']
dist = row['Travel distance']
# 1. 行程开始:累计距离不变(延续上一段结束值)
events.append((start_t, cum_dist))
# 2. 行程结束:累计距离增加
cum_dist += dist
events.append((end_t, cum_dist))
# ✅ 步骤4:插入停车段(若存在间隙)
for i in range(len(df) - 1):
curr_end = df.iloc[i]['Trip_end_dt']
next_start = df.iloc[i+1]['Trip_Start_dt']
if next_start > curr_end:
# 停车:距离不变,时间从 curr_end → next_start
events.append((curr_end, events[-1][1])) # 当前终点距离
events.append((next_start, events[-1][1])) # 下一程起点,距离不变
# ✅ 步骤5:转为 DataFrame 并绘图
timeline = pd.DataFrame(events, columns=['Time', 'Cumulative Distance'])
plt.figure(figsize=(10, 5))
plt.plot(timeline['Time'], timeline['Cumulative Distance'],
drawstyle='steps-post', # 关键!用阶梯后置模式模拟"瞬时到达+停留"
marker='o', linewidth=2, markersize=5)
plt.xlabel('Time of Day')
plt.ylabel('Cumulative Distance (km)')
plt.title('Vehicle #102: Cumulative Distance vs Time')
plt.grid(True, alpha=0.3)
plt.xticks(rotation=30)
plt.tight_layout()
plt.show()? 关键说明:drawstyle='steps-post' 是核心技巧:它让每段水平线在 x 值变化前保持 y 不变,完美对应“到达即停止、停留至下一程开始”的语义;使用 pd.to_timedelta 而非手动拆分字符串,避免时区/跨日问题,且天然支持 matplotlib 时间轴自动格式化;插入停车段逻辑确保图中不会出现“跳跃”,所有静止期均以水平线显式呈现。
⚠️ 注意事项
- 若数据含跨日行程(如 '23:50' → '00:15'),请改用 pd.to_datetime() 配合日期列,或统一加基准日期;
- 多车辆绘图时,建议按 veh_ID 分组循环处理,并用不同颜色/标签区分;
- 如需标注每段行程,可在 plt.annotate() 中添加 Trip_ID 文本。
该方法输出为一条逻辑清晰、物理可解释的连续折线图——斜线代表移动中距离增长,水平线代表停车等待,真正实现了“一车一线、全程可视”。










