RAAMove — download & preprocess

Discourse moves in research-article abstracts (8 moves)

What it is. Sentences from research-article abstracts, each labeled with one of 8 rhetorical moves (Background, Gap, Method, Purpose, Result, Conclusion, Contribution, Implication).

Difficulty of the labeling judgment: ★★★ — hard. Telling a Gap from Background needs rhetorical judgment.

License: CC BY 4.0
Cite: Liu et al. (2024), LREC-COLING. github.com/ljk1228/RAAMove


Every dataset in this course is reshaped into the same canonical schema so one notebook works for all of them:

[{"id": 1, "text": "...", "label": "..."}]

The raw data, though, looks different every time. That difference is the lesson — half of building a gold standard is getting messy real data into a clean, consistent shape.

Step 1 — Download the raw data

Clone the GitHub repository. The data is two JSON files (one per discipline area).

Show code
!git clone --depth 1 https://github.com/ljk1228/RAAMove

Step 2 — Look at the raw format

Each file is a list of objects with idx (which abstract), text (the sentence), and labels (a short move code, e.g. BAC).

Show code
import json
with open("RAAMove/Intelligence.json", encoding="utf-8") as f:
    raw = json.load(f)
print("sentences in this file:", len(raw))
raw[:3]

Step 3 — Reshape into the canonical schema

Two decisions:

  1. Combine both files (both disciplines) into one dataset.
  2. Expand the move codes into readable names (BACBackground) and fit {id, text, label}.
Show code
CODES = {"BAC": "Background", "GAP": "Gap", "MTD": "Method",
         "PUR": "Purpose", "RST": "Result", "CLN": "Conclusion",
         "CTN": "Contribution", "IMP": "Implication"}

rows = []
for fname in ["RAAMove/Intelligence.json", "RAAMove/Engineering.json"]:
    with open(fname, encoding="utf-8") as f:
        for r in json.load(f):
            rows.append({"text": r["text"].strip(),
                         "label": CODES.get(r["labels"], r["labels"])})

print("combined sentences:", len(rows))

Step 4 — Inspect the labels

Notice the classes are imbalanced — there are far more Method sentences than Implication. That is why we balance before testing.

Show code
from collections import Counter
counts = Counter(item["label"] for item in rows)
print("total items:", len(rows))
print("label counts:", dict(counts))
rows[:3]  # peek at the first three reshaped items

Step 5 — Build a balanced gold set

We take 8 per move here (8 moves × 8 = 64 items).

Show code
# Build a small BALANCED gold set: an equal number of items per label.
# Balance matters so precision/recall/F1 and the confusion matrix are meaningful.
import random
from collections import defaultdict

PER_LABEL = 8          # how many items per label
random.seed(42)         # fixed seed = same sample every run (reproducible)

by_label = defaultdict(list)
for item in rows:
    by_label[item["label"]].append(item)

gold = []
for label in sorted(by_label):
    bucket = by_label[label]
    random.shuffle(bucket)
    gold.extend(bucket[:PER_LABEL])

random.shuffle(gold)
gold = [{"id": i + 1, "text": x["text"], "label": x["label"]} for i, x in enumerate(gold)]

from collections import Counter
print("items:", len(gold), "| per label:", dict(Counter(x["label"] for x in gold)))

Step 6 — Save it

Show code
OUT_FILE = "raamove_moves.json"
Show code
import json
with open(OUT_FILE, "w", encoding="utf-8") as f:
    json.dump(gold, f, ensure_ascii=False, indent=2)
print(f"Saved {len(gold)} items to {OUT_FILE}")
gold[:3]  # preview the first three items

You built raamove_moves.json. Use it in the Day 3 tutorial (replicating Kim & Lu).