ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

实验2基础实验 数据看板的设计与实现

实验2基础实验 数据看板的设计与实现

一、实验目的:

  1. 理解数据看板在业务监控和统计分析中的作用,掌握从需求分析、数据预处理到可视化展示的基本流程。
  2. 掌握 Streamlit 页面布局、数据卡片和明细表格的使用方法。
  3. 使用 Pandas 完成数据读取、缺失值处理、异常值剔除、去重和分组统计。
  4. 使用 Plotly 制作柱状图、折线图和环形图,根据数据特点选择合适的图表。
  5. 检查看板指标与原始数据的对应关系,完成界面整合与结果验证。

二、实验环境:

  • 硬件:可运行 Python 和浏览器的计算机。
  • 操作系统:macOS。
  • 开发语言:Python 3.11。
  • 主要依赖:Streamlit、Pandas、Plotly,具体版本见工程中的requirements.txt。
  • 展示工具:Codex 内置浏览器(实际截图环境),也可使用 Chrome 浏览器访问。
  • 数据来源:自行生成的校园消费模拟数据,统计周期为 2026 年 9 月 1 日至 9 月 30 日,仅用于实验。

三、实验内容:

1. 看板需求分析与版面设计

本实验选择校园消费统计场景,展示消费总额、消费笔数、平均每笔金额和前后半月金额变化。通过类别对比了解主要消费方向,通过日期趋势观察消费波动,并保留明细数据供核对。

看板分为数据概览区、维度统计区、趋势分析区和明细数据区。主色采用蓝色#2563EB,辅助色采用青绿色、橙色、紫色与粉色,背景采用浅灰蓝色#F4F7FB,图表背景采用白色。同一消费类别在柱状图和环形图中保持相同颜色。

版面草图如下:

┌──────────────────────────────────────────────────┐ │ 校园消费数据看板 │ │ 统计周期、数据来源与清洗结果 │ ├────────────┬────────────┬────────────┬────────────┤ │ 消费总额 │ 消费笔数 │ 平均每笔 │ 半月增长率 │ ├─────────────────────────┬────────────────────────┤ │ 各类消费金额柱状图 │ 消费结构环形图 │ ├─────────────────────────┴────────────────────────┤ │ 每日消费金额折线图 │ ├──────────────────────────────────────────────────┤ │ 消费明细数据表 │ └──────────────────────────────────────────────────┘

2. 实验数据准备与预处理

使用固定随机种子2026生成 900 条有效消费记录,保证数据可重复生成。每条记录包括交易编号、消费日期、消费类别、支付方式和消费金额五个字段。消费类别包括餐饮、购物、交通、学习和文娱,支付方式包括校园卡、微信和支付宝。

在有效记录中另外加入 7 条无效记录和 1 条重复记录,得到 908 条原始记录,保存为 UTF-8 BOM 编码的 CSV 文件。无效记录包括缺失日期、缺失类别、缺失金额、负金额、超限金额、无效日期和零金额。

字段含义处理规则
transaction_id交易编号不允许为空,按编号去重
date消费日期转换为日期,保留实验统计周期内记录
category消费类别只保留五种约定类别
payment支付方式只保留三种约定支付方式
amount消费金额转换为数值,保留 0<金额≤500 元的记录

500 元是本实验对单笔校园日常消费设置的模拟校验上限,并非通用异常值标准。日期和金额转换失败时统一转为空值,再剔除必填字段为空的记录。完成处理后按日期、交易编号排序,得到 900 条有效记录,共清除 8 条记录。

数据生成主要代码如下,代码未添加注释:

frompathlibimportPathimportcsvimportrandomfromdatetimeimportdate,timedelta random.seed(2026)root=Path(__file__).resolve().parent categories=['餐饮','购物','交通','学习','文娱']ranges={'餐饮':(8,38),'购物':(15,150),'交通':(2,20),'学习':(10,90),'文娱':(15,100)}rows=[]foriinrange(900):category=random.choices(categories,weights=[46,20,12,14,8])[0]low,high=ranges[category]rows.append({'transaction_id':f'T{i+1:04d}','date':str(date(2026,9,1)+timedelta(days=i%30)),'category':category,'payment':random.choice(['校园卡','微信','支付宝']),'amount':round(random.uniform(low,high),2)})fori,(key,value)inenumerate([('date',''),('category',''),('amount',''),('amount',-20),('amount',9999),('date','2026-09-99'),('amount',0)]):record=rows[i].copy()record['transaction_id']=f'X{i+1:04d}'record[key]=value rows.append(record)rows.append(rows[0].copy())path=root/'data'/'campus_consumption_raw.csv'path.parent.mkdir(parents=True,exist_ok=True)withpath.open('w',encoding='utf-8-sig',newline='')asf:writer=csv.DictWriter(f,fieldnames=rows[0].keys())writer.writeheader()writer.writerows(rows)print(f'已生成{len(rows)}条模拟数据:{path}')

