Dependency relations let you extract subjects, objects and modifiers, which flat tokens and entities cannot express.
The dependency parser links every token to a head token with a labelled relation, forming a tree whose root is usually the main verb.
token.dep_: the relation to its head, such asnsubj(nominal subject),dobj(direct object),amod(adjective modifier),prep/pobj(preposition and its object),ROOTtoken.head: the parent token (the root is its own head)token.children,token.lefts,token.rights: direct dependentstoken.subtree: the token and everything below it, in order;token.ancestorswalks up
This structure answers questions regular expressions can't. To find who did what, look for verbs and collect their nsubj and dobj children. To get the full phrase a word heads, take its subtree: doc[t.left_edge.i : t.right_edge.i + 1].
Labels follow the ClearNLP scheme in the English models (spacy.explain describes each). Passive sentences use nsubjpass and agent, so handle both voices when extracting relations.
displacy.render(doc, style="dep") draws the tree in a notebook or browser, and displacy.serve runs a small web server; looking at real parses is the fastest way to write correct extraction rules.
Parses are predictions and degrade on long, informal or ungrammatical text. Write extraction rules defensively and test them on real data; the DependencyMatcher lets you express tree patterns declaratively.
Example
import spacy
nlp = spacy.load("en_core_web_sm")
doc = nlp("The new manager quickly approved the large budget.")
for t in doc:
print(f"{t.text:9} {t.dep_:7} <- {t.head.text}")
# The det <- manager
# new amod <- manager
# manager nsubj <- approved
# quickly advmod <- approved
# approved ROOT <- approved
# ... budget is dobj <- approved
def svo(doc):
for verb in (t for t in doc if t.pos_ == "VERB"):
subj = [c for c in verb.children if c.dep_ in ("nsubj", "nsubjpass")]
obj = [c for c in verb.children if c.dep_ in ("dobj", "attr")]
if subj and obj:
phrase = lambda t: doc[t.left_edge.i : t.right_edge.i + 1].text
yield phrase(subj[0]), verb.lemma_, phrase(obj[0])
print(list(svo(doc)))
# [('The new manager', 'approve', 'the large budget')]Remember
- Every token has a head and a dep_ label; the root heads the sentence
- children, subtree, left_edge/right_edge navigate the tree
- Combine nsubj/dobj children of verbs to extract relations
- Visualise with displacy to design rules
Watch out for
- Extracting only nsubj and missing passive sentences, which use nsubjpass.