Skip to content

Dev tokens#887

Merged
kbenoit merged 34 commits into
masterfrom
dev-tokens
Jul 28, 2017
Merged

Dev tokens#887
kbenoit merged 34 commits into
masterfrom
dev-tokens

Conversation

@koheiw

@koheiw koheiw commented Jul 27, 2017

Copy link
Copy Markdown
Collaborator

Adds tokens.tokens(). Also gives much greater control of types of tokens to be preserved, while increasing efficiency.

@codecov

codecov Bot commented Jul 27, 2017

Copy link
Copy Markdown

Codecov Report

Merging #887 into master will decrease coverage by 0.03%.
The diff coverage is 84.9%.

@@            Coverage Diff             @@
##           master     #887      +/-   ##
==========================================
- Coverage   77.72%   77.68%   -0.04%     
==========================================
  Files         106      107       +1     
  Lines        7976     8142     +166     
==========================================
+ Hits         6199     6325     +126     
- Misses       1777     1817      +40

@kbenoit kbenoit left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good.

Most of my comments are for consideration, not for changes, but do take a look at the remove_hyphens behaviour.

For the next version, let's make it a priority to work on the tokenizers package and see how we can go about using those tokenisers instead (for segmenting, not hashing), and also how we could write a "selection-based" (rather than segmentation-based) tokenizer around a spacyr tokenization.

# didn't use accessor documents() because didn't want to pass
# that large object
rownames(x$documents)
if (is.null(rownames(x$documents))) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems slightly dangerous, since it means we could have a corpus object with no document names, but that docnames() will make it seem like it does have document names. Have you tested all of the functions that rely on there being actual document names? If they all call docnames.corpus() then we should be OK, since this will create the names.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Documents should have names always, but really old corpus object might not. This is a safeguard for errors in subsequent processes. If we were to give up backward compatibility, better to raise errors here.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK

Comment thread tests/testthat/test-dfm.R
thesDfm[1:10, (nfeature(thesDfm)-8) : nfeature(thesDfm)]
dictDfm <- dictDfm <- dfm(mycorpus, dictionary = mydict, valuetype = "glob")
dictDfm <- dictDfm[1:10, ]
dictDfm <- thesDfm <- dfm(mycorpus, thesaurus = mydict, valuetype = "glob")

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure what the point of these assignments are, since they keep overwriting dictDfm. Shouldn't there be some expect_*() calls around these to make them tests?

@koheiw koheiw Jul 28, 2017

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are the oldest test for dfm that were not isolated. I do not see why they are still there either. I assigned output values to variables only because I do not want to set dfm to be printed to my console.

Comment thread tests/testthat/test-dfm.R
dfm(txt, thesaurus = mydict, verbose = FALSE)
dfm(txt, thesaurus = mydict, verbose = FALSE)
dictDfm <- dfm(txt, dictionary = mydict, verbose = FALSE)
dictDfm <- dfm(txt, thesaurus = mydict, verbose = FALSE)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Likewise, not sure what the point of these are, esp since one line is repeated.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should delete all the code in test_that("oldest dfm test", {}).

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll take a look at that.

Comment thread tests/testthat/test-dfm.R
dfm_lookup(txtDfm, mydict, valuetype = "glob")
dfm_lookup(txtDfm, mydict, exclusive = FALSE, valuetype = "glob", verbose = FALSE)
txtDfm <- dfm(txt, verbose = FALSE)
dictDfm <- dfm_lookup(txtDfm, mydict, valuetype = "glob")

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto re above

Comment thread tests/testthat/test-dfm.R
dfm(txt, stem = TRUE)
dfm(txt, remove = stopwords("english"))
dfm(txt, stem = TRUE, remove = stopwords("english"))
txtDfm <- dfm(txt)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here too - no tests here

