news 2026/9/18 1:21:53

大学生网购调研数据管线:问卷设计、数据清洗与自动报告生成

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
大学生网购调研数据管线:问卷设计、数据清洗与自动报告生成

简介:围绕大学生网络消费行为展开的调查报告文档,适用于电子商务、市场营销、社会学等专业的学生与教师,也可为校园消费调研或课程作业提供参考样本。全文以问卷调研为主线,依次覆盖调查背景与目的、提纲设计、样本选取与抽样方法,并对年级分布、店铺搜索路径、网店选择影响因素、常用购物平台、购买商品类别、购物频率、发展趋势与交易安全性等维度逐一展开数据统计与图表分析,其中随机抽取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(z=1.96, p=0.5, e=0.05, deff=1.3, valid_rate=0.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(bold=True) c.alignment = Alignment(horizontal="center") ws.freeze_panes = "A2" # 冻结表头,录入时不易错行 # 性别、年级用下拉列表,避免出现"男生""男 "这类同义异形 dv_gender = DataValidation(type="list", formula1='"1,2"', allow_blank=False) dv_grade = DataValidation(type="list", formula1='"1,2,3,4"', allow_blank=False) 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(type="decimal", operator="between", formula1=0, formula2=20000, allow_blank=True) ws.add_data_validation(dv_ticket) dv_ticket.add("H2:H2000") wb.save("survey_template.xlsx")

三个参数值得说明:allow_blank=False用在关键分组字段上,逼着录入选值;freeze_panes="A2"是给人工录入减负,行数一多没有冻结表头很容易串行;数值型字段的范围约束比事后清洗便宜得多。

2.4 预调研:30 份试填要盯的三个指标

正式发放前先找 30 个人试填,看三件事:量表题的信度、填答时长分布、以及逻辑矛盾率。信度用 Cronbach's α,一般要求 0.7 以上,低于 0.6 说明题目在测不同的东西,要删题或改表述。

import pandas as pd def cronbach_alpha(df): """df 的每一列是一道量表题,行是样本""" df = df.dropna() k = df.shape[1] var_items = df.var(axis=0, ddof=1).sum() # 各题方差之和 var_total = df.sum(axis=1).var(ddof=1) # 总分方差 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 读入与类型统一

先用dtype=str全部按字符串读进来,再逐列转换,避免 pandas 把“1”和“1.0”混着解析,也避免学号被当成数字丢掉前导零。

import pandas as pd import numpy as np raw = pd.read_excel("survey_raw.xlsx", sheet_name="responses", dtype=str) 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], errors="coerce") raw["gender"] = raw["gender"].astype("Int64") df = raw.copy() print(df.dtypes)

errors="coerce"是关键参数:它把“未填”“无”这类文本变成 NaN,而不是抛异常中断脚本。这样后面能统一按缺失值规则处理,也能顺便统计出哪一列脏得最厉害。

3.2 缺失值、逻辑矛盾与异常值的三条规则

缺失值不能一律删。关键字段(性别、年级、网购频次)缺失的样本对分组分析没价值,直接剔除;非关键字段(客单价、满意度)缺失可以保留,做对应分析时再按列删。逻辑矛盾和异常值要按规则表处理,处理动作和影响样本量都要记录。

检查项判定条件处理动作典型影响
填答过快duration < 60 秒整条剔除剔除 3%~8%
重复提交student_id 重复保留最早一条剔除 1%~3%
关键字段缺失gender/grade/freq 为空整条剔除剔除 2%~5%
行为矛盾freq=1 且 avg_ticket>0客单价置 0修正而非剔除
数值越界avg_ticket>20000 或 <0按缺失处理影响 <1%
量表直线作答impulse~satis 全部同值标记但不删,做敏感性分析5%~10%
# 1) 填答时长过滤 df = df[df["duration"].fillna(0) >= 60] # 2) 同一学号重复提交,保留第一次 df = df.sort_values("duration", ascending=False).drop_duplicates("student_id", keep="first") # 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 步的keep="first"是配合上一行的排序用的:先按时长降序排,再保留第一条,等于留下填得最认真的那份。如果直接按原顺序去重,留下的可能是随手点的那份。

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[f"platform_{p}"] = df["platform"].apply(lambda s: int(p in split_multi(s))) for c in CATEGORIES: df[f"cat_{c}"] = df["category"].apply(lambda s: int(c in split_multi(s))) # 平台渗透率 = 选择人数 / 有效样本 penetration = df[[f"platform_{p}" for p in PLATFORMS]].mean().sort_values(ascending=False) print((penetration * 100).round(1))

