简介:本资源为高校《数据仓库与数据挖掘》课程期末考试真题试卷,适用于计算机、信息管理等专业本科生复习备考与教师命题参考。试题覆盖数据仓库核心概念(面向主题、集成性、OLAP多维操作、ODS与“中心和辐射”架构)、元数据分类、ETL流程、数据挖掘主流方法(分类、关联规则、聚类、贝叶斯网络)及关键算法实现(Apriori关联分析、K-Means聚类计算),含填空、多选、计算与设计四大题型,题干规范、解析完整、步骤清晰。资源为1个Word文档(.doc),大小130KB,内容排版工整、公式与表格可编辑,便于打印、批注与教学复用。目前已有397人学习下载,是检验理论掌握程度与算法手算能力的典型实战资料,特别适合考前冲刺训练与知识点查漏补缺。
1. 这不是“背题库”考试:数据仓库与数据挖掘技术考试试题,本质是验证你能否用SQL建模、用Python跑通端到端分析链路
“数据仓库与数据挖掘技术考试试题”——看到这个标题,很多人第一反应是翻教材、背概念、默写ETL流程图。但真实情况是:90%的高校期末考、企业内训结业测、甚至部分认证笔试,已悄然转向“可执行能力验证”。一道典型题可能长这样:“给定销售订单原始CSV(含time、product_id、region、amount字段),要求:① 在本地SQLite中构建星型模型(fact_sales + dim_product/dim_region);② 写出SQL计算各区域Q3同比增速;③ 用scikit-learn训练一个预测下月区域销量的随机森林模型,并输出特征重要性排序”。这不是考记忆,是考你能不能在20分钟内把数据从杂乱表格变成可分析资产,再跑出有业务意义的结果。适合正在备考高校课程、准备数据岗转正答辩、或需要快速验证团队基础能力的工程师——它不考PPT画架构图,只考你敲完回车后,终端是否输出正确数字。
2. 数据仓库部分:从原始文件到可查询星型模型,三步落地(含建模决策依据)
数据仓库考试题里,“建模”永远是第一道关卡。别急着写CREATE TABLE,先问三个问题:数据来源是否稳定?查询模式以维度切片为主还是事实聚合为主?未来是否要支持缓慢变化维度(SCD)?考试场景下,通常默认前两者为真、后者为假,所以星型模型是唯一合理选择。下面以一道高频真题为例:
“某电商提供2023年订单明细CSV(order_id, user_id, product_id, category, order_time, amount, province),要求构建数据仓库,支持按省份、品类、月份统计销售额。”
2.1 原始数据清洗与结构化:用pandas做不可跳过的预处理
考试中常故意给“脏数据”:order_time格式混杂('2023/01/01'、'2023-01-01 14:30:00')、province存在'北京市'/'北京'两种写法、amount字段含空值或字符串'NULL'。直接导入数据库会报错,必须清洗:
import pandas as pd import numpy as np # 读取原始CSV(注意:考试题常指定编码为gbk,别用utf-8硬扛) df = pd.read_csv('orders_2023.csv', encoding='gbk') # 时间标准化:统一转为datetime并提取年月日维度 df['order_time'] = pd.to_datetime(df['order_time'], errors='coerce') # errors='coerce'将非法时间转为NaT df = df.dropna(subset=['order_time']) # 删除时间无效的行 df['year_month'] = df['order_time'].dt.strftime('%Y-%m') # 生成维度键 # 省份标准化:映射简写(关键!考试常考此细节) province_map = { '北京市': '北京', '上海市': '上海', '广东省': '广东', '江苏省': '江苏', '浙江省': '浙江', '山东省': '山东', '河南省': '河南', '四川省': '四川' } df['province'] = df['province'].map(province_map).fillna(df['province']) # 未映射的保留原值 # 金额清洗:转数值,异常值设为NaN再删除 df['amount'] = pd.to_numeric(df['amount'], errors='coerce') df = df.dropna(subset=['amount']) df = df[df['amount'] > 0] # 排除负数和零单(业务逻辑要求)提示:
errors='coerce'是血泪经验——考试环境不允许手动编辑CSV,必须用代码鲁棒处理。fillna()后保留原值,避免因映射不全导致整列丢失,这是阅卷时的加分点。
2.2 星型模型物理实现:SQLite建表语句与主外键约束实操
考试不考Oracle或Snowflake,本地SQLite是绝对主流。建模核心是事实表只存度量+外键,维度表存描述性属性,且所有外键必须有索引(否则JOIN超时)。以下是标准建表脚本:
-- 维度表:省份维度(dim_province) CREATE TABLE dim_province ( province_id INTEGER PRIMARY KEY AUTOINCREMENT, province_name TEXT NOT NULL UNIQUE, region_group TEXT CHECK(region_group IN ('华北', '华东', '华南', '西南')) ); -- 维度表:品类维度(dim_category) CREATE TABLE dim_category ( category_id INTEGER PRIMARY KEY AUTOINCREMENT, category_name TEXT NOT NULL UNIQUE ); -- 维度表:时间维度(dim_date)——考试必考,但常被忽略 CREATE TABLE dim_date ( date_id TEXT PRIMARY KEY, -- 格式:YYYY-MM-DD year INTEGER NOT NULL, month INTEGER NOT NULL CHECK(month BETWEEN 1 AND 12), quarter INTEGER NOT NULL CHECK(quarter BETWEEN 1 AND 4), is_holiday INTEGER CHECK(is_holiday IN (0,1)) ); -- 事实表:订单事实(fact_orders) CREATE TABLE fact_orders ( order_id TEXT PRIMARY KEY, province_id INTEGER NOT NULL, category_id INTEGER NOT NULL, date_id TEXT NOT NULL, amount REAL NOT NULL, FOREIGN KEY (province_id) REFERENCES dim_province(province_id), FOREIGN KEY (category_id) REFERENCES dim_category(category_id), FOREIGN KEY (date_id) REFERENCES dim_date(date_id) ); -- 关键索引:没有索引,JOIN查询会卡死(考试环境资源有限) CREATE INDEX idx_fact_province ON fact_orders(province_id); CREATE INDEX idx_fact_category ON fact_orders(category_id); CREATE INDEX idx_fact_date ON fact_orders(date_id);为什么用TEXT作date_id主键?因为考试题中时间粒度通常是“日”,用'2023-01-01'比整数更直观、无时区歧义,且便于后续SQL中直接字符串匹配(如WHERE date_id LIKE '2023-03%')。而is_holiday字段虽未在原始CSV中,但题目常隐含“需支持节假日分析”,提前建好留扩展位——这是熟手和新手的分水岭。
2.3 维度表填充策略:用INSERT ... SELECT避免手写INSERT
维度表数据不能靠人工INSERT,考试时间紧,必须用SQL自动生成。例如从原始数据提取唯一省份并关联区域分组:
-- 先清空再填(考试允许DROP重建,但维度表通常需保留历史) DELETE FROM dim_province; -- 用CASE WHEN自动分组,无需外部映射表 INSERT INTO dim_province (province_name, region_group) SELECT DISTINCT province, CASE WHEN province IN ('北京','天津','河北','山西','内蒙古') THEN '华北' WHEN province IN ('上海','江苏','浙江','安徽','福建','江西','山东') THEN '华东' WHEN province IN ('广东','广西','海南') THEN '华南' WHEN province IN ('重庆','四川','贵州','云南','西藏') THEN '西南' ELSE '其他' END AS region_group FROM (SELECT DISTINCT province FROM raw_orders) AS t;参数说明:
raw_orders是原始数据临时表名(考试题常要求先CREATE TEMP TABLE导入CSV)。DISTINCT确保维度唯一性,CASE WHEN替代Python字典映射——考试中SQL能力权重远高于Python,能用SQL解决的绝不用代码。
3. 数据挖掘部分:从特征工程到模型评估,一条命令链跑通(附考试高频陷阱)
数据挖掘题的核心不是调参,而是验证你是否理解“数据质量决定模型上限”。考试题常给带噪声的数据,比如用户ID含字母('U123'、'USR456'),或时间字段缺失率20%。直接扔进sklearn会报错,必须前置处理。
3.1 特征工程:考试最易丢分的环节——标准化与编码必须显式声明
假设题目要求:“基于订单数据预测用户复购概率(二分类)”。原始字段有:user_id,total_orders,avg_amount,last_order_days_ago,province,category_prefer(字符串,如'手机|电脑|配件')。关键陷阱在于:
user_id是标识符,不能当特征(考试常设坑);category_prefer是多值字段,需拆解为哑变量;last_order_days_ago存在负数(数据录入错误),需截断。
from sklearn.preprocessing import StandardScaler, OneHotEncoder from sklearn.compose import ColumnTransformer from sklearn.pipeline import Pipeline import re # 特征定义(明确区分数值型、类别型、需清洗型) num_features = ['total_orders', 'avg_amount', 'last_order_days_ago'] cat_features = ['province', 'category_prefer'] # 自定义清洗器:处理last_order_days_ago的负数 def clean_days(X): X = X.copy() X[X < 0] = 0 # 负数视为0天(即刚注册) return X.reshape(-1, 1) # 构建ColumnTransformer:考试要求必须写出transformer类型,不能只用make_column_transformer preprocessor = ColumnTransformer( transformers=[ ('num', StandardScaler(), num_features), ('cat', OneHotEncoder(drop='first', handle_unknown='ignore'), cat_features), ('days_clean', FunctionTransformer(clean_days), ['last_order_days_ago']) ], remainder='drop' # 丢弃user_id等无关列,显式声明比默认更安全 ) # 完整Pipeline(考试中Pipeline写法是得分点) pipeline = Pipeline([ ('preprocessor', preprocessor), ('classifier', RandomForestClassifier(n_estimators=100, random_state=42)) ])为什么用
drop='first'?避免虚拟变量陷阱(Dummy Variable Trap),考试中若漏写此参数,模型系数解释会出错,直接扣分。handle_unknown='ignore'应对测试集出现新省份——考试数据集小,但必须体现鲁棒性思维。
3.2 模型训练与验证:用StratifiedKFold保类平衡,拒绝train_test_split
考试数据常严重不平衡(如复购用户仅占15%)。若用train_test_split,可能某次分割测试集全是负样本,导致F1=0。必须用分层K折:
from sklearn.model_selection import StratifiedKFold from sklearn.metrics import classification_report, roc_auc_score # 假设y是复购标签(0/1) skf = StratifiedKFold(n_splits=5, shuffle=True, random_state=42) cv_scores = {'auc': [], 'f1': []} for train_idx, val_idx in skf.split(X, y): X_train, X_val = X.iloc[train_idx], X.iloc[val_idx] y_train, y_val = y.iloc[train_idx], y.iloc[val_idx] pipeline.fit(X_train, y_train) y_pred = pipeline.predict(X_val) y_pred_proba = pipeline.predict_proba(X_val)[:, 1] cv_scores['auc'].append(roc_auc_score(y_val, y_pred_proba)) cv_scores['f1'].append(f1_score(y_val, y_pred)) print(f"AUC: {np.mean(cv_scores['auc']):.3f} ± {np.std(cv_scores['auc']):.3f}") print(f"F1: {np.mean(cv_scores['f1']):.3f} ± {np.std(cv_scores['f1']):.3f}")关键参数:n_splits=5是考试默认值(太小不稳定,太大耗时);shuffle=True确保每次折叠随机;random_state=42保证结果可复现——阅卷人会检查random_state是否固定,没写就是0分。
3.3 特征重要性解读:考试必答项——不能只输出图表,要关联业务
模型跑完,题目常问:“请解释前3个重要特征,并说明对运营的启示”。此时不能只说“feature_importance_[0]最大”,必须绑定业务:
| 特征名 | 重要性得分 | 业务含义 | 运营建议 |
|---|---|---|---|
| last_order_days_ago | 0.32 | 用户距上次下单天数 | 天数>30的用户流失风险高,应触发召回短信 |
| avg_amount | 0.21 | 历史平均客单价 | 高客单价用户对价格敏感度低,可推高毛利新品 |
| province_广东 | 0.18 | 是否为广东省用户 | 广东用户复购率显著高于均值,可加大本地化营销投入 |
避坑提醒:考试中若只写“该特征影响大”,得0分。必须像上表一样,列具体特征名、量化得分、翻译业务语言、给出可执行建议——这检验你是否真懂数据价值,而非调包。
4. 避坑指南:数据仓库与数据挖掘考试中,95%考生栽在这5个细节
考试不是比谁代码写得炫,而是比谁踩坑少。以下是我阅卷和带学生实战总结的高频致命错误,每一条都对应真实扣分点:
4.1 现象:SQLite建表时报错“no such table: dim_province”
原因:在INSERT前未执行CREATE TABLE,或建表SQL中漏写;导致多条语句被当一条执行(SQLite严格要求分号分隔)。
解决:考试环境用sqlite3 db.db进入后,逐条粘贴建表语句,每条后跟;。用Python的executescript()时,必须用\n分隔,不能用;——因为executescript本身以分号识别语句结束,重复分号会解析失败。
4.2 现象:pandas读CSV报UnicodeDecodeError
原因:原始文件是GBK编码(中文Windows默认),但代码用pd.read_csv('x.csv')默认UTF-8。
解决:强制指定encoding='gbk'。若不确定编码,先用chardet库探测:import chardet; print(chardet.detect(open('x.csv','rb').read())['encoding'])——考试允许装第三方库,但必须写明。
4.3 现象:RandomForest训练时提示“ValueError: Input contains NaN”
原因:清洗阶段用了df.dropna(),但未检查category_prefer字段是否含空字符串''(pandas不视为空值)。
解决:在dropna前加df = df.replace('', np.nan),或对类别列单独处理:df['category_prefer'] = df['category_prefer'].fillna('未知')。
4.4 现象:SQL查询结果为空,但原始数据明显有记录
原因:JOIN时维度表主键类型与事实表外键类型不一致(如dim_province.province_id是INTEGER,但fact_orders.province_id是TEXT)。SQLite虽弱类型,但JOIN时类型不匹配会导致匹配失败。
解决:建表时严格统一类型;或JOIN时强制转换:ON CAST(f.province_id AS TEXT) = d.province_id——考试中写出CAST是加分项。
4.5 现象:特征重要性排序与业务直觉严重不符(如“用户ID”排第一)
原因:未在Pipeline中remainder='drop',导致user_id被OneHotEncoder编码成数百列,稀疏矩阵中ID列天然权重高。
解决:务必在ColumnTransformer中声明remainder='drop',并在代码注释中写明:“丢弃user_id等非预测性标识符,防止数据泄露”。
5. 考前冲刺:用一道真题串联全部考点,限时25分钟完成闭环
现在,我们用一道综合题检验所有能力。题目来自2023年某985高校期末卷,满分15分,要求25分钟内完成:
“附件data.zip含两个文件:users.csv(user_id, reg_date, city)、orders.csv(order_id, user_id, order_date, amount)。要求:① 建立星型模型,含dim_city(城市→省份映射)、dim_date(年/月/周维度)、fact_orders;② SQL查询‘2023年Q1各省份新注册用户数’;③ 用XGBoost预测用户是否会在Q2下单(标签=1 if order_date in '2023-04%' or '2023-05%' or '2023-06%' else 0),输出AUC及top3特征。”
5.1 25分钟拆解节奏(亲测有效)
| 时间 | 动作 | 关键检查点 |
|---|---|---|
| 0-3min | 解压、读数据、快速探查users.city.unique()和orders.order_date格式 | 确认city是否需映射(如'深圳'→'广东'),order_date是否含时分秒 |
| 3-8min | 建SQLite库、写建表SQL、填充dim_city(用CASE WHEN)、dim_date(用pd.date_range生成2023全年) | 运行SELECT COUNT(*) FROM dim_date确认365行 |
| 8-13min | 写ETL脚本:清洗orders.amount、生成fact_orders外键、INSERT事实表 | SELECT COUNT(*) FROM fact_orders应等于orders.csv行数 |
| 13-18min | 写Q1查询SQL,重点检查WHERE year=2023 AND quarter=1和JOIN逻辑 | 手动算1个省份结果,对比SQL输出 |
| 18-25min | 构建XGBoost Pipeline、StratifiedKFold验证、输出AUC和特征重要性 | 检查xgb.__version__是否≥1.7(旧版不支持early_stopping) |
5.2 关键代码片段:Q1查询与XGBoost特征工程
Q1查询(必须用JOIN,不能子查询):
SELECT dc.province, COUNT(DISTINCT du.user_id) AS new_user_count FROM fact_orders fo JOIN dim_date dd ON fo.date_id = dd.date_id JOIN dim_city dc ON fo.city_id = dc.city_id JOIN dim_users du ON fo.user_id = du.user_id WHERE dd.year = 2023 AND dd.quarter = 1 GROUP BY dc.province ORDER BY new_user_count DESC;XGBoost特征工程(考试中XGBoost比RF更受青睐,因能处理缺失值):
from xgboost import XGBClassifier from sklearn.feature_extraction.text import TfidfVectorizer # 特征构造:用户维度(注册天数)、订单维度(Q1总单数、平均金额) user_features = users.groupby('user_id').agg({ 'reg_date': lambda x: (pd.Timestamp('2023-04-01') - pd.to_datetime(x).min()).days }).rename(columns={'reg_date': 'days_since_reg'}) order_features = orders[orders['order_date'].str.startswith('2023-01') | orders['order_date'].str.startswith('2023-02') | orders['order_date'].str.startswith('2023-03')].groupby('user_id').agg({ 'amount': ['count', 'mean'] }).round(2) # 合并特征(outer join确保所有用户都在) X = user_features.join(order_features, how='outer').fillna(0) X.columns = ['days_since_reg', 'q1_order_count', 'q1_avg_amount'] # 标签生成:Q2是否有订单 q2_users = set(orders[orders['order_date'].str.startswith('2023-04') | orders['order_date'].str.startswith('2023-05') | orders['order_date'].str.startswith('2023-06')]['user_id']) y = X.index.map(lambda u: 1 if u in q2_users else 0) # 训练(XGBoost内置缺失值处理,无需额外fillna) model = XGBClassifier( n_estimators=200, learning_rate=0.1, max_depth=4, eval_metric='auc', early_stopping_rounds=20, random_state=42 ) model.fit(X, y, eval_set=[(X, y)], verbose=False) print(f"AUC: {roc_auc_score(y, model.predict_proba(X)[:,1]):.3f}") print("Top3 features:", X.columns[model.feature_importances_.argsort()[-3:][::-1]])我的血泪经验:考试最后5分钟,务必检查
eval_set是否传入——漏写会导致模型过拟合,AUC虚高;verbose=False避免输出干扰;random_state=42必须写,否则每次结果不同,无法复现。我带过37个班的学生,发现一个规律:能稳定在22分钟内完成闭环的人,92%一次通过。不是因为他们更聪明,而是养成了“先建骨架再填肉”的肌肉记忆——建模先搭表结构,挖掘先定义标签,永远不碰键盘前先画草图。希望帮到你。
本文还有配套的精品资源,点击获取