3. 基础可视化图表与看板实现

使用st.columns()完成四张指标卡与两列图表布局,用st.plotly_chart()显示图表,用st.dataframe()展示清洗后的全部记录。明细表通过滚动查看后续数据。

  • 数据卡片:展示消费总额、消费笔数、平均每笔金额和后半月较前半月增长率。
  • 柱状图:按类别汇总消费金额,并按金额降序排列,方便比较各类消费规模。
  • 环形图:展示各类消费金额占总金额的比例。
  • 折线图:按日期汇总消费金额,补齐统计周期内的全部日期,观察每日变化。

指标计算口径如下:

  • 消费总额=有效记录的消费金额之和。
  • 消费笔数=清洗后的有效记录数。
  • 平均每笔金额=消费总额÷消费笔数。
  • 后半月较前半月增长率=(9 月 16—30 日消费总额÷9 月 1—15 日消费总额-1)×100%。前半月金额为零时显示“无法计算”。

本实验使用本地 CSV 数据,页面加载时读取数据,不设置自动刷新,完成基础静态看板要求。

看板主要代码如下,包含数据清洗、指标计算、图表制作和页面整合:

frompathlibimportPathimportpandasaspdimportplotly.expressaspximportstreamlitasst ROOT=Path(__file__).resolve().parent CATEGORIES=['餐饮','购物','交通','学习','文娱']COLORS=['#2563eb','#14b8a6','#f59e0b','#8b5cf6','#f472b6']defload_data():raw=pd.read_csv(ROOT/'data'/'campus_consumption_raw.csv')df=raw.copy()df['date']=pd.to_datetime(df['date'],errors='coerce')df['amount']=pd.to_numeric(df['amount'],errors='coerce')df=df.dropna(subset=['transaction_id','date','category','payment','amount'])df=df[df['category'].isin(CATEGORIES)]df=df[df['payment'].isin(['校园卡','微信','支付宝'])]df=df[df['amount'].between(0,500,inclusive='right')]df=df[df['date'].between('2026-09-01','2026-09-30')]df=df.drop_duplicates(subset=['transaction_id']).sort_values(['date','transaction_id'])returnraw,df.reset_index(drop=True)defstyle_chart(fig):fig.update_layout(template='plotly_white',font=dict(family='Arial, PingFang SC',size=13),margin=dict(l=12,r=12,t=12,b=12),height=285,paper_bgcolor='white',plot_bgcolor='white',xaxis_title=None,yaxis_title=None)returnfigdefmain():st.set_page_config(page_title='校园消费数据看板',page_icon='📊',layout='wide')st.markdown('''<style> .stApp {background:#f4f7fb; color:#172b4d;} .block-container {padding-top:2rem; padding-bottom:2rem; max-width:1500px;} h1 {font-size:2rem !important; color:#172b4d;} h3 {font-size:1.08rem !important; color:#172b4d;} [data-testid="stMetric"] {background:white; border:1px solid #e2e8f0; border-radius:12px; padding:16px 20px;} [data-testid="stMetricValue"] {color:#2563eb; font-size:1.85rem;} [data-testid="stCaptionContainer"] {color:#64748b;} </style>''',unsafe_allow_html=True)raw,df=load_data()st.title('校园消费数据看板')st.caption('CAMPUS CONSUMPTION / 2026年9月 / 基础实验 · 模拟数据')st.caption(f'数据质量:原始{len(raw)}条 → 有效{len(df)}条,清除{len(raw)-len(df)}条无效或重复记录。')total=df['amount'].sum()average=df['amount'].mean()first=df.loc[df['date'].dt.day<=15,'amount'].sum()second=df.loc[df['date'].dt.day>15,'amount'].sum()growth=(second/first-1)*100iffirstelseNonest.subheader('数据概览')cols=st.columns(4)cols[0].metric('消费总额(元)',f'{total:,.2f}')cols[1].metric('消费笔数(笔)',f'{len(df):,}')cols[2].metric('平均每笔(元)',f'{average:.2f}')cols[3].metric('后半月较前半月',f'{growth:+.2f}%'ifgrowthisnotNoneelse'无法计算')category=df.groupby('category',as_index=False)['amount'].sum()category=category.sort_values('amount',ascending=False)left,right=st.columns([1.2,1])withleft:st.subheader('维度统计 · 各类消费金额')bar=px.bar(category,x='category',y='amount',color='category',color_discrete_map=dict(zip(CATEGORIES,COLORS)),text_auto='.2f')bar.update_layout(showlegend=False)bar.update_traces(hovertemplate='%{x}<br>消费金额:%{y:.2f} 元<extra></extra>')st.plotly_chart(style_chart(bar),width='stretch')withright:st.subheader('维度统计 · 消费结构占比')pie=px.pie(category,names='category',values='amount',hole=0.60,color='category',color_discrete_map=dict(zip(CATEGORIES,COLORS)))pie.update_traces(textinfo='label+percent',textposition='outside',hovertemplate='%{label}<br>%{value:.2f} 元<br>占比 %{percent}<extra></extra>')pie.update_layout(showlegend=False)st.plotly_chart(style_chart(pie),width='stretch')st.subheader('趋势分析 · 每日消费金额')daily=df.groupby('date')['amount'].sum().reindex(pd.date_range('2026-09-01','2026-09-30'),fill_value=0).rename_axis('date').reset_index()line=px.line(daily,x='date',y='amount',markers=True,color_discrete_sequence=[COLORS[0]])line.update_traces(fill='tozeroy',fillcolor='rgba(37,99,235,0.08)',hovertemplate='%{x|%m月%d日}<br>消费金额:%{y:.2f} 元<extra></extra>')line.update_xaxes(tickformat='%m-%d',dtick=86400000*3)line.update_layout(height=245)line=style_chart(line)line.update_layout(height=245)st.plotly_chart(line,width='stretch')st.subheader('明细数据 · 清洗后的消费记录')display=df.copy()display['date']=display['date'].dt.strftime('%Y-%m-%d')display.columns=['交易编号','消费日期','消费类别','支付方式','消费金额(元)']st.dataframe(display,hide_index=True,width='stretch',height=230,column_config={'消费金额(元)':st.column_config.NumberColumn(format='%.2f')})st.caption('统计口径:金额为人民币;增长率比较9月16—30日与9月1—15日消费总额;数据仅用于课程实验。')if__name__=='__main__':main()

