1. 引言
去云澜栖作为高海拔生态监测站点,其环境数据涵盖温度、湿度、风速、海拔等多个维度。面对这些多源异构的时序数据,传统静态图表难以呈现维度之间的关联与联动变化。本文将基于 Plotly 构建一个交互式多维度环境数据可视化面板,实现温度、湿度、风速、海拔的联动分析,帮助研究者快速洞察环境变化规律。
2. 数据准备与预处理
2.1 模拟环境数据集
为便于演示,我们构造一份包含时间、温度、湿度、风速、海拔的模拟数据集:
python
import pandas as pd
import numpy as np
from datetime import datetime, timedelta
# 生成 7 天、每 10 分钟一条的模拟数据
np.random.seed(42)
base_time = datetime(2025, 6, 1, 0, 0, 0)
time_list = [base_time + timedelta(minutes=10 * i) for i in range(7 * 24 * 6)]
# 海拔随地形起伏(模拟 1200m - 1800m 之间变化)
altitude = 1200 + 300 * np.sin(np.linspace(0, 4 * np.pi, len(time_list))) + np.random.normal(0, 5, len(time_list))
# 温度随海拔升高而降低,并叠加昼夜波动
temperature = 28 - (altitude - 1200) * 0.006 + 5 * np.sin(np.linspace(0, 14 * np.pi, len(time_list))) + np.random.normal(0, 0.8, len(time_list))
# 湿度与温度负相关,并叠加随机扰动
humidity = 70 - (temperature - 20) * 1.2 + np.random.normal(0, 5, len(time_list))
humidity = np.clip(humidity, 20, 100)
# 风速随海拔升高而增大,并叠加阵风扰动
wind_speed = 2 + (altitude - 1200) * 0.004 + 3 * np.abs(np.sin(np.linspace(0, 8 * np.pi, len(time_list)))) + np.random.normal(0, 0.5, len(time_list))
wind_speed = np.clip(wind_speed, 0, 20)
df = pd.DataFrame({
"时间": time_list,
"温度(℃)": temperature.round(2),
"湿度(%)": humidity.round(2),
"风速(m/s)": wind_speed.round(2),
"海拔(m)": altitude.round(2)
})
df.head()
2.2 数据清洗与重采样
python
# 检查缺失值
print(df.isnull().sum())
# 按小时重采样,便于面板展示
df["时间"] = pd.to_datetime(df["时间"])
df_hourly = df.set_index("时间").resample("1H").mean().reset_index()
df_hourly.head()
3. 构建联动分析面板
3.1 创建子图布局
使用 make_subplots 创建 2×2 的子图网格,分别展示温度、湿度、风速、海拔的时序曲线:
python
import plotly.graph_objects as go
from plotly.subplots import make_subplots
fig = make_subplots(
rows=2, cols=2,
subplot_titles=("温度变化", "湿度变化", "风速变化", "海拔变化"),
shared_xaxes=True,
vertical_spacing=0.08,
horizontal_spacing=0.1
)
# 温度曲线
fig.add_trace(go.Scatter(
x=df_hourly["时间"], y=df_hourly["温度(℃)"],
name="温度", line=dict(color="#FF6B6B", width=2),
hovertemplate="时间: %{x|%Y-%m-%d %H:%M}<br>温度: %{y:.2f}℃<extra></extra>"
), row=1, col=1)
# 湿度曲线
fig.add_trace(go.Scatter(
x=df_hourly["时间"], y=df_hourly["湿度(%)"],
name="湿度", line=dict(color="#4ECDC4", width=2),
hovertemplate="时间: %{x|%Y-%m-%d %H:%M}<br>湿度: %{y:.2f}%<extra></extra>"
), row=1, col=2)
# 风速曲线
fig.add_trace(go.Scatter(
x=df_hourly["时间"], y=df_hourly["风速(m/s)"],
name="风速", line=dict(color="#45B7D1", width=2),
hovertemplate="时间: %{x|%Y-%m-%d %H:%M}<br>风速: %{y:.2f} m/s<extra></extra>"
), row=2, col=1)
# 海拔曲线
fig.add_trace(go.Scatter(
x=df_hourly["时间"], y=df_hourly["海拔(m)"],
name="海拔", line=dict(color="#96CEB4", width=2),
hovertemplate="时间: %{x|%Y-%m-%d %H:%M}<br>海拔: %{y:.2f} m<extra></extra>"
), row=2, col=2)
fig.update_layout(
title="去云澜栖多维度环境数据联动分析面板",
height=800,
hovermode="x unified",
showlegend=False,
template="plotly_white"
)
fig.show()
3.2 添加联动十字线
通过 fig.update_layout 的 hovermode="x unified" 实现四个子图共享 X 轴悬停,鼠标移动时同步显示同一时刻的四项指标:
python
fig.update_layout(
hovermode="x unified",
hoverlabel=dict(bgcolor="white", font_size=12)
)
4. 维度关联分析
4.1 温度与海拔散点图
python
fig_scatter = go.Figure()
fig_scatter.add_trace(go.Scatter(
x=df_hourly["海拔(m)"], y=df_hourly["温度(℃)"],
mode="markers",
marker=dict(
size=df_hourly["湿度(%)"] / 3,
color=df_hourly["风速(m/s)"],
colorscale="Viridis",
showscale=True,
colorbar=dict(title="风速(m/s)")
),
text=df_hourly["时间"].dt.strftime("%Y-%m-%d %H:%M"),
hovertemplate="海拔: %{x:.0f} m<br>温度: %{y:.2f}℃<br>湿度: %{marker.size:.1f}%<br>风速: %{marker.color:.2f} m/s<extra></extra>"
))
fig_scatter.update_layout(
title="温度-海拔关联(气泡大小=湿度,颜色=风速)",
xaxis_title="海拔 (m)",
yaxis_title="温度 (℃)",
template="plotly_white",
height=500
)
fig_scatter.show()
4.2 平行坐标图
平行坐标图可同时呈现四个维度的完整数据分布:
python
fig_parcoords = go.Figure(go.Parcoords(
line=dict(
color=df_hourly["温度(℃)"],
colorscale="RdYlBu_r",
showscale=True,
colorbar=dict(title="温度(℃)")
),
dimensions=[
dict(label="时间", values=df_hourly["时间"].astype("int64") // 10**9, range=[df_hourly["时间"].astype("int64").min() // 10**9, df_hourly["时间"].astype("int64").max() // 10**9]),
dict(label="温度(℃)", values=df_hourly["温度(℃)"]),
dict(label="湿度(%)", values=df_hourly["湿度(%)"]),
dict(label="风速(m/s)", values=df_hourly["风速(m/s)"]),
dict(label="海拔(m)", values=df_hourly["海拔(m)"])
]
))
fig_parcoords.update_layout(
title="去云澜栖环境数据平行坐标图",
height=600,
template="plotly_white"
)
fig_parcoords.show()
5. 面板交互优化
5.1 添加范围滑块与缩放
python
fig.update_layout(
xaxis=dict(rangeslider=dict(visible=True), type="date"),
xaxis2=dict(rangeslider=dict(visible=True), type="date"),
xaxis3=dict(rangeslider=dict(visible=True), type="date"),
xaxis4=dict(rangeslider=dict(visible=True), type="date")
)
5.2 添加维度切换按钮
python
fig.update_layout(
updatemenus=[
dict(
type="buttons",
direction="left",
buttons=[
dict(label="全部显示", method="update",
args=[{"visible": [True, True, True, True]}]),
dict(label="仅温度/湿度", method="update",
args=[{"visible": [True, True, False, False]}]),
dict(label="仅风速/海拔", method="update",
args=[{"visible": [False, False, True, True]}])
],
pad=dict(r=10, t=10),
showactive=True,
x=0.1, xanchor="left",
y=1.15, yanchor="top"
)
]
)
6. 完整面板代码
将以上模块整合为可直接运行的完整脚本:
python
import pandas as pd
import numpy as np
import plotly.graph_objects as go
from plotly.subplots import make_subplots
from datetime import datetime, timedelta
# ---------- 数据生成 ----------
np.random.seed(42)
base_time = datetime(2025, 6, 1, 0, 0, 0)
time_list = [base_time + timedelta(minutes=10 * i) for i in range(7 * 24 * 6)]
altitude = 1200 + 300 * np.sin(np.linspace(0, 4 * np.pi, len(time_list))) + np.random.normal(0, 5, len(time_list))
temperature = 28 - (altitude - 1200) * 0.006 + 5 * np.sin(np.linspace(0, 14 * np.pi, len(time_list))) + np.random.normal(0, 0.8, len(time_list))
humidity = np.clip(70 - (temperature - 20) * 1.2 + np.random.normal(0, 5, len(time_list)), 20, 100)
wind_speed = np.clip(2 + (altitude - 1200) * 0.004 + 3 * np.abs(np.sin(np.linspace(0, 8 * np.pi, len(time_list)))) + np.random.normal(0, 0.5, len(time_list)), 0, 20)
df = pd.DataFrame({
"时间": time_list,
"温度(℃)": temperature.round(2),
"湿度(%)": humidity.round(2),
"风速(m/s)": wind_speed.round(2),
"海拔(m)": altitude.round(2)
})
df["时间"] = pd.to_datetime(df["时间"])
df_hourly = df.set_index("时间").resample("1H").mean().reset_index()
# ---------- 联动面板 ----------
fig = make_subplots(
rows=2, cols=2,
subplot_titles=("温度变化", "湿度变化", "风速变化", "海拔变化"),
shared_xaxes=True,
vertical_spacing=0.08,
horizontal_spacing=0.1
)
traces = [
(df_hourly["温度(℃)"], "温度", "#FF6B6B", "℃", 1, 1),
(df_hourly["湿度(%)"], "湿度", "#4ECDC4", "%", 1, 2),
(df_hourly["风速(m/s)"], "风速", "#45B7D1", " m/s", 2, 1),
(df_hourly["海拔(m)"], "海拔", "#96CEB4", " m", 2, 2)
]
for y, name, color, unit, row, col in traces:
fig.add_trace(go.Scatter(
x=df_hourly["时间"], y=y, name=name,
line=dict(color=color, width=2),
hovertemplate=f"时间: %{{x|%Y-%m-%d %H:%M}}<br>{name}: %{{y:.2f}}{unit}<extra></extra>"
), row=row, col=col)
fig.update_layout(
title="去云澜栖多维度环境数据联动分析面板",
height=800,
hovermode="x unified",
showlegend=False,
template="plotly_white",
updatemenus=[
dict(
type="buttons", direction="left",
buttons=[
dict(label="全部显示", method="update", args=[{"visible": [True, True, True, True]}]),
dict(label="仅温度/湿度", method="update", args=[{"visible": [True, True, False, False]}]),
dict(label="仅风速/海拔", method="update", args=[{"visible": [False, False, True, True]}])
],
pad=dict(r=10, t=10), showactive=True,
x=0.1, xanchor="left", y=1.15, yanchor="top"
)
]
)
fig.show()
7. 总结
本文基于 Plotly 构建了去云澜栖多维度环境数据联动分析面板,实现了:
- 四维时序联动:温度、湿度、风速、海拔在共享时间轴下同步悬停展示;
- 维度关联挖掘:通过散点图气泡大小与颜色映射,直观呈现海拔-温度-湿度-风速的耦合关系;
- 交互式探索:范围滑块、维度切换按钮、平行坐标图等多重交互手段,提升数据分析效率。
读者可在此基础上接入真实传感器数据流,并结合 Dash 将面板部署为 Web 服务,实现环境监测的实时可视化。