一、实验目的理解数据看板在业务监控和统计分析中的作用掌握从需求分析、数据预处理到可视化展示的基本流程。掌握 Streamlit 页面布局、数据卡片和明细表格的使用方法。使用 Pandas 完成数据读取、缺失值处理、异常值剔除、去重和分组统计。使用 Plotly 制作柱状图、折线图和环形图根据数据特点选择合适的图表。检查看板指标与原始数据的对应关系完成界面整合与结果验证。二、实验环境硬件可运行 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)rootPath(__file__).resolve().parent categories[餐饮,购物,交通,学习,文娱]ranges{餐饮:(8,38),购物:(15,150),交通:(2,20),学习:(10,90),文娱:(15,100)}rows[]foriinrange(900):categoryrandom.choices(categories,weights[46,20,12,14,8])[0]low,highranges[category]rows.append({transaction_id:fT{i1:04d},date:str(date(2026,9,1)timedelta(daysi%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)]):recordrows[i].copy()record[transaction_id]fX{i1:04d}record[key]value rows.append(record)rows.append(rows[0].copy())pathroot/data/campus_consumption_raw.csvpath.parent.mkdir(parentsTrue,exist_okTrue)withpath.open(w,encodingutf-8-sig,newline)asf:writercsv.DictWriter(f,fieldnamesrows[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 ROOTPath(__file__).resolve().parent CATEGORIES[餐饮,购物,交通,学习,文娱]COLORS[#2563eb,#14b8a6,#f59e0b,#8b5cf6,#f472b6]defload_data():rawpd.read_csv(ROOT/data/campus_consumption_raw.csv)dfraw.copy()df[date]pd.to_datetime(df[date],errorscoerce)df[amount]pd.to_numeric(df[amount],errorscoerce)dfdf.dropna(subset[transaction_id,date,category,payment,amount])dfdf[df[category].isin(CATEGORIES)]dfdf[df[payment].isin([校园卡,微信,支付宝])]dfdf[df[amount].between(0,500,inclusiveright)]dfdf[df[date].between(2026-09-01,2026-09-30)]dfdf.drop_duplicates(subset[transaction_id]).sort_values([date,transaction_id])returnraw,df.reset_index(dropTrue)defstyle_chart(fig):fig.update_layout(templateplotly_white,fontdict(familyArial, PingFang SC,size13),margindict(l12,r12,t12,b12),height285,paper_bgcolorwhite,plot_bgcolorwhite,xaxis_titleNone,yaxis_titleNone)returnfigdefmain():st.set_page_config(page_title校园消费数据看板,page_icon,layoutwide)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-testidstMetric] {background:white; border:1px solid #e2e8f0; border-radius:12px; padding:16px 20px;} [data-testidstMetricValue] {color:#2563eb; font-size:1.85rem;} [data-testidstCaptionContainer] {color:#64748b;} /style,unsafe_allow_htmlTrue)raw,dfload_data()st.title(校园消费数据看板)st.caption(CAMPUS CONSUMPTION / 2026年9月 / 基础实验 · 模拟数据)st.caption(f数据质量原始{len(raw)}条 → 有效{len(df)}条清除{len(raw)-len(df)}条无效或重复记录。)totaldf[amount].sum()averagedf[amount].mean()firstdf.loc[df[date].dt.day15,amount].sum()seconddf.loc[df[date].dt.day15,amount].sum()growth(second/first-1)*100iffirstelseNonest.subheader(数据概览)colsst.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无法计算)categorydf.groupby(category,as_indexFalse)[amount].sum()categorycategory.sort_values(amount,ascendingFalse)left,rightst.columns([1.2,1])withleft:st.subheader(维度统计 · 各类消费金额)barpx.bar(category,xcategory,yamount,colorcategory,color_discrete_mapdict(zip(CATEGORIES,COLORS)),text_auto.2f)bar.update_layout(showlegendFalse)bar.update_traces(hovertemplate%{x}br消费金额%{y:.2f} 元extra/extra)st.plotly_chart(style_chart(bar),widthstretch)withright:st.subheader(维度统计 · 消费结构占比)piepx.pie(category,namescategory,valuesamount,hole0.60,colorcategory,color_discrete_mapdict(zip(CATEGORIES,COLORS)))pie.update_traces(textinfolabelpercent,textpositionoutside,hovertemplate%{label}br%{value:.2f} 元br占比 %{percent}extra/extra)pie.update_layout(showlegendFalse)st.plotly_chart(style_chart(pie),widthstretch)st.subheader(趋势分析 · 每日消费金额)dailydf.groupby(date)[amount].sum().reindex(pd.date_range(2026-09-01,2026-09-30),fill_value0).rename_axis(date).reset_index()linepx.line(daily,xdate,yamount,markersTrue,color_discrete_sequence[COLORS[0]])line.update_traces(filltozeroy,fillcolorrgba(37,99,235,0.08),hovertemplate%{x|%m月%d日}br消费金额%{y:.2f} 元extra/extra)line.update_xaxes(tickformat%m-%d,dtick86400000*3)line.update_layout(height245)linestyle_chart(line)line.update_layout(height245)st.plotly_chart(line,widthstretch)st.subheader(明细数据 · 清洗后的消费记录)displaydf.copy()display[date]display[date].dt.strftime(%Y-%m-%d)display.columns[交易编号,消费日期,消费类别,支付方式,消费金额元]st.dataframe(display,hide_indexTrue,widthstretch,height230,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 负责图表展示三者结合可以完成一个结构完整的基础数据看板。后续可以在此基础上增加日期筛选和类别筛选但本次实验主要完成基础静态展示与结果验证。