4. 运行方法

进入随附的“校园消费看板”工程目录后执行:

python-mpipinstall-rrequirements.txt python generate_data.py python-mstreamlit run app.py

浏览器打开终端显示的本地地址,通常为http://localhost:8501。工程已附带原始 CSV 文件,可直接启动看板;重新执行生成程序将生成相同数据。

5. 实验结果与验证

实际运行 Streamlit 程序后,页面显示四张指标卡、柱状图、环形图、折线图和明细表。以下图片为本次程序运行截图:

图1 数据概览与维度统计运行结果。

图2 趋势分析与明细数据运行结果。

对清洗结果、分类汇总和日期汇总进行核对,结果如下:

验证项目运行结果
原始记录数908 条
清除的无效及重复记录8 条
有效消费笔数900 笔
消费总额36,137.11 元
平均每笔金额40.15 元
前半月消费金额17,818.23 元
后半月消费金额18,318.88 元
后半月较前半月增长率+2.81%

各类别汇总如下,图表使用同一份清洗数据:

消费类别金额(元)金额占比
购物13,987.3338.71%
餐饮9,854.5827.27%
学习5,933.4716.42%
文娱5,321.1214.72%
交通1,040.612.88%

分类金额合计与每日金额合计均为 36,137.11 元,与数据卡片一致。交易编号无重复,必填字段无空值,日期均在统计周期内,金额均符合本实验设定的有效区间。浏览器检查确认三张图表正常显示,页面无程序异常。

从模拟数据可见,购物类消费金额最高,为 13,987.33 元;交通类最低,为 1,040.61 元。后半月较前半月增加 500.65 元,增长约 2.81%。9 月 29 日消费金额最高,为 1,566.15 元。这些结论仅描述本次模拟数据,不能作为真实校园消费规律。

实验中需要处理的主要问题与解决方法如下:

问题解决方法
缺失值和无效日期影响统计使用容错转换,将转换失败内容转为空值后剔除
重复记录导致金额重复累计以交易编号为依据去重
负金额、零金额和超限金额影响图表根据本实验单笔金额规则进行过滤
中文 CSV 文件的兼容性使用 UTF-8 BOM 编码保存文件
不同图表的类别颜色可能不一致统一设置类别与颜色的映射关系
增长率容易产生口径歧义明确比较前后两个等长的 15 天区间

四、心得体会:

通过本次实验,我理解了数据看板从需求分析、版面设计、数据准备到图表整合的实现过程。看板中的指标需要围绕明确的问题选择:总额反映消费规模,平均每笔金额反映单笔消费水平,分类图表展示消费结构,日期折线图展示变化趋势。

数据清洗是保证统计准确的前提。空值、异常金额和重复记录都会影响汇总结果,因此应先确定字段类型和有效范围,再进行分组统计。将指标卡、分类汇总和日期汇总相互核对,可以及时发现统计口径不一致的问题。

在页面设计方面,清晰的分区、统一的类别配色和适当的间距有助于阅读。Streamlit 能够简化页面搭建,Pandas 负责数据处理,Plotly 负责图表展示,三者结合可以完成一个结构完整的基础数据看板。后续可以在此基础上增加日期筛选和类别筛选,但本次实验主要完成基础静态展示与结果验证。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进