Spaces:
Sleeping
Sleeping
File size: 10,695 Bytes
2307f32 76afe6d 2307f32 76afe6d 2307f32 61a6b87 2307f32 76afe6d f072311 f6b784e 2307f32 8846ec1 2307f32 f6b784e 2307f32 76afe6d f6b784e 2307f32 76afe6d f6b784e 2307f32 f6b784e 2307f32 f6b784e 2307f32 f6b784e 2307f32 f6b784e 2307f32 f6b784e 76afe6d f6b784e 2307f32 f6b784e 2307f32 f6b784e 2307f32 f6b784e 2307f32 f6b784e 76afe6d f6b784e 2307f32 461075b f6b784e 461075b 2307f32 461075b 76afe6d 461075b f072311 f6b784e 461075b |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 |
"""
TODO
"""
# 首先计算相对路径
from pathlib import Path
import os
import urllib.request, zipfile
import streamlit as st
this_file = Path(__file__).resolve()
this_directory = this_file.parent
# 数据集文件夹路径
data_cangzhou_folder = this_directory / "data/Cangzhou"
data_static_folder = data_cangzhou_folder / "Static" # windows linux差异,不要写错了
# 数据文件夹路径(静态数据)
data_folder = data_static_folder
# 然后
import streamlit as st
import pandas as pd
from datetime import datetime
import plotly.graph_objects as go
import plotly.express as px
# 设置页面配置为宽屏模式,以便能同时显示三个图表
st.set_page_config(layout="wide")
# 设置应用标题,参考数据集介绍,反映作业要求
st.title("S&M-HSTPM2d5数据集可视化——清华大学数据可视化课程作业1")
# 如果 data/Cangzhou 目录不存在,则下载并解压数据集
if not (data_cangzhou_folder).exists():
st.info("Cangzhou 数据集不存在,正在下载中,请耐心等待……")
url = "https://zenodo.org/records/4028130/files/Cangzhou.zip?download=1"
local_zip = this_directory / "Cangzhou.zip"
urllib.request.urlretrieve(url, str(local_zip))
with zipfile.ZipFile(str(local_zip), "r") as zip_ref:
zip_ref.extractall(path=str(this_directory / "data"))
st.success("Cangzhou 数据集下载并解压完成!")
st.rerun()
st.markdown("叶璨铭,2024214500,[email protected]")
# 创建三个等宽的列,分别展示图(a)、图(b)和图(c)
col1, col2, col3 = st.columns(3)
@st.cache_data
def load_and_filter_data(file_path, start_time, end_time):
# 根据CSV格式,第一列作为时间戳(无列名),后续列依次为pm2d5、lat、lon
df = pd.read_csv(file_path, header=0, names=['timestamp', 'pm2d5', 'lat', 'lon'])
# 转换为datetime格式
df['timestamp'] = pd.to_datetime(df['timestamp'])
# 过滤指定区间数据
filtered_df = df[(df['timestamp'] >= start_time) & (df['timestamp'] <= end_time)]
return filtered_df
# 图(a): PM2.5随时间变化折线图(静态传感器数据)
with col1:
st.header("(a)折线图:展示 PM2.5 浓度水平随时间的变化")
with st.expander("绘制要求"):
st.markdown("1. 使用 static 文件夹中的 .csv 文件,并筛选出时间戳在 2019-01-01 00:00:00 到 2019-01-01 12:00:00 之间的数据。")
st.markdown("2. X 轴和 Y 轴分别表示时间和 PM2.5 浓度水平。")
st.markdown("3. 为每个静态传感器绘制一条折线,并用不同颜色进行区分。")
# 定义过滤时间段
start_dt = datetime(2019, 1, 1, 0, 0, 0)
end_dt = datetime(2019, 1, 1, 12, 0, 0)
# 获取静态数据文件夹中CSV文件
csv_files = [f for f in os.listdir(data_folder) if f.endswith('.csv')]
sensor_data = {}
for file in csv_files:
file_path = os.path.join(data_folder, file)
sensor_name = file.split('.')[0]
sensor_data[sensor_name] = load_and_filter_data(file_path, start_dt, end_dt)
# 整理数据:以时间戳为索引,每个传感器的PM2.5为一列
chart_data = pd.DataFrame()
for sensor, data in sensor_data.items():
chart_data[sensor] = data.set_index('timestamp')['pm2d5']
# 绘制折线图(添加图表标题及图例标签)
fig_line = px.line(chart_data,
title="PM2.5随时间变化折线图",
labels={"variable": "传感器", "value": "PM2.5", "index": "时间"})
fig_line.update_layout(xaxis_title="时间", yaxis_title="PM2.5水平")
st.plotly_chart(fig_line, use_container_width=True)
with st.expander("详细说明"):
st.markdown("**描述:** 本图展示了各静态传感器在2019年1月1日0:00至12:00期间的PM2.5浓度随时间变化的趋势。横轴表示时间,纵轴表示PM2.5数值,不同折线代表不同传感器的数据。")
st.markdown("**解读:** 曲线波动反映了空气质量的时段变化,峰值可能预示短期污染事件,而持续低值表明空气较为清洁。传感器数据对比有助于区域污染差异的分析。")
# 图(b): 车辆移动散点图(移动传感器数据)
with col2:
st.header("(b)散点图:展示车辆搭载移动传感器在城市中的移动情况")
with st.expander("绘制要求"):
st.markdown("1. 使用 mobile 文件夹中的 .csv 文件,并筛选出时间戳在 2019-01-02 10:00:00 到 2019-01-02 10:20:00 之间的数据。")
st.markdown("2. X 轴和 Y 轴分别表示经度和纬度。")
st.markdown("3. 使用散点图展示车辆传感器的位置,用不同颜色区分各传感器,并通过调整透明度(早期数据更透明)表达时间演变。")
# 定义mobile数据文件夹路径
mobile_folder = str(this_directory / "data/Cangzhou/Mobile")
csv_files_mobile = [f for f in os.listdir(mobile_folder) if f.endswith('.csv')]
mobile_sensor_data = {}
start_mobile = datetime(2019, 1, 2, 10, 0, 0)
end_mobile = datetime(2019, 1, 2, 10, 20, 0)
for file in csv_files_mobile:
sensor_name = file.split('.')[0]
file_path = os.path.join(mobile_folder, file)
df = load_and_filter_data(file_path, start_mobile, end_mobile)
if not df.empty:
mobile_sensor_data[sensor_name] = df
# 使用Plotly绘制散点图并根据时间调整透明度
fig2 = go.Figure()
colors = px.colors.qualitative.Plotly
def hex_to_rgba(hex_color, alpha):
hex_color = hex_color.lstrip('#')
r = int(hex_color[0:2], 16)
g = int(hex_color[2:4], 16)
b = int(hex_color[4:6], 16)
return f"rgba({r}, {g}, {b}, {alpha})"
total_time = (end_mobile - start_mobile).total_seconds()
sensor_index = 0
for sensor, data in mobile_sensor_data.items():
base_color = colors[sensor_index % len(colors)]
sensor_index += 1
custom_colors = []
for ts in data['timestamp']:
dt_seconds = (ts - start_mobile).total_seconds()
normalized = dt_seconds / total_time if total_time > 0 else 0
# 透明度:早期数据较透明(alpha值较低),后期较不透明
alpha = 0.3 + 0.7 * normalized
custom_colors.append(hex_to_rgba(base_color, alpha))
fig2.add_trace(go.Scatter(
x = data['lon'],
y = data['lat'],
mode = 'markers',
marker = dict(color = custom_colors, size = 10),
name = sensor
))
fig2.update_layout(
xaxis_title="经度",
yaxis_title="纬度",
title="车辆移动散点图"
)
st.plotly_chart(fig2, use_container_width=True)
with st.expander("详细说明"):
st.markdown("**描述:** 本图利用Mobile文件夹中的CSV数据,在2019年1月2日10:00至10:20期间展示车辆传感器的位置分布。横轴表示经度,纵轴表示纬度,不同颜色代表不同传感器。")
st.markdown("**解读:** 通过调整散点透明度(早期数据较透明),图中显示了车辆移动的时间演变趋势,为探索城市中车辆行驶路径提供依据。")
# 图(c): 3D直方图——展示整个城市在特定时段内的PM2.5分布
with col3:
st.header("(c)3D直方图:展示整个城市在特定时段内的PM2.5分布")
with st.expander("绘制要求"):
st.markdown("1. 使用 mobile 和 static 文件夹中的 .csv 文件,并筛选出时间戳在 2019-01-01 09:00:00 到 2019-01-01 09:10:00 之间的数据。")
st.markdown("2. 将数据的GPS坐标转换为网格坐标(例如采用 0.01 度的分辨率),并在相同网格内聚合数据。")
st.markdown("3. X 轴、Y 轴和 Z 轴分别表示经度、纬度和PM2.5数值。")
# 定义过滤时间段
start_c = datetime(2019, 1, 1, 9, 0, 0)
end_c = datetime(2019, 1, 1, 9, 10, 0)
# 读取 static 文件夹中的 CSV 文件数据
csv_files_static = [f for f in os.listdir(data_folder) if f.endswith('.csv')]
data_list = []
for file in csv_files_static:
file_path = os.path.join(data_folder, file)
df = load_and_filter_data(file_path, start_c, end_c)
if not df.empty:
data_list.append(df)
# 读取 mobile 文件夹中的 CSV 文件数据
csv_files_mobile = [f for f in os.listdir(str(this_directory / "data/Cangzhou/Mobile")) if f.endswith('.csv')]
mobile_folder = str(this_directory / "data/Cangzhou/Mobile")
for file in csv_files_mobile:
file_path = os.path.join(mobile_folder, file)
df = load_and_filter_data(file_path, start_c, end_c)
if not df.empty:
data_list.append(df)
# 合并所有数据
if data_list:
df_all = pd.concat(data_list, ignore_index=True)
else:
df_all = pd.DataFrame(columns=["timestamp", "pm2d5", "lat", "lon"])
# 将 GPS 坐标转换为网格坐标(采用 0.01 度分辨率)
resolution = 0.01
df_all["lon_grid"] = df_all["lon"].round(2)
df_all["lat_grid"] = df_all["lat"].round(2)
# 聚合每个网格内的PM2.5数据(计算平均值)
df_group = df_all.groupby(["lon_grid", "lat_grid"]).agg({"pm2d5": "mean"}).reset_index()
# 绘制3D直方图:由于 Plotly 不支持原生 Bar3d,这里采用 3D 表面图展示网格数据
# 将聚合数据转换为适合表面图的矩阵
if not df_group.empty:
pivot = df_group.pivot(index="lat_grid", columns="lon_grid", values="pm2d5")
pivot = pivot.fillna(0)
# 创建3D表面图,x轴为经度、y轴为纬度、z轴为PM2.5平均值
fig3 = go.Figure(data=[go.Surface(z=pivot.values, x=pivot.columns, y=pivot.index, colorscale='Viridis')])
else:
fig3 = go.Figure()
fig3.update_layout(
scene=dict(
xaxis_title="经度",
yaxis_title="纬度",
zaxis_title="PM2.5"
),
title="PM2.5分布3D表面图"
)
st.plotly_chart(fig3, use_container_width=True)
with st.expander("详细说明"):
st.markdown("**描述:** 本图通过汇总 mobile 和 static 两种传感器数据,展示了 2019-01-01 09:00 至 09:10 期间整个城市内PM2.5浓度的分布。")
st.markdown("**解读:** 基于GPS坐标转换为网格坐标后,对相同网格中的数据进行聚合,直观展示不同区域的空气质量水平,从而为进一步的环境分析提供参考。")
|