Comment thread R/tokens.R Outdated
#' @export
#' @noRd
tokens.corpus <- function(x, ..., include_docvars = TRUE) {
tokens.corpus <- function(x, ..., hash = TRUE, include_docvars = TRUE) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting - I thought we would get errors in the CHECK if not all the signatures are the same.

Comment thread R/tokens.R
regex = c(regex, "^[\\p{S}]+$")
if (remove_separators)
regex = c(regex, "^[\uFE00-\uFE0F\\p{Z}\\p{C}]+$")
if (remove_url)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

with the exception of the remove_url, the others are char_class and might be faster implemented that way (through stringi::stri_remove_charclass() - although I realise that tokens_remove() does not currently provide this option. Something to note for the future? Or, we could examine the regex in tokens_remove() and if it matches a charclass, we call that instead of the equivalent regex function. We should test the performance differences before investing time in this.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think there will be a much performance gain, but tokens_remove(toks, 'P', 'charclass') is certainly much easier than tokens_remove(toks, "^[\\p{P}]$", 'regex').

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking we would stick to the stringi syntax, not create an interpretative layer in between. So it would be similar to

> stri_detect_charclass("Unicode symbol £", "[\\p{S}\\p{P}]")
[1] TRUE
> stri_detect_charclass("Unicode puncutation !", "[\\p{S}\\p{P}]")
[1] TRUE

Comment thread R/tokens.R Outdated
#' @param concatenator character between multi-word expressions, default is the underscore character. See Details.
#' @return \code{as.tokens} returns a quanteda \link{tokens} object.
#' @details The \code{concatenator} value is (TO BE COMPLETED).
#' @details The \code{concatenator} is used to automatically generate dictionary values for multi-word expressions

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please provide a bit more detail, and an (prose) example.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Comment thread R/tokens.R
"skip" = 0L,
"concatenator" = concatenator,
"padding" = FALSE)
docvars(result) <- data.frame(row.names = docnames(result))

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't all this be easier if we had an S4 class for this with a prototype to set these slots?

Comment thread R/tokens.R
for (i in seq_along(x)) {

if (verbose) catm("...tokenizing", i, "of" , length(x), "blocks\n")
if (what %in% c("word", "fastestword", "fasterword")) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This look might be a natural thing to parallelise (in R).

@koheiw

koheiw commented Jul 28, 2017

Copy link
Copy Markdown
Collaborator Author

Since I separated special handling of hyphens and Twitter tags etc, tokens_word() became almost nothing. We can use tokenizers anytime.

@kbenoit

kbenoit commented Jul 28, 2017

Copy link
Copy Markdown
Collaborator

Some dfm print tests are failing because the tokenisation behaviour has changed. Before we just rewrite the test expectations, should we investigate the differences so we are sure we understand them?

@koheiw

koheiw commented Jul 28, 2017

Copy link
Copy Markdown
Collaborator Author

I added old argument to tokens to compare with the version. The difference is from hyphenated words:

old <- unlist(tokens(data_char_ukimmig2010, old = TRUE), use.names = FALSE)
new <- unlist(tokens(data_char_ukimmig2010, old = FALSE), use.names = FALSE)
new2 <- unlist(tokens(data_char_ukimmig2010, remove_hyphen = TRUE, old = FALSE), use.names = FALSE)

setdiff(old, new)
setdiff(new, old)

setdiff(old, new2)
setdiff(new2, old)

> setdiff(old, new)
[1] "abiding"        "non"            "ill"            "educated"       "knee"           "jerk"           "300,000"       
[8] "represented"    "term"           "re"             "building"       "soft"           "touch"          "breakers"      
[15] "Abiding"        "sponsored"      "ethnicity"      "28"             "decision"       "creating"       "cross"         
[22] "boundary"       "E"              "longer"         "discriminatory" "quality"        "No"             "traffickers"   
[29] "co"             "ordinated"      "wide"           "point"          "scoring"        "recently"       "skill"         
> setdiff(new, old)
[1] "law-abiding"           "decades-long"          "non-British"           "ill-educated"          "knee-jerk"            
[6] "300,000-500,000"       "over-represented"      "Non-Immigration"       "non-immigration"       "long-term"            
[11] "re-building"           "second-generation"     "soft-touch"            "law-breakers"          "Law-Abiding"          
[16] "re-entry"              "government-sponsored"  "ethnicity-specific"    "28-style"              "second-class"         
[21] "decision-making"       "nation-building"       "civilisation-creating" "non-EU"                "cross-boundary"       
[26] "E-borders"             "longer-term"           "non-discriminatory"    "high-quality"          "Australian-style"     
[31] "points-based"          "police-level"          "public-sector"         "No-one"                "people-traffickers"   
[36] "high-priority"         "co-ordinated"          "EU-wide"               "point-scoring"         "recently-arrived"     
[41] "high-skill"            "five-year"             "time-limited"          "non-work"              "face-to-face"         
> setdiff(old, new2)
character(0)
> setdiff(new2, old)
character(0)

@kbenoit

kbenoit commented Jul 28, 2017

Copy link
Copy Markdown
Collaborator

OK, nice. So it looks like most of the difference is coming from hyphens being preserved (correctly) in the new code, while being split (incorrectly) in the old code.

@koheiw

koheiw commented Jul 28, 2017

Copy link
Copy Markdown
Collaborator Author

It seems to be the case in Japanese texts too.

@koheiw

koheiw commented Jul 28, 2017

Copy link
Copy Markdown
Collaborator Author

This is just a confirmation:

microbenchmark::microbenchmark(
    old = tokens(corp, old = TRUE),
    new = tokens(corp, remove_hyphen = TRUE, old = FALSE),
    times = 10
)


Unit: seconds
 expr       min       lq     mean   median       uq      max neval
  old 14.301439 14.73900 15.08327 15.02283 15.65775 15.80192    10
  new  9.725151 10.22395 10.31369 10.41880 10.43882 10.86264    10

@kbenoit
kbenoit merged commit c4dc679 into master Jul 28, 2017
@kbenoit
kbenoit deleted the dev-tokens branch July 28, 2017 17:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants