简介:这份PDF是田纳西大学数字人文研究生证书课程使用的作品集评估量表(Rubric1),面向修读数字人文方向的研究生、课程助教与指导教师,用于解决作品集如何组织、按什么维度评审才算达标的问题。量表围绕公共网站展示、项目多样性、技术应用与创新、内容质量、呈现与交流五类指标展开,逐项给出从“不可接受/不完整”到“优秀”的四级评分描述,并要求学生为每门数字人文课程提交代表性项目,注明个人或协作分工,同时说明图像与文献的引用出处。资源包含1个PDF文件,约109KB,轻量便于打印或随课程材料分发;英文原文保留评审说明、评分体系与自检条款,可直接用作评估表或作品集自评清单。目前已有82人学习下载,适合需要对照官方标准打磨作品集、准备年终展示论坛的研究生与教师参考。
1. 数字人文作品集 Rubric1 的核心逻辑:一份评分表如何决定证书申请结果
见过太多人把数字人文作品集当成“项目成果的 PDF 合订本”,结果在 Rubric1 的评分表上栽了跟头。一个做晚清报刊文本挖掘的朋友,把三年积累的 Python 脚本、GIS 地图、词频可视化全部导出成静态图片塞进一份两百页的文档,自认为内容饱满。对照 Rubric1 逐项自评后,技术实现和可复现性两个维度连及格线都没碰到。问题不在他做了什么项目,而在评分表关心的不是“你做了多少”,而是“别人能不能看懂、能不能验证、能不能接着用”。
Rubric1 是一份把“数字方法”和“人文论证”两条评价线拆成可操作条目的评分工具。它不看项目规模,也不在意用了多少热门工具,逐项检查的是:问题意识是否从人文材料中生长出来、技术选型是否支撑了论证、数据和代码是否可追溯、文档能否让第三方独立复现。田纳西大学数字人文研究生证书课程把这份作品集当作结业门槛,意味着评审同时站在学术导师和工程验收两个位置上审视你的材料。
不管你是否申请这个证书项目,只要你在做数字人文方向的作品集或项目文档,Rubric1 的结构都值得当成需求规格来读。后面几章从评分维度拆解入手,落到自动化自检脚本和构建流程,让评分表上的每一条都能对应到你仓库里的某个文件、某段代码或者某条提交记录。
2. 拆解 Rubric1 的评分维度:把“人文+技术”翻译成可检查的工程指标
把评分表当需求文档读,第一步是找出它的维度分布和权重倾向。Rubric1 的条目通常围绕六个锚点展开,每个锚点下面再分若干可观察的行为描述。这些描述不是空泛的形容词,而是可以对应到具体文件、命令和目录结构的检查项。
2.1 Rubric1 的六个评分锚点与权重分布
根据常见的 DH 作品集评审框架和 Rubric1 的条目结构,评分维度大致可以归为六类。不同版本在权重上有差异,但核心锚点相对稳定。
| 评分锚点 | 检查内容 | 典型权重 | 对应产出物 |
|---|---|---|---|
| 问题意识与人文论证 | 研究问题是否从材料中产生,数字方法是否服务于论证 | 25% | 项目说明、研究日志 |
| 技术实现深度 | 工具选择理由、代码复杂度、数据处理链路 | 20% | 源码仓库、技术笔记 |
| 数据管理与规范 | 数据来源、格式标准、元数据、许可协议 | 15% | data/ 目录、datapackage.json |
| 可复现性 | 环境配置、依赖锁定、构建脚本 | 15% | Dockerfile、requirements.txt |
| 文档与呈现 | README、使用说明、界面可读性 | 15% | README.md、静态站点 |
| 批判性反思 | 对方法局限、伦理问题、失败尝试的讨论 | 10% | reflection.md、issue 记录 |
这张表里最容易踩坑的是权重分布。很多人把 80% 的精力砸在“技术实现深度”上,写了几千行代码,但问题意识和反思两块加起来 35% 的分数几乎空着。评审看到的是一个技术演示,不是一个数字人文项目。
注意:不同年份的 Rubric1 可能在权重上有微调,建议拿到最新版 PDF 后先做一次权重对照,把每一项的分值抄到项目管理的看板里。
2.2 从评分描述到可检查项:把每条 rubric 翻译成布尔判定
评分表上的描述往往是“作品展现了清晰的论证逻辑”这种句子。要让它可检查,得往下翻译一层。我一般会把每条描述改写成一个可以用“是/否”回答的问题,再绑定到一个文件路径或命令的输出。
下面这张对照表是把 Rubric1 中“可复现性”维度的三条描述拆开后的结果:
| 原始评分描述 | 可检查问题 | 检查方式 |
|---|---|---|
| 项目提供了完整的环境配置说明 | 仓库根目录是否存在 README 或 INSTALL,且包含依赖安装命令 | test -f README.md |
| 依赖版本被锁定 | 是否存在 requirements.txt、package-lock.json 或 environment.yml | find . -maxdepth 2 -name "*.lock" -o -name "requirements.txt" |
| 第三方能按文档独立运行 | 在干净容器里执行构建脚本是否无报错 | docker build -t portfolio-test . && docker run --rm portfolio-test |
这种翻译方式的好处是,你可以把它写进一个检查脚本,每次提交前自动跑一遍。人工逐条对照评分表容易漏项,脚本不会。
2.3 权重最高的“问题意识”维度怎么落地成文件
问题意识这一块看起来最“软”,但它在 Rubric1 里权重最高,而且完全可以用工程化的方式组织材料。我通常会在仓库里建一个narrative/目录,里面放三样东西:研究问题的来源说明、材料选择理由、数字方法介入的逻辑。
narrative/ ├── 01-research-question.md # 问题从哪份档案、哪个现象中产生 ├── 02-source-selection.md # 为什么选这批材料,排除了什么 └── 03-method-rationale.md # 为什么用这个词频工具而不是那个逻辑说明:这三个文件分别回答评审在“问题意识”维度下最常追问的三个问题。第一个文件说明问题不是凭空来的,第二个文件展示你对材料边界的判断,第三个文件证明技术选型是论证驱动的而不是技术驱动的。参数上没有硬性要求,但每份文件建议控制在 800 到 1500 字,太长评审不会细看,太短显得敷衍。
3. 用 Python 对作品集做 Rubric1 自动化自评:文件结构、元数据与代码质量三路扫描
人工逐条对照评分表容易漏,我一般会写一个自评脚本,把 Rubric1 里可自动化的检查项跑一遍。脚本不替代人工判断,但能确保文件结构、元数据完整性和基本代码质量不出低级问题。
3.1 用 pathlib 扫描作品集目录结构并匹配评分表条目
第一路扫描做的是目录结构和文件存在性检查。把 Rubric1 里“文档与呈现”“数据管理”两个维度的基础要求写成一个配置字典,脚本遍历仓库后输出缺失项。
from pathlib import Path import json # 把 Rubric1 中可自动检查的条目写成配置 RUBRIC_CHECKS = { "readme": {"path": "README.md", "weight": 3, "desc": "项目总说明"}, "data_dir": {"path": "data", "weight": 3, "desc": "原始数据与处理后数据"}, "license": {"path": "LICENSE", "weight": 1, "desc": "数据与代码许可"}, "narrative": {"path": "narrative", "weight": 4, "desc": "问题意识与论证文档"}, "notebooks": {"path": "notebooks", "weight": 2, "desc": "分析过程记录"}, "reflection": {"path": "reflection.md", "weight": 3, "desc": "批判性反思"}, } def scan_portfolio(root: str) -> dict: root_path = Path(root) results = {} for key, check in RUBRIC_CHECKS.items(): target = root_path / check["path"] results[key] = { "exists": target.exists(), "is_dir": target.is_dir() if target.exists() else False, "weight": check["weight"], "desc": check["desc"], } return results if __name__ == "__main__": report = scan_portfolio(".") missing = [k for k, v in report.items() if not v["exists"]] total = sum(v["weight"] for v in report.values() if v["exists"]) max_total = sum(v["weight"] for v in report.values()) print(json.dumps(report, indent=2, ensure_ascii=False)) print(f"\n基础项得分: {total}/{max_total}") if missing: print(f"缺失项: {', '.join(missing)}")逻辑说明:RUBRIC_CHECKS字典把评分表里可自动检查的条目映射到文件路径上,weight字段是该项在自评体系里的相对重要性。scan_portfolio函数遍历根目录,用pathlib检查每个目标是否存在。输出分两部分:完整报告和缺失项摘要。
参数说明:root参数指向作品集仓库的根目录,默认当前目录。如果你把作品集分成多个子模块,可以把RUBRIC_CHECKS里的path改成相对子目录的路径。权重值不需要和 Rubric1 完全一致,它只用于自评排序,帮你判断先补哪个缺失项。
3.2 元数据规范校验:从 README 到数据字典的字段完整性
第二路扫描检查文档内容的质量,不只是“文件在不在”,而是“文件里有没有关键信息”。我一般会检查 README 是否包含项目描述、安装步骤、数据来源和使用示例这四个段落。
import re REQUIRED_SECTIONS = { "项目描述": r"##\s*(项目描述|Project Description|Overview)", "安装步骤": r"##\s*(安装|Installation|Setup|Getting Started)", "数据来源": r"##\s*(数据|Data|Dataset)", "使用示例": r"##\s*(使用|Usage|Example)", } def check_readme_sections(readme_path: str) -> dict: text = Path(readme_path).read_text(encoding="utf-8") result = {} for name, pattern in REQUIRED_SECTIONS.items(): result[name] = bool(re.search(pattern, text, re.IGNORECASE)) return result def check_data_dict(data_dir: str) -> list: """检查 data/ 目录下是否有数据字典或元数据文件""" data_path = Path(data_dir) meta_files = list(data_path.glob("*.json")) + list(data_path.glob("*datapackage*")) return [str(f) for f in meta_files]逻辑说明:REQUIRED_SECTIONS用正则匹配 README 里的二级标题,覆盖 Rubric1 对“文档与呈现”维度的最低要求。check_data_dict检查data/目录下是否有 JSON 格式的元数据或 datapackage 描述文件,对应“数据管理与规范”维度。
参数说明:正则里的re.IGNORECASE让匹配不区分大小写,因为有些 README 写英文标题。如果你的项目数据以 CSV 为主,建议在data/下放一个datapackage.json,字段参考 Frictionless Data 规范,至少包含name、resources、licenses三个顶层字段。
3.3 代码质量静态扫描与 Rubric1 技术分项的映射
第三路用radon和pylint做基础代码质量检查,输出圈复杂度和未使用变量等指标。这些指标不直接对应 Rubric1 的评分锚点,但能帮你判断“技术实现深度”维度里代码部分是否站得住。
# 安装静态检查工具 pip install radon pylint # 输出所有 Python 文件的圈复杂度,按复杂度排序 radon cc -s -a notebooks/ src/ --total-average # 检查代码规范问题,只看错误和警告 pylint notebooks/ src/ --disable=all --enable=E,W --output-format=text逻辑说明:radon cc输出每个函数的圈复杂度,-s显示复杂度评分,-a显示平均值。圈复杂度超过 10 的函数在评审眼里是“可读性风险”,建议拆成更小的函数。pylint只打开错误和警告级别,忽略风格建议,避免输出太长。
参数说明:--total-average会在最后输出整体平均复杂度,我一般把目标定在 5 以下。如果你的 notebook 里有很多探索性代码,可以把notebooks/排除在radon检查之外,只对src/做严格检查。
注意:静态检查的结果不要直接贴进作品集正文,而是用来指导修改。评审看到的是修改后的代码,不是检查报告。
3.4 把三路扫描结果汇总成一份自评报告
三路扫描跑完后,把结果合并成一个 Markdown 报告,按 Rubric1 的维度分组呈现。报告里只列缺失项和改进建议,通过项用一行统计带过。
| 扫描路径 | 检查项数量 | 通过 | 缺失/警告 | 对应 Rubric1 锚点 |
|---|---|---|---|---|
| 目录结构 | 6 | 4 | 2 | 文档与呈现、数据管理 |
| 文档内容 | 4 | 3 | 1 | 问题意识、文档与呈现 |
| 代码质量 | 动态 | 动态 | 动态 | 技术实现深度 |
这样一份报告放在仓库的docs/self-assessment.md里,提交前跑一次,能避免大部分低级失分。更重要的是,它让评审看到你对自己的项目有系统性的检查习惯。
4. 从本地 notebook 到可评审的作品集:Git 历史、静态站点与数据持久化的工程链路
自评脚本解决的是“有没有”的问题,这一章解决“能不能被第三方独立跑起来、能不能被长期引用”的问题。Rubric1 里可复现性和数据管理两个维度加起来占 30%,需要一套完整的构建和发布链路来支撑。
4.1 用 Git 提交历史构建“过程性评分”的证据链
Rubric1 的“问题意识与人文论证”维度里,有一条隐含要求:评审希望看到项目是怎么一步步演化的,而不是一个突然出现的成品。Git 提交历史是展示过程的最好材料,但前提是提交信息有结构。
# 查看提交历史的时间分布和提交信息 git log --oneline --since="6 months ago" --pretty=format:"%ad %s" --date=short # 按目录统计提交频次,看哪些部分迭代最多 git log --name-only --pretty=format: | sort | uniq -c | sort -rn | head -20逻辑说明:第一条命令输出近半年的提交记录,日期加提交信息,方便你检查迭代节奏是否合理。第二条统计每个文件被修改的次数,修改频次高的文件通常是项目核心,可以在 README 里重点说明。
参数说明:--since的时间范围根据你的项目周期调整。如果项目只做了三个月,改成--since="3 months ago"。--date=short输出YYYY-MM-DD格式,避免时区和格式混乱。
我一般会在提交信息里用feat:、fix:、docs:、refactor:这几个前缀区分提交类型。评审如果看 Git 历史,能快速理解每个阶段的重点。如果评审不看,这些前缀也不影响项目本身。
4.2 用静态站点把 notebook 和数据展示转成可浏览的评审入口
评审不会去克隆你的仓库再跑 Jupyter。我一般会用一个静态站点生成器把 notebook 导出成 HTML,加一个首页说明,部署到静态托管上,把链接放进 README 顶部。工具选型上,Jekyll 和 Astro 都行,我用 Astro 多一些,因为构建速度快。
# 用 nbconvert 把 notebook 导出为 HTML jupyter nbconvert --to html \ --output-dir dist/notebooks \ --template classic \ notebooks/*.ipynb # 用 Astro 建一个最小站点骨架 npm create astro@latest portfolio-site -- --template minimal cd portfolio-site npm install逻辑说明:jupyter nbconvert把 notebook 转成独立 HTML,--output-dir指定输出目录。--template classic保留输出单元格和代码,适合评审阅读。Astro 的 minimal 模板生成一个空站点,把导出的 HTML 放进public/notebooks/目录,首页用 Markdown 写项目概述和导航。
参数说明:--template可以换成lab获得更接近 JupyterLab 的样式,但classic的打印效果更好。如果你的 notebook 里有交互组件(比如 Plotly),需要额外安装nbconvert[webpdf]或用--to webpdf导出 PDF 版本作为补充。
注意:导出的 HTML 里可能包含本地文件路径或临时变量,提交前用
grep -r "/Users/" dist/检查一遍,把个人路径替换成相对路径。
4.3 数据持久化:从 CSV 到 Parquet 加数据字典的规范化处理
Rubric1 的“数据管理”维度要求数据可追溯、格式规范、有元数据。我一般会把原始数据和处理后数据分开存放,原始数据只读,处理后数据用 Parquet 格式保存,附带一个 JSON 数据字典。
import pandas as pd import json from pathlib import Path # 读取原始 CSV,不做任何修改 raw = pd.read_csv("data/raw/corpus.csv", encoding="utf-8") # 基础清洗后保存为 Parquet cleaned = raw.dropna(subset=["text", "date"]).copy() cleaned["date"] = pd.to_datetime(cleaned["date"]) cleaned.to_parquet("data/processed/corpus.parquet", index=False) # 生成数据字典 data_dict = { "name": "late-qing-corpus", "resources": [ { "path": "data/processed/corpus.parquet", "schema": { "fields": [ {"name": "text", "type": "string", "description": "正文文本"}, {"name": "date", "type": "date", "description": "出版日期"}, {"name": "source", "type": "string", "description": "来源档案编号"}, ] }, } ], "licenses": [{"name": "CC-BY-4.0", "path": "https://creativecommons.org/licenses/by/4.0/"}], } Path("data/datapackage.json").write_text( json.dumps(data_dict, indent=2, ensure_ascii=False), encoding="utf-8" )逻辑说明:raw数据只读,所有清洗操作在新变量上进行。Parquet 格式比 CSV 小,读取快,而且保留数据类型。data_dict按 Frictionless Data 的 datapackage 结构组织,包含资源路径、字段描述和许可协议。
参数说明:dropna的subset指定只删除text和date为空的行。to_parquet的index=False避免把 pandas 索引写进文件。数据字典里的licenses字段必须填,Rubric1 对数据许可有明确要求,缺这项会扣分。
4.4 用 Docker 把环境配置固化成可验证的构建单元
可复现性维度的最高要求是“第三方能在干净环境里跑通”。我一般会写一个 Dockerfile,把 Python 版本、依赖安装和数据下载步骤全部固化。
FROM python:3.11-slim WORKDIR /app # 先复制依赖文件,利用 Docker 层缓存 COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt # 复制项目文件 COPY . . # 运行自评脚本作为构建检查 RUN python scripts/self_assessment.py --fail-on-missing CMD ["jupyter", "nbconvert", "--to", "html", "--output-dir", "dist/", "notebooks/analysis.ipynb"]逻辑说明:基础镜像用python:3.11-slim,体积小且版本明确。先复制requirements.txt再安装依赖,这样修改代码时不会重复安装依赖。RUN python scripts/self_assessment.py --fail-on-missing把自评脚本接入构建流程,缺文件时构建失败。
参数说明:--no-cache-dir避免 pip 缓存增加镜像体积。--fail-on-missing是自评脚本的自定义参数,检测到缺失项时返回非零退出码。如果你的项目需要下载外部数据,在COPY . .之后加一个RUN python scripts/download_data.py,确保构建过程包含数据获取步骤。
5. 提交前的 Rubric1 反查:模拟评审打分与高频扣分点的修复清单
作品集改到最后一轮,最容易出问题的不是技术实现,而是评审打开材料后的前五分钟体验。这一章用一个模拟打分的流程,把 Rubric1 的评分表反过来当检查清单用。
5.1 用评分表做反向检查:从评审视角走一遍材料
我一般会请一个不了解项目的朋友,给他 Rubric1 的评分表和作品集链接,让他按顺序走一遍:先看首页说明,再打开一个 notebook,再翻到反思文档,最后尝试按 README 跑一遍命令。记录他在哪一步卡住、哪一步问问题、哪一步直接跳过。
这个过程的输出是一份“评审路径记录”,把卡住的位置和对应的评分维度标出来。常见的结果是:首页缺少项目一句话说明、notebook 输出单元格太多、README 的安装命令没有指定 Python 版本、反思文档写成了技术总结而不是方法讨论。
注意:模拟评审的人选最好是同专业但不同技术背景的,纯技术背景的人容易忽略人文论证的问题,纯人文背景的人容易忽略环境配置的细节。
5.2 高频扣分点修复:文档缺失、链接腐烂与数据不可获取
根据我见过的作品集,以下五类问题出现频率最高,修复成本也最低。
| 高频扣分点 | 对应 Rubric1 锚点 | 修复方式 | 检查命令 |
|---|---|---|---|
| README 缺少项目一句话说明 | 文档与呈现 | 在标题下加一行>引用,说明项目做什么、用什么数据 | head -5 README.md |
| notebook 输出未清理 | 技术实现深度 | 提交前执行jupyter nbconvert --clear-output --inplace notebooks/*.ipynb | grep -c "execution_count" notebooks/*.ipynb |
| 外部链接失效 | 数据管理与规范 | 用lychee或linkchecker扫描 README 和文档中的 URL | lychee --verbose README.md narrative/*.md |
| 数据文件未附许可 | 数据管理与规范 | 在data/下加LICENSE或在datapackage.json里填licenses字段 | test -f data/LICENSE |
| 反思文档写成技术总结 | 批判性反思 | 重写为“我原本想做什么、实际遇到了什么限制、如果重做会改什么”三段式 | 人工检查 |
# 批量清理 notebook 输出 jupyter nbconvert --clear-output --inplace notebooks/*.ipynb # 用 lychee 检查所有 Markdown 文件中的链接 lychee --verbose --no-progress README.md narrative/*.md docs/*.md # 检查 data/ 目录下是否有许可文件 find data/ -maxdepth 1 -iname "*license*" -o -iname "*copying*"逻辑说明:--clear-output清空 notebook 的执行计数和输出,避免提交几百 KB 的冗余数据。lychee扫描所有链接并报告失效 URL,--no-progress关闭进度条让输出更干净。find命令检查数据目录下的许可文件,匹配license或copying两种常见命名。
参数说明:--inplace直接修改原文件,建议在 Git 提交前执行,这样改动可以单独提交一条chore: clear notebook outputs。lychee的--verbose输出每个链接的检查结果,如果链接太多可以只检查 README。find的-maxdepth 1避免递归到子目录。
5.3 把反查结果写成一份提交说明
最后一轮修改完成后,我一般会在仓库根目录加一个submission-notes.md,记录这一轮改了什么、为什么改、哪些问题已知但未解决。这份文件不是 Rubric1 的硬性要求,但它向评审传递一个信号:你对作品集的质量有主动管理意识。
文件结构建议三段:本轮修改摘要、已知限制、后续计划。每段三五句话,不要写成又一篇反思文档。已知限制那一段尤其重要,Rubric1 的批判性反思维度鼓励你主动暴露问题,而不是等评审来发现。把限制写清楚,反而比假装项目完美得分更高。
本文还有配套的精品资源,点击获取