
简介围绕大学生网络消费行为展开的调查报告文档适用于电子商务、市场营销、社会学等专业的学生与教师也可为校园消费调研或课程作业提供参考样本。全文以问卷调研为主线依次覆盖调查背景与目的、提纲设计、样本选取与抽样方法并对年级分布、店铺搜索路径、网店选择影响因素、常用购物平台、购买商品类别、购物频率、发展趋势与交易安全性等维度逐一展开数据统计与图表分析其中随机抽取30个样本、回收有效问卷28份可作为问卷设计与结果分析的实例参照。资源包共1个doc文件体积约1.49MB打开即可阅读完整正文与配套图表无需额外安装工具。已有86人学习下载适合需要了解大学生网购特征、撰写同类调查报告时对照参考。1. 从一份《大学生网上购物情况调查报告.doc》说起问卷到结论之间隔了什么每年学期末教务系统里总会出现一批文件名高度相似的文档《大学生网上购物情况调查报告.doc》。常见版本是三五百份问卷、几张饼图、一句“可见大学生网购热情高涨”。但如果这份报告要用于课程设计答辩、校园渠道立项、或者电商平台的校园用户研究评审第一句话往往是“样本多少份、怎么抽的、月均消费这个数字怎么算出来的”真正要交付的并不是一篇 Word 文档而是一条从问卷设计、字段编码、数据清洗、统计检验到自动排版的完整数据管线。它解决的是“数字站不住脚”这个具体问题口径写清楚、脚本能重跑、样本量算得出来。适合看这份内容的人有三类——要交调研作业的学生、带学生做实证分析的老师以及做校园用户研究的运营和数据分析新人。下面按“问卷怎么设计、数据怎么洗、结论怎么算、文档怎么出”的顺序把每一环的命令、参数和坑拆开讲。2. 问卷设计与变量建模把“大学生网上购物”拆成可统计字段很多人是先写题目、后想分析结果收回来才发现“你一般买什么”是多选却按单选统计了。正确的顺序是先定字段名和取值再写题面。字段名一旦确定中途不要改因为后面 Excel 透视、SPSS 变量视图、pandas 列名全靠它对齐。2.1 变量分层人口学、行为、态度三层结构把整份问卷拆成三层每层对应不同的统计方法人口学变量做分组行为变量做主分析态度量表做信度和因子。下面这张表是校园网购调研里比较通用的一套字段可以直接改成自己的版本。字段名题型取值 / 编码后续用途gender单选1男2女分组变量卡方交叉grade单选1~4 对应大一至大四分组变量、趋势分析monthly_cost区间单选1~6 对应生活费档位分箱后作定序变量platform多选淘宝/京东/拼多多/抖音/唯品会/其他拆哑变量算渗透率category多选服饰/数码/食品/美妆/图书/虚拟服务拆哑变量做品类结构freq单选1几乎不买5几乎每天定序变量映射月单量avg_ticket数值填空单位元连续变量做回归impulseLikert 1~51完全不同意5完全同意量表题算总分satisLikert 1~5同上信度分析、因子分析duration平台自动记录秒清洗阶段剔除乱填这里有两个容易被忽略的点。第一生活费不要让人直接填数字学生对这个数往往只有一个模糊印象填出来的尾数全是 0 和 500做区间单选反而更稳。第二多选题的选项集合必须提前冻结“其他”也要有独立列否则每批数据的哑变量列数不一样脚本第二次跑就会报列不匹配。2.2 抽样与样本量95% 置信度、±5% 误差要收多少份样本量不是拍脑袋定的用比例估计公式算n Z²p(1-p)/e²。p 取 0.5 是最保守的估计方差最大Z 在 95% 置信度下取 1.96允许误差 e 取 0.05。算出来 385 份但校园调研很少是简单随机抽样整班发放会带来设计效应再加上无效问卷实际发放量要往上抬。import math def calc_sample(z1.96, p0.5, e0.05, deff1.3, valid_rate0.85): z: 置信水平对应的分位数95% 用 1.96 p: 目标比例未知时取 0.5 最保守 e: 允许误差常规取 0.05 deff: 设计效应整群/整班抽样取 1.2~1.5 valid_rate: 有效回收率线上问卷一般 0.8~0.9 n0 z ** 2 * p * (1 - p) / e ** 2 # 简单随机抽样所需样本 n math.ceil(n0 * deff) # 乘设计效应 n_issue math.ceil(n / valid_rate) # 反推需要发放的份数 return round(n0, 2), n, n_issue print(calc_sample()) # (384.16, 500, 589)按这组参数发放 589 份才比较稳妥实操里一般凑整到 600 份。发下去还要做配额不能让某个年级占到七成按年级 4 档、性别 2 类做交叉配额专业大类做兜底。配额表要写进报告附录这决定了“样本对学校有没有代表性”这一问能不能答上来。2.3 用 openpyxl 生成带回填校验的回收模板线上问卷平台大多能直接导出 xlsx但如果调研是在教室里发纸质表、后面人工录入或者需要让同学统一填一个表格最好生成一个带下拉约束的模板从源头减少脏数据。from openpyxl import Workbook from openpyxl.worksheet.datavalidation import DataValidation from openpyxl.styles import Font, Alignment wb Workbook() ws wb.active ws.title responses headers [student_id, gender, grade, monthly_cost, platform, category, freq, avg_ticket, impulse, satis, duration] ws.append(headers) for c in ws[1]: c.font Font(boldTrue) c.alignment Alignment(horizontalcenter) ws.freeze_panes A2 # 冻结表头录入时不易错行 # 性别、年级用下拉列表避免出现男生男 这类同义异形 dv_gender DataValidation(typelist, formula11,2, allow_blankFalse) dv_grade DataValidation(typelist, formula11,2,3,4, allow_blankFalse) ws.add_data_validation(dv_gender) ws.add_data_validation(dv_grade) dv_gender.add(B2:B2000) dv_grade.add(C2:C2000) # 客单价限制为 0~20000 的数值挡住一顿饭这类文本 dv_ticket DataValidation(typedecimal, operatorbetween, formula10, formula220000, allow_blankTrue) ws.add_data_validation(dv_ticket) dv_ticket.add(H2:H2000) wb.save(survey_template.xlsx)三个参数值得说明allow_blankFalse用在关键分组字段上逼着录入选值freeze_panesA2是给人工录入减负行数一多没有冻结表头很容易串行数值型字段的范围约束比事后清洗便宜得多。2.4 预调研30 份试填要盯的三个指标正式发放前先找 30 个人试填看三件事量表题的信度、填答时长分布、以及逻辑矛盾率。信度用 Cronbachs α一般要求 0.7 以上低于 0.6 说明题目在测不同的东西要删题或改表述。import pandas as pd def cronbach_alpha(df): df 的每一列是一道量表题行是样本 df df.dropna() k df.shape[1] var_items df.var(axis0, ddof1).sum() # 各题方差之和 var_total df.sum(axis1).var(ddof1) # 总分方差 return k / (k - 1) * (1 - var_items / var_total) items pd.read_excel(pilot_30.xlsx)[[impulse, satis, trust, repurchase]] print(round(cronbach_alpha(items), 3))填答时长这一项最容易被跳过。线上问卷平台会自动记录低于 60 秒的样本基本可以判定为乱填纸质录入没有时长可以用“同一份表里前后矛盾的题目”替代比如前面选了“几乎不网购”、后面客单价填了 300 元。3. 数据清洗与字段编码把回收表变成能跑的 DataFrame清洗是整个流程里最耗时间的一步通常占掉一半以上工时。原则是所有处理都写成代码不手工改单元格。手工改过的表没人能复现被人追问一句“这几份为什么删了”就答不上来。3.1 读入与类型统一先用dtypestr全部按字符串读进来再逐列转换避免 pandas 把“1”和“1.0”混着解析也避免学号被当成数字丢掉前导零。import pandas as pd import numpy as np raw pd.read_excel(survey_raw.xlsx, sheet_nameresponses, dtypestr) raw.columns [c.strip().lower() for c in raw.columns] # 统一列名格式 # 逐列声明目标类型转换失败的一律变 NaN不静默丢数据 num_cols [grade, monthly_cost, freq, avg_ticket, impulse, satis, duration] for col in num_cols: raw[col] pd.to_numeric(raw[col], errorscoerce) raw[gender] raw[gender].astype(Int64) df raw.copy() print(df.dtypes)errorscoerce是关键参数它把“未填”“无”这类文本变成 NaN而不是抛异常中断脚本。这样后面能统一按缺失值规则处理也能顺便统计出哪一列脏得最厉害。3.2 缺失值、逻辑矛盾与异常值的三条规则缺失值不能一律删。关键字段性别、年级、网购频次缺失的样本对分组分析没价值直接剔除非关键字段客单价、满意度缺失可以保留做对应分析时再按列删。逻辑矛盾和异常值要按规则表处理处理动作和影响样本量都要记录。检查项判定条件处理动作典型影响填答过快duration 60 秒整条剔除剔除 3%~8%重复提交student_id 重复保留最早一条剔除 1%~3%关键字段缺失gender/grade/freq 为空整条剔除剔除 2%~5%行为矛盾freq1 且 avg_ticket0客单价置 0修正而非剔除数值越界avg_ticket20000 或 0按缺失处理影响 1%量表直线作答impulse~satis 全部同值标记但不删做敏感性分析5%~10%# 1) 填答时长过滤 df df[df[duration].fillna(0) 60] # 2) 同一学号重复提交保留第一次 df df.sort_values(duration, ascendingFalse).drop_duplicates(student_id, keepfirst) # 3) 关键字段缺失剔除 df df.dropna(subset[gender, grade, freq]) # 4) 行为矛盾修正 mask (df[freq] 1) (df[avg_ticket].fillna(0) 0) df.loc[mask, avg_ticket] 0.0 # 5) 数值越界按缺失处理 df.loc[(df[avg_ticket] 20000) | (df[avg_ticket] 0), avg_ticket] np.nan print(清洗后有效样本, len(df))注意第 2 步的keepfirst是配合上一行的排序用的先按时长降序排再保留第一条等于留下填得最认真的那份。如果直接按原顺序去重留下的可能是随手点的那份。3.3 多选题拆成哑变量矩阵多选题导出的单元格内容是“淘宝,京东,拼多多”这种字符串必须先冻结类别集合再拆列。用get_dummies直接拆的问题是某批数据里没人选“唯品会”列就消失了两次运行的结果没法对齐。def split_multi(value): if pd.isna(value): return [] text str(value).replace(, ,).replace(、, ,) return [x.strip() for x in text.split(,) if x.strip()] PLATFORMS [淘宝, 京东, 拼多多, 抖音, 唯品会, 其他] CATEGORIES [服饰, 数码, 食品, 美妆, 图书, 虚拟服务] for p in PLATFORMS: df[fplatform_{p}] df[platform].apply(lambda s: int(p in split_multi(s))) for c in CATEGORIES: df[fcat_{c}] df[category].apply(lambda s: int(c in split_multi(s))) # 平台渗透率 选择人数 / 有效样本 penetration df[[fplatform_{p} for p in PLATFORMS]].mean().sort_values(ascendingFalse) print((penetration * 100).round(1))列名统一加前缀platform_、cat_是为了后面批量取列df.filter(likeplatform_)一行就能把整块矩阵拿出来做交叉分析不用手写列名列表。3.4 分箱与派生变量月均消费金额怎么算原始问卷里没有“月均消费金额”这个字段它是算出来的。算法必须在报告里写明白否则同一个数据集两个人能算出两个数。# 生活费分箱左闭右开避免 2000 这种边界值落进两个箱 bins [0, 1000, 1500, 2000, 2500, 3000, np.inf] labels [1000以下, 1000-1500, 1500-2000, 2000-2500, 2500-3000, 3000以上] df[cost_bin] pd.cut(df[monthly_cost_num], binsbins, labelslabels, rightFalse) # 频次映射为月单量经验系数需在报告口径中说明 freq_to_orders {1: 0.5, 2: 2, 3: 4, 4: 8, 5: 20} df[monthly_orders] df[freq].map(freq_to_orders) df[monthly_amount] df[monthly_orders] * df[avg_ticket] print(df[monthly_amount].describe().round(2))rightFalse表示左闭右开边界值 2000 归到“2000-2500”而不是“1500-2000”这样区间不重不漏。freq_to_orders里的系数是估计值写报告时要注明“月单量由频次档位折算属于近似口径”这一句能挡掉一半的质疑。4. 统计分析与显著性检验从描述统计到客单价回归分析部分要回答三个问题大学生网购的结构长什么样、不同群体之间有没有真实差异、客单价受什么影响。前两个靠描述统计和卡方第三个靠回归。4.1 描述性统计与消费画像先把核心指标的分布拉出来中位数和分位数比均值更能说明问题因为客单价是典型的右偏分布均值会被少数高消费样本拉高。core [avg_ticket, monthly_amount, impulse, satis] desc df[core].describe(percentiles[0.25, 0.5, 0.75]).T desc[cv] desc[std] / desc[mean] # 变异系数衡量离散程度 print(desc.round(2))cv比标准差更好用因为它去掉了量纲。客单价的变异系数超过 1 就说明个体差异极大报告里只写均值会有误导性。4.2 交叉表与卡方检验性别、年级和网购频次有没有真差异交叉表看分布卡方检验判断差异是不是抽样波动。from scipy.stats import chi2_contingency import numpy as np ct pd.crosstab(df[grade], df[freq]) chi2, p, dof, expected chi2_contingency(ct) n ct.values.sum() cramers_v np.sqrt(chi2 / (n * (min(ct.shape) - 1))) print(fchi2{chi2:.2f}, p{p:.4f}, dof{dof}, V{cramers_v:.3f}) print(期望频数小于5的格子占比, (expected 5).mean().round(3))两个参数要点p 0.05 只说明“有差异”差异有多大要看 Cramérs V0.1 以下算弱相关expected 5的格子占比超过 20% 时卡方结果不可靠要把年级合并成“低年级/高年级”再跑一遍。4.3 客单价的多元线性回归与稳健标准误import statsmodels.api as sm from statsmodels.stats.outliers_influence import variance_inflation_factor X df[[monthly_cost_num, freq, impulse, satis]].astype(float) X sm.add_constant(X) y df[avg_ticket].astype(float) model sm.OLS(y, X, missingdrop).fit(cov_typeHC3) # HC3 稳健标准误 print(model.summary()) vif pd.DataFrame({ 变量: X.columns, VIF: [variance_inflation_factor(X.values, i) for i in range(X.shape[1])] }) print(vif.round(2))cov_typeHC3处理异方差客单价这类右偏数据几乎一定存在异方差用普通标准误会把显著性说得过于乐观。VIF 超过 10 说明存在多重共线性常见于把 “freq” 和折算出来的 “monthly_orders” 同时放进模型——这两个变量本质上是同一个东西选一个就行。4.4 可视化中文字体与三个必调参数图表在 Word 里显示成方框九成是因为没设中文字体。import matplotlib matplotlib.use(Agg) # 无显示环境下也能出图 import matplotlib.pyplot as plt plt.rcParams[font.sans-serif] [SimHei, Microsoft YaHei, Noto Sans CJK SC] plt.rcParams[axes.unicode_minus] False # 负号正常显示 plt.rcParams[figure.dpi] 150 # 插入 Word 不糊 fig, ax plt.subplots(figsize(7, 4)) df.boxplot(columnavg_ticket, bycost_bin, axax, gridFalse) ax.set_title(不同生活费档位的客单价分布) ax.set_xlabel(月生活费档位) ax.set_ylabel(客单价元) plt.suptitle() plt.tight_layout() plt.savefig(fig_ticket_box.png, bbox_inchestight)图形类型适合的变量组合关键参数常见误用分组柱状图分组变量 × 定序变量rot0防标签倾斜用堆叠柱比多选占比箱线图分组变量 × 连续变量showfliersFalse看主体样本量 30 时硬画热力图多选哑变量的共现矩阵annotTrue标数值拿它当相关系数图散点加拟合线两个连续变量先看分布再加lowess直接线性拟合有异常点的数据多选题的占比不能用堆叠柱状图表示因为各选项之间有重叠加起来会超过 100%这类数据只适合做条形图。5. 结果落地与复现自动生成 .doc 与一键重跑分析做完最后一步是把数字和图塞进那份《大学生网上购物情况调查报告.doc》。手工复制粘贴的后果是改一个清洗规则就要重新排版一遍。用 python-docx 从模板生成能把排版成本压到接近零。5.1 用模板文件保留封面与页眉样式from docx import Document from docx.shared import Pt, Cm doc Document(template.docx) # 预置封面、页眉、正文字体 doc.add_heading(大学生网上购物情况调查报告, level1) doc.add_paragraph( f本次调研共发放 {ISSUED} 份回收有效问卷 {len(df)} 份 f有效回收率 {len(df)/ISSUED:.1%}置信水平 95%允许误差 ±5%。 ) doc.add_heading(一、样本结构与消费概况, level2) doc.add_paragraph(f有效样本中男生占比 {male_ratio:.1%} f月均网购金额中位数为 {median_amount:.0f} 元。) doc.add_picture(fig_ticket_box.png, widthCm(14)) doc.add_heading(二、群体差异检验, level2) doc.add_paragraph(f年级与网购频次的卡方检验 p{p:.4f} fCramérs V{cramers_v:.3f}。) doc.save(大学生网上购物情况调查报告.doc)Document(template.docx)而不是Document()是让封面、页眉、正文样式沿用模板add_heading的 level 只是逻辑层级视觉样式由模板决定。图片宽度用Cm(14)而不是像素是因为 A4 页边距 2.5 厘米时正文宽度约 16 厘米14 厘米留出边距不会撑破版面。5.2 一键重跑把清洗规则和结果绑在同一个脚本里import argparse parser argparse.ArgumentParser() parser.add_argument(--input, defaultsurvey_raw.xlsx) parser.add_argument(--issued, typeint, default600, help实际发放份数) parser.add_argument(--out, default大学生网上购物情况调查报告.doc) parser.add_argument(--seed, typeint, default42) args parser.parse_args() # 清洗规则里的任何随机过程都固定种子保证两次运行结果一致 import numpy as np rng np.random.default_rng(args.seed)命令行是python run_report.py --input survey_raw.xlsx --issued 600 --out 报告.doc。把--issued做成参数而不是写死是因为答辩时被问到“回收率怎么算的”改一个数就能重新出表。下面这张自查表放在报告附录第一页比任何解释都管用。检查项应当对上的位置发放份数、有效份数、回收率方法章节 与 附录脚本输出样本量计算公式与参数抽样设计小节剔除规则与影响条数清洗规则表月均消费金额的折算系数变量定义表卡方、回归的 p 值与效应量分析章节 与 图表标题脚本、参数与随机种子附录命令示例把--seed、样本量对账表和清洗规则表一起写进报告附录下一次有人质疑某个数字时你能在三分钟内重跑出同一张表——这份报告的硬度就体现在这一步。本文还有配套的精品资源点击获取