vicary 0.2.8 → 0.2.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/assets/MANIFEST.json +13 -6
- data/assets/stop_words_never_capitalised.txt +126 -0
- data/assets/stop_words_sometimes_capitalised.txt +109 -0
- data/lib/vicary/candidates.rb +146 -3
- data/lib/vicary/corpus.rb +9 -0
- data/lib/vicary/gates.rb +10 -1
- data/lib/vicary/lexicon.rb +19 -0
- data/lib/vicary/version.rb +1 -1
- metadata +3 -2
- data/assets/stop_words.txt +0 -63
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 2417f5e9aa64949da91c1ca4cf5cc1dac1b8a4bdc70929576bbc7b64d8e629f9
|
|
4
|
+
data.tar.gz: 67cd930a6714621ed7872054fac572b87a210940b1644032a845b16d2b2bbfb3
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: dfe4bc4a8eb71174d969e41f270eadf8c7170a97e54870fc27b8f2610a8ce2e6b976e39cba2ed87435652d8ca366ca4fb9d90795f773bd8b20db6017a3bce99b
|
|
7
|
+
data.tar.gz: 3bef064a891171f7668d8773e813c6efedea6f25385c5da72a4141279662838b623bcefe4a06de28866efdf7f53b04051fa74a02ad6f9c35acc70b9eba1b0f9a
|
data/README.md
CHANGED
|
@@ -69,7 +69,7 @@ gzip, which `zlib` reads, so that hazard does not arise on the default path.
|
|
|
69
69
|
|
|
70
70
|
Two of the last three read the corpus the repository now ships in
|
|
71
71
|
`conformance/corpora/`, so they measure on a bare checkout with no environment
|
|
72
|
-
set: 100% carrier recall and
|
|
72
|
+
set: 100% carrier recall and 7.400 over-fired spans per essay against a ≤ 7.40
|
|
73
73
|
bar, identical to Python and TypeScript. `VICARY_EVAL_CORPUS_TSV` is an override
|
|
74
74
|
for a different corpus, not a requirement.
|
|
75
75
|
|
data/assets/MANIFEST.json
CHANGED
|
@@ -20,14 +20,21 @@
|
|
|
20
20
|
"title": 38024
|
|
21
21
|
}
|
|
22
22
|
},
|
|
23
|
-
"
|
|
24
|
-
"bytes":
|
|
25
|
-
"entries":
|
|
23
|
+
"stop_words_never_capitalised.txt": {
|
|
24
|
+
"bytes": 8017,
|
|
25
|
+
"entries": 499,
|
|
26
26
|
"format": 1,
|
|
27
|
-
"min_package_version": "0.2.
|
|
28
|
-
"sha256": "
|
|
27
|
+
"min_package_version": "0.2.10",
|
|
28
|
+
"sha256": "23798cb88672c126d71a9d0988b9adf0aa044bdc291847012d76deae71fe1d33"
|
|
29
|
+
},
|
|
30
|
+
"stop_words_sometimes_capitalised.txt": {
|
|
31
|
+
"bytes": 6351,
|
|
32
|
+
"entries": 295,
|
|
33
|
+
"format": 1,
|
|
34
|
+
"min_package_version": "0.2.10",
|
|
35
|
+
"sha256": "d6c842a9eeaaa74d739a9daea2439d2f69f8b97e7e87157caafd685fd11a32c8"
|
|
29
36
|
}
|
|
30
37
|
},
|
|
31
38
|
"manifest_version": 1,
|
|
32
|
-
"written_by": "vicary 0.2.
|
|
39
|
+
"written_by": "vicary 0.2.10"
|
|
33
40
|
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
#!lexicon 1
|
|
2
|
+
#!list stop_words_never_capitalised 499
|
|
3
|
+
# Ordinary words that must never become name candidates, and that English never
|
|
4
|
+
# capitalises in the middle of a sentence.
|
|
5
|
+
#
|
|
6
|
+
# HALF A LIST. The stoplist every front door vetoes candidates against is this
|
|
7
|
+
# file UNION `stop_words_sometimes_capitalised.txt`, and neither half is the
|
|
8
|
+
# stoplist on its own. Loading only one under-vetoes, which makes the redactor
|
|
9
|
+
# more aggressive — the failure that looks privacy-safe and corrupts prose. The
|
|
10
|
+
# `#!list` count on each file is per-file; the union is what code must build.
|
|
11
|
+
#
|
|
12
|
+
# Why there are two files. One consumer asks a second question of these words:
|
|
13
|
+
# `capitalises_ordinary_words` decides a document's capitals are untrustworthy
|
|
14
|
+
# when it finds a mid-sentence capital on a stop word, on the premise that a
|
|
15
|
+
# capital there was never orthographic. That premise is true of the words below
|
|
16
|
+
# and false of the words in the sibling file — "in July" and "my Dad" are
|
|
17
|
+
# correctly capitalised English, and reading them as sloppiness suppressed
|
|
18
|
+
# `Alvarez` in "We stayed with the Alvarez family in July." So the *veto* wants
|
|
19
|
+
# every stop word and the *signal* wants only these, and one list could not
|
|
20
|
+
# serve both.
|
|
21
|
+
#
|
|
22
|
+
# The line between the two files is orthographic, not thematic, because a
|
|
23
|
+
# thematic line is an argument every new word reopens:
|
|
24
|
+
#
|
|
25
|
+
# here function words, verbs, adverbs, interjections and contractions —
|
|
26
|
+
# a capital on one mid-sentence is a mistake, never a construction;
|
|
27
|
+
# sibling nouns, adjectives, numerals, and the proper-noun categories —
|
|
28
|
+
# English capitalises these inside proper names ("Lincoln School",
|
|
29
|
+
# "Great Lakes", "First Baptist"), so a capital is uninformative.
|
|
30
|
+
#
|
|
31
|
+
# Language-neutral on purpose. This list is the only thing standing between
|
|
32
|
+
# candidate generation and "mask every capitalised word", so all three front doors
|
|
33
|
+
# have to work from the same words — a stoplist transliterated by hand into a
|
|
34
|
+
# second language is a second detector wearing the first one's name, and the
|
|
35
|
+
# difference shows up as prose corruption in one language and not the others,
|
|
36
|
+
# which no parity check on masked output would catch.
|
|
37
|
+
#
|
|
38
|
+
# Skewed toward over-inclusion deliberately. A missed name is one span and shows
|
|
39
|
+
# up in the recall number; a wrongly-masked common word corrupts every essay that
|
|
40
|
+
# uses it and shows up nowhere unless somebody reads the prose.
|
|
41
|
+
#
|
|
42
|
+
# Format: `#!` lines are directives, `#` lines are comments, and every other line
|
|
43
|
+
# contributes whitespace-separated words. The count on the `#!list` directive is
|
|
44
|
+
# the number of DISTINCT words after case-folding — authored and generated
|
|
45
|
+
# together, since a reader loads one file — and each front door asserts it
|
|
46
|
+
# against what it parsed. That is not ceremony: a short read here silently makes
|
|
47
|
+
# the redactor MORE aggressive, which looks privacy-safe and passes any check that
|
|
48
|
+
# only asks whether something was masked.
|
|
49
|
+
#
|
|
50
|
+
# A word appearing twice is not an error — the groupings below are thematic and
|
|
51
|
+
# overlap ("else", "may", "us"), and enforcing uniqueness in the source would make
|
|
52
|
+
# the list harder to read for no benefit.
|
|
53
|
+
#
|
|
54
|
+
# Write the SINGULAR only. Plurals are generated into the region at the bottom by
|
|
55
|
+
# `python -m vicary_build lexicon`, which is also what stops one from being added
|
|
56
|
+
# without being priced: the generator drops any form an American bears as a
|
|
57
|
+
# surname, and a hand-written plural is not priced by anything.
|
|
58
|
+
a an the this that these those there here it its it's
|
|
59
|
+
i me my mine myself we us our ours ourselves you your yours
|
|
60
|
+
he him his she her hers they them their theirs who whom whose which what
|
|
61
|
+
and or but so because although though however therefore thus hence yet
|
|
62
|
+
if then else when while until since before after during once whenever
|
|
63
|
+
for from to into onto out off over under above below between among across
|
|
64
|
+
through around about against along beside beyond within without
|
|
65
|
+
at by in on up down near finally
|
|
66
|
+
is am are was were be been being have has had having do does did doing
|
|
67
|
+
can could will would shall should might must let
|
|
68
|
+
not no nor none nothing never always sometimes often usually rarely
|
|
69
|
+
all any both each every few many more most much several some such
|
|
70
|
+
also even just only really very too still again ever else quite rather
|
|
71
|
+
call called come came go went get got give gave take took make made
|
|
72
|
+
see saw look looked think thought know knew say said tell told ask asked
|
|
73
|
+
want wanted need needed try tried help helped work worked feel felt
|
|
74
|
+
find found keep kept leave left put set start started stop stopped
|
|
75
|
+
remember remembered learn learned teach taught write wrote read
|
|
76
|
+
everyone everybody someone somebody anyone anybody nobody everything
|
|
77
|
+
something anything
|
|
78
|
+
yes yeah ok okay maybe perhaps well now today tomorrow yesterday
|
|
79
|
+
im ive ill id dont cant wont didnt isnt aint thats theres whats
|
|
80
|
+
as than instead unless whether either neither plus versus etc
|
|
81
|
+
getting making looking thinking talking playing living walking running
|
|
82
|
+
sitting standing growing learning moving trying using
|
|
83
|
+
back away together alone everywhere somewhere anywhere nowhere
|
|
84
|
+
|
|
85
|
+
# `may`, `march` and `august` are ordinary words too, and they are NOT here:
|
|
86
|
+
# they live in the sibling file, because a mid-sentence `May` is a month far
|
|
87
|
+
# more often than it is a modal. The veto is the union, so candidate generation
|
|
88
|
+
# is unchanged either way; what moves is whether the capital counts as evidence
|
|
89
|
+
# that the writer is sloppy, and on a month it does not.
|
|
90
|
+
|
|
91
|
+
# >>> generated inflections — written by `python -m vicary_build lexicon`
|
|
92
|
+
# Bare plurals of the words above, so a capitalised `Sets` or `Parties` is
|
|
93
|
+
# vetoed by the same lookup its singular is. Hand-written pairs used to do this
|
|
94
|
+
# and only for the 35 somebody thought of.
|
|
95
|
+
#
|
|
96
|
+
# Two subtractions are applied here and neither is optional. A form borne as an
|
|
97
|
+
# American surname is dropped, because a stop word puts every family bearing it
|
|
98
|
+
# beyond the redactor (`may` would claim `Mays`, `will` would claim `Wills`).
|
|
99
|
+
# A form that is a common given name is dropped for a sharper reason: a stop
|
|
100
|
+
# word wins over the given-name tier, so `we` -> `wes` would stop redacting a
|
|
101
|
+
# child called Wes. Plurals still written out by hand above are the ones a
|
|
102
|
+
# subtraction removes from here — they are load-bearing, not leftovers.
|
|
103
|
+
#
|
|
104
|
+
# Regenerate with `python -m vicary_build lexicon`; do not edit below by hand.
|
|
105
|
+
abouts aboves acrosses afters agains againsts aints alones alongs alsos
|
|
106
|
+
althoughs alwayses amongs ands anies ans anybodies anyones anythings
|
|
107
|
+
anywheres arounds ases askeds asks ats aways becauses beens befores beings
|
|
108
|
+
belows bes besides betweens beyonds boths buts calleds calls cames cans
|
|
109
|
+
cants coulds didnts dids doeses doings donts durings eithers elses etcs
|
|
110
|
+
everies everybodies everyones everythings everywheres feels fews finallies
|
|
111
|
+
finds froms gaves gets gives gos gots growings hads hases havings helpeds
|
|
112
|
+
hences hims hises howevers ids ifs ills ins insteads intos ises isnts justs
|
|
113
|
+
keeps kepts knews knows learneds learnings learns leaves lefts lets lookeds
|
|
114
|
+
lookings looks makes maybes mights mosts movings muches musts myselfs
|
|
115
|
+
neededs neithers nobodies nos nothings nots nowheres nows offs oftens okays
|
|
116
|
+
onces onlies ons ontos ourselveses outs perhapses playings pluses puts
|
|
117
|
+
quites rarelies rathers reads reallies remembereds remembers runnings saids
|
|
118
|
+
saws says sets severals shalls shes shoulds sinces sittings somebodies
|
|
119
|
+
someones somethings sometimeses somewheres standings starteds stoppeds
|
|
120
|
+
suches talkings taughts teaches tells thans thems thens therefores thes
|
|
121
|
+
theses thinkings thinks thises thoses thoughs thoughts throughs thuses
|
|
122
|
+
todays togethers tolds tomorrows toos trieds tries tryings unders unlesses
|
|
123
|
+
untils ups uses usings usuallies veries versuses walkings wanteds wants
|
|
124
|
+
wases wents weres whenevers whens whethers whiches whoms whos whoses withins
|
|
125
|
+
withouts wonts workeds woulds writes wrotes yeahs yeses yesterdays yets yous
|
|
126
|
+
# <<< end generated inflections
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
#!lexicon 1
|
|
2
|
+
#!list stop_words_sometimes_capitalised 295
|
|
3
|
+
# Ordinary words that must never become name candidates, but that correct
|
|
4
|
+
# English does capitalise mid-sentence.
|
|
5
|
+
#
|
|
6
|
+
# HALF A LIST. The stoplist every front door vetoes candidates against is this
|
|
7
|
+
# file UNION `stop_words_never_capitalised.txt`, and neither half is the
|
|
8
|
+
# stoplist on its own. Read that file's header for the split and the leak that
|
|
9
|
+
# forced it; in one line: the veto wants every stop word, and the
|
|
10
|
+
# sloppy-capitaliser signal wants only the never-capitalised half, because
|
|
11
|
+
# "in July" and "my Dad" are correct English and told us nothing about the
|
|
12
|
+
# writer.
|
|
13
|
+
#
|
|
14
|
+
# Three kinds of word are here, and the test for all three is the same — is
|
|
15
|
+
# there a correct English sentence with this word capitalised in the middle of
|
|
16
|
+
# it?
|
|
17
|
+
#
|
|
18
|
+
# * **Nouns and adjectives and numerals.** English capitalises them inside
|
|
19
|
+
# proper names: "Lincoln School", "Great Lakes", "First Baptist Church",
|
|
20
|
+
# "New Year's Day". `School` fired the signal on four NWP papers and every
|
|
21
|
+
# one of them was a school's name.
|
|
22
|
+
# * **Months, weekdays and honorifics.** Always capitalised. `July` is what
|
|
23
|
+
# suppressed `Alvarez` in "We stayed with the Alvarez family in July."
|
|
24
|
+
# * **Named categories that identify nobody** — holidays and observances,
|
|
25
|
+
# languages, nationalities and ethnic groups, religions and their scriptures.
|
|
26
|
+
# These are on the stoplist at all because `English` was masked in a 34-word
|
|
27
|
+
# paper by an English-language learner writing about learning English.
|
|
28
|
+
#
|
|
29
|
+
# Two rules govern what may be added to the named-category block, and both are
|
|
30
|
+
# load-bearing.
|
|
31
|
+
#
|
|
32
|
+
# 1. Nothing that `is_common_given_name` already carries. Easter, Valentine,
|
|
33
|
+
# German, Latina, Christian, Jesus and Quran were all proposed and all
|
|
34
|
+
# excluded on that test: a stop word wins over the given-name tier, so
|
|
35
|
+
# listing one of those would stop redacting a real child called Christian.
|
|
36
|
+
# 2. Priced before adding, not after. The 50 category words below claim 138,506
|
|
37
|
+
# American surname bearers between them — 0.072% of the census population,
|
|
38
|
+
# against the 1.25% ceiling the bare-surname exposure gate enforces. French
|
|
39
|
+
# (63,991) and English (46,393) are most of it. Measured 2026-09-10;
|
|
40
|
+
# re-measure before extending, because the cheap-looking additions are the
|
|
41
|
+
# common ones.
|
|
42
|
+
#
|
|
43
|
+
# Scope of that block is deliberately narrow. Public FIGURES, PLACES and WORKS
|
|
44
|
+
# belong in the gazetteer, which already carries `Che Guevara` and `Australia`;
|
|
45
|
+
# settlements are excluded from it on purpose and stay excluded. This list is
|
|
46
|
+
# only for the categories no gazetteer tier models at all.
|
|
47
|
+
#
|
|
48
|
+
# Same format and the same bias as the sibling file: singulars only, plurals
|
|
49
|
+
# generated at the bottom by `python -m vicary_build lexicon`, count asserted on
|
|
50
|
+
# read.
|
|
51
|
+
another other same different new old good bad better best worst
|
|
52
|
+
great big small long short high low young happy sad hard easy
|
|
53
|
+
one two three four five six seven eight nine ten hundred thousand million
|
|
54
|
+
next last first second third
|
|
55
|
+
right wrong true false sure certain important special favorite
|
|
56
|
+
people person thing time day year week month hour minute
|
|
57
|
+
school class teacher student friend family home house mom dad mother father
|
|
58
|
+
parent brother sister grandma grandpa
|
|
59
|
+
life world way place part kind sort lot
|
|
60
|
+
january february march april may june july august september october
|
|
61
|
+
november december monday tuesday wednesday thursday friday saturday sunday
|
|
62
|
+
mr mrs ms dr pm usa u.s tv
|
|
63
|
+
|
|
64
|
+
# Plurals the generated region below cannot carry. Each is also an American
|
|
65
|
+
# surname, so the census veto drops it — and a stop word that quietly stops
|
|
66
|
+
# existing is a recall regression with no diff to read. Written out by hand for
|
|
67
|
+
# that reason, not as a leftover: check
|
|
68
|
+
# `test_the_built_list_still_carries_every_word_it_used_to` before touching one.
|
|
69
|
+
brothers days friends schools times ways weeks years
|
|
70
|
+
|
|
71
|
+
# The named categories. See rules 1 and 2 in the header before adding one.
|
|
72
|
+
christmas halloween thanksgiving hanukkah passover ramadan eid diwali
|
|
73
|
+
kwanzaa valentines
|
|
74
|
+
english spanish french chinese japanese korean italian russian arabic
|
|
75
|
+
hindi portuguese vietnamese greek polish irish scottish
|
|
76
|
+
hispanic latino asian african american european mexican
|
|
77
|
+
canadian indian native jewish muslim catholic protestant buddhist
|
|
78
|
+
hindu nazi
|
|
79
|
+
god bible koran torah allah
|
|
80
|
+
|
|
81
|
+
# >>> generated inflections — written by `python -m vicary_build lexicon`
|
|
82
|
+
# Bare plurals of the words above, so a capitalised `Sets` or `Parties` is
|
|
83
|
+
# vetoed by the same lookup its singular is. Hand-written pairs used to do this
|
|
84
|
+
# and only for the 35 somebody thought of.
|
|
85
|
+
#
|
|
86
|
+
# Two subtractions are applied here and neither is optional. A form borne as an
|
|
87
|
+
# American surname is dropped, because a stop word puts every family bearing it
|
|
88
|
+
# beyond the redactor (`may` would claim `Mays`, `will` would claim `Wills`).
|
|
89
|
+
# A form that is a common given name is dropped for a sharper reason: a stop
|
|
90
|
+
# word wins over the given-name tier, so `we` -> `wes` would stop redacting a
|
|
91
|
+
# child called Wes. Plurals still written out by hand above are the ones a
|
|
92
|
+
# subtraction removes from here — they are load-bearing, not leftovers.
|
|
93
|
+
#
|
|
94
|
+
# Regenerate with `python -m vicary_build lexicon`; do not edit below by hand.
|
|
95
|
+
africans allahs americans anothers aprils arabics asians augusts bads bests
|
|
96
|
+
bigs buddhists canadians catholics certains chineses christmases classes
|
|
97
|
+
dads decembers differents diwalis drs easies eids eights englishes europeans
|
|
98
|
+
falses families fathers favorites februaries firsts fours frenches fridays
|
|
99
|
+
gods grandmas grandpas greats greeks halloweens hanukkahs happies highs
|
|
100
|
+
hindis hindus hispanics hours houses hundreds importants indians irishes
|
|
101
|
+
italians januaries japaneses jewishes julies korans koreans kwanzaas lasts
|
|
102
|
+
latinos lifes lots marches mexicans millions minutes moms mondays months
|
|
103
|
+
mothers mses muslims natives nazis nexts novembers octobers others parents
|
|
104
|
+
parts passovers places pms polishes portugueses protestants ramadans rights
|
|
105
|
+
russians sads saturdays scottishes seconds septembers sevens sisters sixes
|
|
106
|
+
sorts spanishes specials students sundays sures teachers tens thanksgivings
|
|
107
|
+
things thirds thousands threes thursdays torahs trues tuesdays tvs twos usas
|
|
108
|
+
valentineses vietnameses wednesdays worsts wrongs
|
|
109
|
+
# <<< end generated inflections
|
data/lib/vicary/candidates.rb
CHANGED
|
@@ -119,8 +119,30 @@ module Vicary
|
|
|
119
119
|
# load-at-import is that a host may `require "vicary"` to read
|
|
120
120
|
# {Vicary::VERSION} without a vendored asset, and raising there would fail a
|
|
121
121
|
# program that never redacts anything.
|
|
122
|
+
#
|
|
123
|
+
# **The union of two files, and every veto must use the union.** The split is
|
|
124
|
+
# not thematic: `stop_words_never_capitalised` holds the words a mid-sentence
|
|
125
|
+
# capital is a *mistake* on, and `stop_words_sometimes_capitalised` the ones
|
|
126
|
+
# correct English capitalises inside a proper name ("Lincoln School"), in a
|
|
127
|
+
# date ("in July") or as a nationality. Only a document-level *signal* needs
|
|
128
|
+
# that distinction; candidate generation needs all 794 and always did.
|
|
122
129
|
def self.stop_words
|
|
123
|
-
@stop_words ||= Lexicon.
|
|
130
|
+
@stop_words ||= Lexicon.stop_words
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# The half of the stoplist a mid-sentence capital is never orthographic on.
|
|
134
|
+
# Its own list because reading the whole stoplist for that question counts
|
|
135
|
+
# "in July" as evidence that the writer capitalises sloppily, which it is
|
|
136
|
+
# not — see the file header in `asset/lexicon/`.
|
|
137
|
+
def self.never_capitalised
|
|
138
|
+
@never_capitalised ||= Lexicon.load(Lexicon::NEVER_CAPITALISED)
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
# The other half: stop words English does capitalise mid-sentence — months,
|
|
142
|
+
# weekdays, honorifics, nationalities, and the nouns and adjectives that sit
|
|
143
|
+
# inside proper names.
|
|
144
|
+
def self.sometimes_capitalised
|
|
145
|
+
@sometimes_capitalised ||= Lexicon.load(Lexicon::SOMETIMES_CAPITALISED)
|
|
124
146
|
end
|
|
125
147
|
|
|
126
148
|
# Contraction and possessive tails. `[A-Z][A-Za-z'’]*` matches "I'm" as one
|
|
@@ -383,6 +405,14 @@ module Vicary
|
|
|
383
405
|
# document level, and that is what `INCONSISTENT` is for.
|
|
384
406
|
MARKS_PROPER_NOUNS_MIN = 2
|
|
385
407
|
|
|
408
|
+
# How many ordinary words a document must capitalise mid-sentence before its
|
|
409
|
+
# capitals stop counting as testimony. One is enough, and the reason it is
|
|
410
|
+
# not a rate: a word on the never-capitalised half of the stoplist is not a
|
|
411
|
+
# name AND is not a word English capitalises, so a capital on one is neither
|
|
412
|
+
# orthographic nor ambiguous. A floor of one is only defensible while that
|
|
413
|
+
# list stays that clean, which is what the sibling lexicon is for.
|
|
414
|
+
STRAY_CAPITALS_MIN = 1
|
|
415
|
+
|
|
386
416
|
# A sentence opening on a lower-case letter, which is the writer telling us
|
|
387
417
|
# directly that they are not keeping standard capitalisation. Matched at the
|
|
388
418
|
# start of the text as well as after a sentence break.
|
|
@@ -645,9 +675,32 @@ module Vicary
|
|
|
645
675
|
end
|
|
646
676
|
|
|
647
677
|
# Whether `token` is an ordinary word that must never become a candidate.
|
|
678
|
+
#
|
|
679
|
+
# The one place inflection is folded at runtime, and it folds exactly one
|
|
680
|
+
# thing: a trailing clitic, so `Nazi's` is looked up as `nazi`. Plurals are
|
|
681
|
+
# NOT folded here — they are written into the asset by
|
|
682
|
+
# `python -m vicary_build lexicon`, which drops any form an American bears
|
|
683
|
+
# as a surname before emitting it, so all three front doors inherit the
|
|
684
|
+
# behaviour from the same bytes instead of implementing it three times.
|
|
648
685
|
def stop?(token)
|
|
649
|
-
|
|
650
|
-
|
|
686
|
+
stop_words.include?(fold(token))
|
|
687
|
+
end
|
|
688
|
+
|
|
689
|
+
# `token` as the stoplists are keyed: lower-cased, clitic and edge
|
|
690
|
+
# punctuation off. Extracted so the two lookups cannot fold differently.
|
|
691
|
+
def fold(token)
|
|
692
|
+
strip(without_clitic(strip(token.downcase, ".,")), "'’")
|
|
693
|
+
end
|
|
694
|
+
|
|
695
|
+
# Whether a capital on `token` mid-sentence is a mistake rather than a
|
|
696
|
+
# construction — the narrow half of the stoplist.
|
|
697
|
+
#
|
|
698
|
+
# The question {.capitalises_ordinary_words?} needs and {.stop?} does not
|
|
699
|
+
# answer: every word here is a stop word, but a stop word may be a month, a
|
|
700
|
+
# nationality or the noun in "Lincoln School", and English capitalises all
|
|
701
|
+
# three correctly.
|
|
702
|
+
def never_capitalised?(token)
|
|
703
|
+
never_capitalised.include?(fold(token))
|
|
651
704
|
end
|
|
652
705
|
|
|
653
706
|
# Words split on whitespace, the way Python's bare `str.split()` does.
|
|
@@ -1095,6 +1148,82 @@ module Vicary
|
|
|
1095
1148
|
!corroborated?(tokens, written_as_a_capital, is_given)
|
|
1096
1149
|
end
|
|
1097
1150
|
|
|
1151
|
+
# Whether this document capitalises words that cannot be names.
|
|
1152
|
+
#
|
|
1153
|
+
# Counts mid-sentence capitals landing on the **never-capitalised** half of
|
|
1154
|
+
# the stoplist, which is a narrower question than {.stop?} answers and the
|
|
1155
|
+
# reason that list is a file of its own.
|
|
1156
|
+
#
|
|
1157
|
+
# It used to read the whole stoplist, and that was wrong in a way no
|
|
1158
|
+
# threshold fixes. The stoplist carries months, weekdays, honorifics,
|
|
1159
|
+
# nationalities, religions and ordinary nouns — `English` is on it because
|
|
1160
|
+
# a 34-word paper by an English-language learner had the word masked — and
|
|
1161
|
+
# every one of those is *correctly* capitalised. A document writing "in
|
|
1162
|
+
# July" tripped this having told us nothing, which suppressed `Alvarez` in
|
|
1163
|
+
# "We stayed with the Alvarez family in July."
|
|
1164
|
+
#
|
|
1165
|
+
# Unlike `marks_proper_nouns?`, which counts every mid-sentence capital and
|
|
1166
|
+
# therefore counts the names too, this cannot be satisfied by a document
|
|
1167
|
+
# that simply names a lot of people. Headings are excluded because title
|
|
1168
|
+
# case capitalises every word in one.
|
|
1169
|
+
def capitalises_ordinary_words?(text, headings = [])
|
|
1170
|
+
count = 0
|
|
1171
|
+
each_match(text, MID_SENTENCE_CAP) do |m|
|
|
1172
|
+
word = m[1]
|
|
1173
|
+
next unless never_capitalised?(word)
|
|
1174
|
+
|
|
1175
|
+
start = m.begin(1)
|
|
1176
|
+
finish = start + word.length
|
|
1177
|
+
next if headings.any? { |h_start, h_end| start < h_end && finish > h_start }
|
|
1178
|
+
|
|
1179
|
+
count += 1
|
|
1180
|
+
end
|
|
1181
|
+
count >= STRAY_CAPITALS_MIN
|
|
1182
|
+
end
|
|
1183
|
+
|
|
1184
|
+
# The mid-sentence guard: drop a lone capital a sloppy capitaliser chose.
|
|
1185
|
+
#
|
|
1186
|
+
# The mirror of {.suppressed_as_an_unevidenced_capital?}, and it exists
|
|
1187
|
+
# because that rule guards the smaller hole. A sentence-initial capital is
|
|
1188
|
+
# orthographically required, so it proves nothing — that is the rule
|
|
1189
|
+
# already there, and on the NWP corpus it is **4 of 66** false-positive
|
|
1190
|
+
# spans. A *mid-sentence* capital was taken as sufficient evidence on its
|
|
1191
|
+
# own, and that is **41 of 66**.
|
|
1192
|
+
#
|
|
1193
|
+
# So a mid-sentence capital stops being self-sufficient, but **only in a
|
|
1194
|
+
# document that has shown its capitals are unreliable** — see
|
|
1195
|
+
# {.capitalises_ordinary_words?}. In every other document nothing changes.
|
|
1196
|
+
#
|
|
1197
|
+
# Two channels can still keep the span, and the document's own
|
|
1198
|
+
# capitalisation is deliberately not one of them: `written_as_a_capital`
|
|
1199
|
+
# *is* the mid-sentence capital, so consulting it here would be the span
|
|
1200
|
+
# vouching for itself. What is left is evidence from outside the document —
|
|
1201
|
+
# the given-name tier, and a first-person relation in the local context
|
|
1202
|
+
# ("my friend Cade"), which is testimony that the token names a person
|
|
1203
|
+
# whatever its case.
|
|
1204
|
+
#
|
|
1205
|
+
# **The measured weakness, which is an equity exposure and not just a
|
|
1206
|
+
# coverage one.** Of the true catches this leaves intact on the NWP corpus,
|
|
1207
|
+
# three — `Amy`, `Barry`, `Whitney` — survive on the given-name tier alone.
|
|
1208
|
+
# Given-name coverage is systematically thinner for less common and
|
|
1209
|
+
# non-Anglo names, so the population this rule is most likely to leak is
|
|
1210
|
+
# not a random sample of children.
|
|
1211
|
+
def suppressed_as_a_stray_mid_sentence_capital?(tokens, start, finish, text, starts,
|
|
1212
|
+
is_given)
|
|
1213
|
+
return false unless tokens.length == 1
|
|
1214
|
+
# A sentence-initial capital belongs to the other rule, which already
|
|
1215
|
+
# consults a channel this one must not.
|
|
1216
|
+
return false if starts.any? { |s| s <= start && start <= s + 2 }
|
|
1217
|
+
|
|
1218
|
+
stripped = strip(tokens[0].downcase, ".,'’")
|
|
1219
|
+
return false if is_given.call(stripped)
|
|
1220
|
+
|
|
1221
|
+
folded = without_clitic(stripped)
|
|
1222
|
+
return false if folded != stripped && is_given.call(folded)
|
|
1223
|
+
|
|
1224
|
+
!names_someone_in_the_writers_life?(text, start, finish)
|
|
1225
|
+
end
|
|
1226
|
+
|
|
1098
1227
|
# ---------------------------------------------------------------------
|
|
1099
1228
|
# Titles, and the relations that override them
|
|
1100
1229
|
# ---------------------------------------------------------------------
|
|
@@ -1576,6 +1705,7 @@ module Vicary
|
|
|
1576
1705
|
settlement = options[:settlement]
|
|
1577
1706
|
headings_are_orthographic = options.fetch(:headings_are_orthographic, true)
|
|
1578
1707
|
title_relation_refusal = options.fetch(:title_relation_refusal, true)
|
|
1708
|
+
mid_sentence_corroboration = options.fetch(:mid_sentence_corroboration, true)
|
|
1579
1709
|
|
|
1580
1710
|
blocked = each_match(text, PROTECTED).map { |m| [m.begin(0), m.begin(0) + m[0].length] }
|
|
1581
1711
|
starts = sentence_starts(text)
|
|
@@ -1610,6 +1740,10 @@ module Vicary
|
|
|
1610
1740
|
end
|
|
1611
1741
|
|
|
1612
1742
|
written_as_a_capital = mid_sentence_capitals(text, starts, headings)
|
|
1743
|
+
# A property of the whole document, read once, for the same reason
|
|
1744
|
+
# `habit` is: two call sites computing it separately could disagree.
|
|
1745
|
+
stray_capitals = mid_sentence_corroboration && !given_name.nil? &&
|
|
1746
|
+
capitalises_ordinary_words?(text, headings)
|
|
1613
1747
|
|
|
1614
1748
|
out = []
|
|
1615
1749
|
each_match(text, CANDIDATE_RE) do |m|
|
|
@@ -1638,6 +1772,15 @@ module Vicary
|
|
|
1638
1772
|
next
|
|
1639
1773
|
end
|
|
1640
1774
|
|
|
1775
|
+
# ...and the other half of the same question, for the capital the
|
|
1776
|
+
# rule above reads as evidence. Only in a document that has shown its
|
|
1777
|
+
# capitals are worth less than that.
|
|
1778
|
+
if stray_capitals &&
|
|
1779
|
+
suppressed_as_a_stray_mid_sentence_capital?(run, start, start + joined.length,
|
|
1780
|
+
text, starts, given_name)
|
|
1781
|
+
next
|
|
1782
|
+
end
|
|
1783
|
+
|
|
1641
1784
|
# A *trailing* apostrophe is the closing quote, not part of the name.
|
|
1642
1785
|
# The candidate pattern treats `'` as a name character so O'Brien
|
|
1643
1786
|
# survives, which also means "words like 'Terrence'" arrives as
|
data/lib/vicary/corpus.rb
CHANGED
|
@@ -104,6 +104,10 @@ module Vicary
|
|
|
104
104
|
|
|
105
105
|
Metrics = Struct.new(
|
|
106
106
|
:essays, :recall_held_out, :recall_held_out_passed, :recall_held_out_total,
|
|
107
|
+
# Every REDACT span, not only the held-out ones. Held-out recall is blind
|
|
108
|
+
# to a span the detector was SHOWN, and a suppression rule that drops one
|
|
109
|
+
# leaves held-out recall at 100%.
|
|
110
|
+
:recall_all, :recall_all_passed, :recall_all_total,
|
|
107
111
|
:over_fire_spans_per_essay, :over_fire_spans_total,
|
|
108
112
|
:asap_rewrites_per_essay, :latency_p50_ms, :latency_p95_ms,
|
|
109
113
|
:latency_pooled_median_ms,
|
|
@@ -485,6 +489,8 @@ module Vicary
|
|
|
485
489
|
|
|
486
490
|
held_out = outcomes.select { |o| o.held_out && o.verdict != "keep" }
|
|
487
491
|
passed = held_out.count(&:passed)
|
|
492
|
+
all_redact = outcomes.reject { |o| o.verdict == "keep" }
|
|
493
|
+
all_passed = all_redact.count(&:passed)
|
|
488
494
|
sorted = latencies.sort
|
|
489
495
|
at = lambda do |q|
|
|
490
496
|
next 0.0 if sorted.empty?
|
|
@@ -497,6 +503,9 @@ module Vicary
|
|
|
497
503
|
recall_held_out: held_out.empty? ? 0.0 : 100.0 * passed / held_out.size,
|
|
498
504
|
recall_held_out_passed: passed,
|
|
499
505
|
recall_held_out_total: held_out.size,
|
|
506
|
+
recall_all: all_redact.empty? ? 0.0 : 100.0 * all_passed / all_redact.size,
|
|
507
|
+
recall_all_passed: all_passed,
|
|
508
|
+
recall_all_total: all_redact.size,
|
|
500
509
|
over_fire_spans_per_essay: cases.empty? ? 0.0 : over_fire.to_f / cases.size,
|
|
501
510
|
over_fire_spans_total: over_fire,
|
|
502
511
|
asap_rewrites_per_essay: cases.empty? ? 0.0 : rewrites.to_f / cases.size,
|
data/lib/vicary/gates.rb
CHANGED
|
@@ -294,7 +294,8 @@ module Vicary
|
|
|
294
294
|
# a caller that wants those gates supplies the number, and one that does
|
|
295
295
|
# not gets NOT MEASURED rather than a load.
|
|
296
296
|
def measure(spec, gate_spec, asset_entries: nil, bare_surname_exposure: nil,
|
|
297
|
-
held_out_recall_carrier: nil,
|
|
297
|
+
held_out_recall_carrier: nil, all_span_recall_carrier: nil,
|
|
298
|
+
over_fire_per_essay: nil,
|
|
298
299
|
latency_regression_pct: nil, latency_regression_detail: nil,
|
|
299
300
|
corpus_id: nil)
|
|
300
301
|
outcomes = []
|
|
@@ -365,6 +366,14 @@ module Vicary
|
|
|
365
366
|
"#{round3(held_out_recall_carrier)}% of held-out REDACT spans in carrier essays"
|
|
366
367
|
end,
|
|
367
368
|
},
|
|
369
|
+
"all_span_recall_carrier" => {
|
|
370
|
+
value: all_span_recall_carrier,
|
|
371
|
+
detail: if all_span_recall_carrier.nil?
|
|
372
|
+
no_corpus
|
|
373
|
+
else
|
|
374
|
+
"#{round3(all_span_recall_carrier)}% of ALL REDACT spans in carrier essays"
|
|
375
|
+
end,
|
|
376
|
+
},
|
|
368
377
|
"over_fire_prose" => {
|
|
369
378
|
value: over_fire_per_essay,
|
|
370
379
|
detail: if over_fire_per_essay.nil?
|
data/lib/vicary/lexicon.rb
CHANGED
|
@@ -46,6 +46,25 @@ module Vicary
|
|
|
46
46
|
# Filename suffix. Named so a second list costs a file rather than a refactor.
|
|
47
47
|
SUFFIX = ".txt"
|
|
48
48
|
|
|
49
|
+
# The two files whose union is the stoplist. Named here rather than at each
|
|
50
|
+
# call site because the *veto* needs both and exactly one consumer needs one
|
|
51
|
+
# — a document-level signal that reads a mid-sentence capital as testimony
|
|
52
|
+
# about the writer, and that must not read "in July" that way. A call site
|
|
53
|
+
# free to load one half is free to narrow the veto by accident, which makes
|
|
54
|
+
# the redactor more aggressive: privacy-safe to look at, prose-corrupting in
|
|
55
|
+
# fact, and invisible to any check that only asks whether something was
|
|
56
|
+
# masked.
|
|
57
|
+
NEVER_CAPITALISED = "stop_words_never_capitalised"
|
|
58
|
+
SOMETIMES_CAPITALISED = "stop_words_sometimes_capitalised"
|
|
59
|
+
STOP_WORD_LISTS = [NEVER_CAPITALISED, SOMETIMES_CAPITALISED].freeze
|
|
60
|
+
|
|
61
|
+
# Every word that must never become a name candidate: both halves, unioned.
|
|
62
|
+
# Word for word the list that shipped as a single `stop_words.txt` through
|
|
63
|
+
# 0.2.9.
|
|
64
|
+
def self.stop_words
|
|
65
|
+
STOP_WORD_LISTS.map { |name| load(name) }.reduce(:|)
|
|
66
|
+
end
|
|
67
|
+
|
|
49
68
|
# A lexicon is absent, unreadable, or not the shape this reader understands.
|
|
50
69
|
#
|
|
51
70
|
# Its own class rather than a bare RuntimeError for the same reason
|
data/lib/vicary/version.rb
CHANGED
|
@@ -6,5 +6,5 @@ module Vicary
|
|
|
6
6
|
# Shared across all three front doors on purpose: one detector, one number. A
|
|
7
7
|
# gem 0.3.0 that corresponds to nothing on PyPI cannot be reasoned about, and
|
|
8
8
|
# the parity claim is between *versions*, not between package names.
|
|
9
|
-
VERSION = "0.2.
|
|
9
|
+
VERSION = "0.2.11"
|
|
10
10
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: vicary
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.2.
|
|
4
|
+
version: 0.2.11
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Blake Thomas
|
|
@@ -25,7 +25,8 @@ files:
|
|
|
25
25
|
- README.md
|
|
26
26
|
- assets/MANIFEST.json
|
|
27
27
|
- assets/notability.txt.gz
|
|
28
|
-
- assets/
|
|
28
|
+
- assets/stop_words_never_capitalised.txt
|
|
29
|
+
- assets/stop_words_sometimes_capitalised.txt
|
|
29
30
|
- lib/vicary.rb
|
|
30
31
|
- lib/vicary/asset.rb
|
|
31
32
|
- lib/vicary/candidates.rb
|
data/assets/stop_words.txt
DELETED
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
#!lexicon 1
|
|
2
|
-
#!list stop_words 421
|
|
3
|
-
# Ordinary words that must never become name candidates.
|
|
4
|
-
#
|
|
5
|
-
# Language-neutral on purpose. This list is the only thing standing between
|
|
6
|
-
# candidate generation and "mask every capitalised word", so all three front doors
|
|
7
|
-
# have to work from the same 421 words — a stoplist transliterated by hand into a
|
|
8
|
-
# second language is a second detector wearing the first one's name, and the
|
|
9
|
-
# difference shows up as prose corruption in one language and not the others,
|
|
10
|
-
# which no parity check on masked output would catch.
|
|
11
|
-
#
|
|
12
|
-
# Skewed toward over-inclusion deliberately. A missed name is one span and shows
|
|
13
|
-
# up in the recall number; a wrongly-masked common word corrupts every essay that
|
|
14
|
-
# uses it and shows up nowhere unless somebody reads the prose.
|
|
15
|
-
#
|
|
16
|
-
# Format: `#!` lines are directives, `#` lines are comments, and every other line
|
|
17
|
-
# contributes whitespace-separated words. The count on the `#!list` directive is
|
|
18
|
-
# the number of DISTINCT words after case-folding, and each front door asserts it
|
|
19
|
-
# against what it parsed. That is not ceremony: a short read here silently makes
|
|
20
|
-
# the redactor MORE aggressive, which looks privacy-safe and passes any check that
|
|
21
|
-
# only asks whether something was masked.
|
|
22
|
-
#
|
|
23
|
-
# A word appearing twice is not an error — the groupings below are thematic and
|
|
24
|
-
# overlap ("else", "may", "us"), and enforcing uniqueness in the source would make
|
|
25
|
-
# the list harder to read for no benefit.
|
|
26
|
-
a an the this that these those there here it its it's
|
|
27
|
-
i me my mine myself we us our ours ourselves you your yours
|
|
28
|
-
he him his she her hers they them their theirs who whom whose which what
|
|
29
|
-
and or but so because although though however therefore thus hence yet
|
|
30
|
-
if then else when while until since before after during once whenever
|
|
31
|
-
for from to into onto out off over under above below between among across
|
|
32
|
-
through around about against along beside besides beyond within without
|
|
33
|
-
at by in on up down near next last first second third finally
|
|
34
|
-
is am are was were be been being have has had having do does did doing
|
|
35
|
-
can could will would shall should may might must let lets
|
|
36
|
-
not no nor none nothing never always sometimes often usually rarely
|
|
37
|
-
all any both each every few many more most much several some such
|
|
38
|
-
another other others same different new old good bad better best worst
|
|
39
|
-
great big small long short high low young happy sad hard easy
|
|
40
|
-
one two three four five six seven eight nine ten hundred thousand million
|
|
41
|
-
also even just only really very too still again ever else quite rather
|
|
42
|
-
call called come came go went get got give gave take took make made
|
|
43
|
-
see saw look looked think thought know knew say said tell told ask asked
|
|
44
|
-
want wanted need needed try tried help helped work worked feel felt
|
|
45
|
-
find found keep kept leave left put set start started stop stopped
|
|
46
|
-
remember remembered learn learned teach taught write wrote read
|
|
47
|
-
everyone everybody someone somebody anyone anybody nobody everything
|
|
48
|
-
something anything people person thing things time times day days
|
|
49
|
-
year years week weeks month months hour hours minute minutes
|
|
50
|
-
school schools class classes teacher teachers student students friend
|
|
51
|
-
friends family families home house mom dad mother father parent parents
|
|
52
|
-
brother sister sisters brothers grandma grandpa
|
|
53
|
-
life world way ways place places part parts kind sort lot lots
|
|
54
|
-
yes yeah ok okay maybe perhaps well now today tomorrow yesterday
|
|
55
|
-
january february march april may june july august september october
|
|
56
|
-
november december monday tuesday wednesday thursday friday saturday sunday
|
|
57
|
-
mr mrs ms dr am pm usa us u.s tv
|
|
58
|
-
im ive ill id dont cant wont didnt isnt aint thats theres whats
|
|
59
|
-
as than instead unless whether either neither plus versus etc
|
|
60
|
-
getting making looking thinking talking playing living walking running
|
|
61
|
-
sitting standing growing learning moving trying using
|
|
62
|
-
back away together alone everywhere somewhere anywhere nowhere
|
|
63
|
-
right wrong true false sure certain important special favorite
|