This repository implements a reproducible, modular pipeline for measuring outward-engagement discourse adjustment in Chinese-language foreign policy texts since 2012. The pipeline is designed to run on a laptop, with deterministic sampling options and on-disk caches to make results reproducible.
- Setup
python3.11 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt- Run the pipeline
python 01_collect.py --config-dir config
python 02_segment.py --config-dir config
python 03_embed.py --config-dir config
python 04_score_axes.py --config-dir config
python 05_run_tests.py --config-dir config
python 06_export_excerpt_bank.py --config-dir config- Outputs
- Tables:
outputs/tables/ - Figures:
outputs/figures/ - Excerpts:
outputs/excerpts/excerpt_bank.jsonl
-
Collect sources (
01_collect.py)- Downloads source documents and stores raw HTML in
data/raw/and parsed JSON indata/parsed/. - Uses the date range in
config/analysis.yamlunless overridden by--analysis-start/--analysis-end. - Uses
data/cache/for HTTP response caching. Use--forceto bypass cache.
- Downloads source documents and stores raw HTML in
-
Segment documents (
02_segment.py)- Loads
data/parsed/docs.jsonl, re-parses cached HTML, and writes segmented JSON todata/segments/. - Outputs
data/segments/segments.jsonl.
- Loads
-
Embed segments (
03_embed.py)- Generates embeddings for non-heading segments using the model in
config/models.yaml. - Writes
data/segments/segments_embedded.jsonland embedding cache files indata/embeddings/. - Use
--forceto regenerate embeddings.
- Generates embeddings for non-heading segments using the model in
-
Score axes & outward filter (
04_score_axes.py)- Builds axis vectors from
config/axes.yamland scores each segment. - Applies outward-engagement thresholds from
config/analysis.yaml. - Outputs
data/segments/segments_scored.jsonl.
- Builds axis vectors from
-
Run analyses (
05_run_tests.py)- Writes tables to
outputs/tables/and figures tooutputs/figures/. - Includes trend, coupling, keyness, slogans, and elasticity outputs.
- Writes tables to
-
Export excerpts (
06_export_excerpt_bank.py)- Generates
outputs/excerpts/excerpt_bank.jsonlfrom scored segments.
- Generates
config/sources.yaml: source URLs, sampling caps, and scraping metadata.config/analysis.yaml: date ranges, thresholds, binning, keyness, and slogan settings.config/models.yaml: embedding model settings and cache mode.config/axes.yaml: axis seed sentences.config/slogans_curated.txt: curated slogans list (one per line).config/stoplist_slogans.txt: stoplist for slogan extraction.
For reproducible runs, lock down both configuration and caches.
-
Pin versions
- Use the
requirements.txtexact versions with Python 3.11.
- Use the
-
Fix sampling (press briefings)
- Set
sample_years,sample_strategy, andsample_seedinconfig/sources.yamlundermfa_pressers. - Alternatively, set
max_docs_per_yearandsample_strategy: evenfor deterministic even-spacing.
- Set
-
Fix analysis date ranges
- Set
analysis_start/analysis_endinconfig/analysis.yamlor pass--analysis-start/--analysis-endto01_collect.py. - For lightweight, deterministic runs, enable
sample_mode: trueand setsample_yearinconfig/analysis.yamlto restrict analysis to a single year.
- Set
-
Persist caches
- Keep
data/cache/anddata/raw/under versioned storage for source reproducibility. - Keep
data/embeddings/if you want to reuse exact embeddings across runs. - Use
--forceonly when you want to intentionally refresh cached content.
- Keep
-
Record model settings
config/models.yamlcontrols the embedding model name, device, and cache mode (embeddingsorscores_only).
config/ # Configuration files
scripts/ # Helper scripts
src/ # Pipeline modules
data/cache/ # Cached HTML responses (per source URL)
data/raw/ # Raw HTML snapshots
data/parsed/ # Parsed JSON documents
# After segmentation/embedding/scoring
data/segments/ # Segment JSON files + jsonl aggregations
data/embeddings/ # Embedding caches (.npz per doc)
outputs/tables/ # CSV tables
outputs/figures/ # PNG figures
outputs/excerpts/ # excerpt_bank.jsonl
- URLs and sampling behavior live entirely in
config/sources.yaml; no URLs are hard-coded in the pipeline. mfa_presserscollection relies on live pages unless cached; storedata/cache/anddata/raw/for strict reproducibility.- Set
cache_mode: scores_onlyinconfig/models.yamlif disk space is limited (embedding caches won’t be written).