Compare spaCy phrases with small dependency chunks
phrases and entities
noun chunks
workforce research
Learn how noun chunks group nouns with attached words, using spaCy and a local UDPipe dependency parse.
After counting adjacent phrases, the Riverton Workforce Lab has a practical choice to make: should basic spreadsheet skills become one review item or three separate words?
A grammar-aware method offers one convention for making that choice. It can group candidate phrases for review, but it does not create a single correct inventory of English phrases.
Note
The Riverton Workforce Lab, its job board, and its training flyer are fictional and were created for teaching.
TipWhat you will learn
Use this lesson to practice how to:
define a noun chunk in plain language;
extract noun chunks with a trained spaCy pipeline;
build simple chunks from a UDPipe dependency parse;
explain why chunk definitions differ across tools;
separate agreement from accuracy; and
explain why parser errors affect chunk rules.
Ask a trained pipeline for noun chunks
This setup chunk reads the sentence file, runs spaCy, and later supports a small UDPipe-based rule. A noun chunk is a short, flat phrase built around one noun: the noun plus the determiners, numbers, compounds, and describing words directly in front of it. Chunks do not nest and do not reach past the noun, so evening classes in October becomes evening classes and October, not one long phrase. Where a chunk stops is a convention, and tools draw the line in different places.
knitr::kable( spacy_phrases |>slice_head(n =12),col.names =c("Sentence ID", "spaCy noun chunk"),caption ="First spaCy noun chunks from the Riverton sentences",row.names =FALSE)
First spaCy noun chunks from the Riverton sentences
Sentence ID
spaCy noun chunk
s001
Paid 12-week training
s002
No prior data experience
s003
Evening schedules
s004
Applicants
s004
basic spreadsheet skills
s005
A high school diploma
s006
The employer
s006
certification training
s007
Rotating night shifts
s007
part
s007
the job
s008
Workers
The released spaCy pipeline returns 40 noun chunks for the 28 sentences. For the check sentence, it returns three flat chunks: Riverton Skills Centre, evening classes, and October.
Build chunks from dependency links
The UDPipe model used here was trained for this site on 500 sentences. On 13 held-out political weblog posts from 2003 to 2006, its unlabelled attachment score was 0.7235 and its labelled attachment score was 0.6561. spaCy’s pipeline is a released production model trained on far more text. This comparison is a teaching device, not a contest between equals.
The local UDPipe model has no tokenizer, so the lesson gives it one token per line. In the code, \(tokens) is R shorthand for a small function that handles one sentence’s tokens at a time.
This classroom rule starts from each token tagged as NOUN or PROPN. It then collects direct dependents whose relation is det, amod, compound, or nummod: a determiner such as a or the, a describing word, a noun working with another noun, or a number modifying a noun.
The one token-count difference between the two parses
Sentence ID
spaCy tokens
UDPipe tokens
s017
8
7
knitr::kable( udpipe_chunks |>slice_head(n =12),col.names =c("Sentence ID", "UDPipe-built chunk"),caption ="First noun chunks built from dependency links",row.names =FALSE)
First noun chunks built from dependency links
Sentence ID
UDPipe-built chunk
s001
Paid
s001
Paid week
s001
week training
s002
No prior data experience
s003
Evening schedules
s004
spreadsheet
s004
need basic spreadsheet skills
s005
A high school
s006
The employer pays
s006
certification
s006
certification training
s007
night
The two systems report different row counts because they split words differently. spaCy separates driver's in s017 into driver and 's; the tokenizer feeding UDPipe keeps it as one token, which accounts for the 179 versus 178 row counts. The printed spaCy chunk strings still keep Paid 12-week training and A valid driver's license; the gap is in token rows, not those displayed chunk strings.
This rule emits one chunk for every noun-like tag, so chunks can overlap. Paid, Paid week, and week training all come from the same four words in s001. The rule also pastes a head to its dependents without checking that the words sit next to each other, so Paid week skips over 12- and is not a string in the sentence. Paid appears because the small tagger labelled it PROPN, which is wrong here: it describes the training. A chunk rule inherits tagging mistakes before it makes its own.
Compare the two lists
Exact agreement is strict: the sentence ID and the chunk text must both match. That makes agreement a string match, not accuracy. Neither list is ground truth, and spaCy chunks are non-overlapping while the UDPipe-built list can overlap.
agreed_chunks <-inner_join(distinct(spacy_phrases),distinct(udpipe_chunks),by =c("doc_id", "chunk"))comparison_summary <-tibble(measure =c("spaCy chunks", "UDPipe-built chunks", "exact agreements"),count =c(nrow(spacy_phrases), nrow(udpipe_chunks), nrow(agreed_chunks)))different_examples <-tibble(method =c("spaCy", "UDPipe-built"),sentence_id =c("s005", "s005"),chunk =c("A high school diploma", "A high school"),explanation =c("spaCy keeps the full education phrase.","The dependency rule attaches words to school and leaves diploma outside this chunk." ))spreadsheet_examples <-bind_rows( spacy_phrases |>filter(doc_id =="s004", str_detect(chunk, "spreadsheet")) |>mutate(method ="spaCy"), udpipe_chunks |>filter(doc_id =="s004", str_detect(chunk, "spreadsheet")) |>mutate(method ="UDPipe-built")) |>select(method, doc_id, chunk)knitr::kable( comparison_summary,col.names =c("Measure", "Count"),caption ="String matches across two chunking conventions",row.names =FALSE)
String matches across two chunking conventions
Measure
Count
spaCy chunks
40
UDPipe-built chunks
56
exact agreements
25
knitr::kable( different_examples,col.names =c("Method", "Sentence ID", "Chunk", "Explanation"),caption ="One sentence where the two chunk methods differ",row.names =FALSE)
One sentence where the two chunk methods differ
Method
Sentence ID
Chunk
Explanation
spaCy
s005
A high school diploma
spaCy keeps the full education phrase.
UDPipe-built
s005
A high school
The dependency rule attaches words to school and leaves diploma outside this chunk.
knitr::kable( spreadsheet_examples,col.names =c("Method", "Sentence ID", "Chunk"),caption ="How each method handles the spreadsheet-skills phrase",row.names =FALSE)
How each method handles the spreadsheet-skills phrase
Method
Sentence ID
Chunk
spaCy
s004
basic spreadsheet skills
UDPipe-built
s004
spreadsheet
UDPipe-built
s004
need basic spreadsheet skills
The two lists have 25 exact string matches. That is agreement, not accuracy, and the counts 40 and 56 are not two measurements of the same thing. For the opening question, spaCy groups basic spreadsheet skills, while the UDPipe-built rule also emits spreadsheet and overreaches to need basic spreadsheet skills. A bigram takes neighbouring words even across phrase boundaries; a chunk groups words the parser links together. Whether that helps depends on whether the parser is right.
What to remember
Noun chunks are flat, non-overlapping spans under a tool-specific convention.
spaCy returns 40 noun chunks for these 28 sentences.
The UDPipe-built rule returns 56 overlapping chunks from a deliberately weak teaching model.
The 25 exact matches are agreement, not accuracy.
Paid week shows how a rule can manufacture text by pasting non-adjacent tokens.
Tokenization alone explains the 179 versus 178 row-count gap.
Review chunk lists as prompts for human inspection. When the two tools disagree, check the sentence, the tokenization, and the parse before drawing any conclusion about the phrase.