个体轨迹图 (Spaghetti Plot)¶
用途:展示纵向数据中每个受试者随时间的变化轨迹,叠加组均值趋势线。
交互式图表¶
浅色半透明线 = 个体轨迹,粗实线 = 组均值趋势。悬停查看具体数值。
生成代码¶
library(plotly)
# 模拟数据
set.seed(123)
n_subj <- 30
visits <- c(0, 4, 8, 12, 16, 20, 24)
spag_data <- do.call(rbind, lapply(1:n_subj, function(i) {
trt <- ifelse(i <= 15, "Drug", "Placebo")
bl <- rnorm(1, 50, 10)
slope <- ifelse(trt == "Drug", -2, -0.5)
data.frame(
Subject = paste0("S", i),
Treatment = trt,
Visit = visits,
Value = bl + slope * visits + rnorm(length(visits), 0, 3)
)
}))
# 个体轨迹
fig <- plot_ly(spag_data, x = ~Visit, y = ~Value,
color = ~Treatment,
split = ~Subject,
type = "scatter", mode = "lines+markers",
opacity = 0.4, line = list(width = 0.5),
showlegend = FALSE)
# 叠加组均值
group_mean <- aggregate(Value ~ Treatment + Visit, spag_data, mean)
fig <- fig |> add_trace(data = group_mean,
x = ~Visit, y = ~Value,
color = ~Treatment,
type = "scatter", mode = "lines+markers",
opacity = 1, line = list(width = 3),
name = ~Treatment)
fig
import plotly.express as px
import pandas as pd
import numpy as np
np.random.seed(123)
n_subj = 30
visits = [0, 4, 8, 12, 16, 20, 24]
rows = []
for i in range(n_subj):
trt = "Drug" if i < 15 else "Placebo"
bl = np.random.normal(50, 10)
slope = -2 if trt == "Drug" else -0.5
for v in visits:
rows.append({
"Subject": f"S{i+1}", "Treatment": trt,
"Visit": v, "Value": bl + slope*v + np.random.normal(0, 3)
})
spag_data = pd.DataFrame(rows)
fig = px.line(spag_data, x="Visit", y="Value",
color="Treatment", line_group="Subject",
opacity=0.3,
title="Spaghetti Plot — Individual Trajectories")
# 叠加组均值
means = spag_data.groupby(["Treatment", "Visit"])["Value"].mean().reset_index()
fig.add_scatter(x=means[means.Treatment=="Drug"].Visit,
y=means[means.Treatment=="Drug"].Value,
mode="lines+markers", name="Drug (Mean)",
line=dict(width=4, color="blue"))
fig.show()