列名统一加前缀(platform_cat_)是为了后面批量取列:df.filter(like="platform_")一行就能把整块矩阵拿出来做交叉分析,不用手写列名列表。

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"], bins=bins, labels=labels, right=False) # 频次映射为月单量(经验系数,需在报告口径中说明) 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))

right=False表示左闭右开,边界值 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(f"chi2={chi2:.2f}, p={p:.4f}, dof={dof}, V={cramers_v:.3f}") print("期望频数小于5的格子占比:", (expected < 5).mean().round(3))

两个参数要点:p < 0.05 只说明“有差异”,差异有多大要看 Cramér's V,0.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, missing="drop").fit(cov_type="HC3") # 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_type="HC3"处理异方差,客单价这类右偏数据几乎一定存在异方差,用普通标准误会把显著性说得过于乐观。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(column="avg_ticket", by="cost_bin", ax=ax, grid=False) ax.set_title("不同生活费档位的客单价分布") ax.set_xlabel("月生活费档位") ax.set_ylabel("客单价(元)") plt.suptitle("") plt.tight_layout() plt.savefig("fig_ticket_box.png", bbox_inches="tight")
图形类型适合的变量组合关键参数常见误用
分组柱状图分组变量 × 定序变量rot=0防标签倾斜用堆叠柱比多选占比
箱线图分组变量 × 连续变量showfliers=False看主体样本量 < 30 时硬画
热力图多选哑变量的共现矩阵annot=True标数值拿它当相关系数图
散点加拟合线两个连续变量先看分布再加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("大学生网上购物情况调查报告", level=1) doc.add_paragraph( f"本次调研共发放 {ISSUED} 份,回收有效问卷 {len(df)} 份," f"有效回收率 {len(df)/ISSUED:.1%};置信水平 95%,允许误差 ±5%。" ) doc.add_heading("一、样本结构与消费概况", level=2) doc.add_paragraph(f"有效样本中男生占比 {male_ratio:.1%}," f"月均网购金额中位数为 {median_amount:.0f} 元。") doc.add_picture("fig_ticket_box.png", width=Cm(14)) doc.add_heading("二、群体差异检验", level=2) doc.add_paragraph(f"年级与网购频次的卡方检验 p={p:.4f}," f"Cramér's 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", default="survey_raw.xlsx") parser.add_argument("--issued", type=int, default=600, help="实际发放份数") parser.add_argument("--out", default="大学生网上购物情况调查报告.doc") parser.add_argument("--seed", type=int, default=42) 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、样本量对账表和清洗规则表一起写进报告附录,下一次有人质疑某个数字时,你能在三分钟内重跑出同一张表——这份报告的硬度,就体现在这一步。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/18 1:18:13

NAO机器人舞步控制:节拍对齐、关节约束与实时反馈闭环

简介&#xff1a;本资源是一份面向机器人开发初学者与高校科研人员的NAO人形机器人舞步程序设计技术论文&#xff0c;聚焦于二次开发实践&#xff0c;解决人形机器人仿生步态规划与舞蹈动作编程等核心问题。全文系统梳理NAO硬件架构&#xff08;25自由度、多模态传感器阵列&…

作者头像 李华
网站建设 2026/9/18 1:16:25

YOLOv11驱动的卫星影像违章建筑检测:从多光谱融合到变化监测

简介&#xff1a;针对城市治理中日益突出的违章建筑问题&#xff0c;这份二十九页的PDF文档系统讲解了基于YOLOv11与卫星影像融合的智能检测方案。内容从YOLO系列算法的发展历程讲起&#xff0c;细致剖析YOLOv11的整体架构、目标检测原理及其优势局限&#xff0c;并介绍卫星影像…

作者头像 李华
网站建设 2026/9/18 1:15:47

从零构建漫画推荐系统:数据、算法与在线部署实战

简介&#xff1a;这是一篇基于Python的漫画平台推荐系统毕业设计论文&#xff0c;目标读者是计算机相关专业的本科生、研究生&#xff0c;以及正在开发推荐系统项目的开发者。文中不仅阐述了研究背景与开发意义&#xff0c;还系统介绍了Python、B/S架构、MySQL、Django、Vue、J…

作者头像 李华
网站建设 2026/9/18 1:15:10

MCP Server 在 Cline 里红点?让 Codex 查配置,TaoToken 给 Key

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/18 1:14:34

数字孪生智慧园区落地技术路径:BIM+GIS+IoT全栈实践

简介&#xff1a;本资源是一份面向智慧园区建设方、数字化解决方案提供商及城市运营管理者的专业级PPT方案&#xff0c;聚焦数字孪生技术在智能可视运营平台中的落地实践&#xff0c;系统性回应传统园区存在的建设周期长、运维依赖人力、数据孤岛严重、安全隐忧突出等核心痛点。…

作者头像 李华