Skip to content

Commit ff1dc21

Browse files
New nrow_subset_linter (#2298)
* New nrow_subset_linter * examples * remove commented code * documentation feedback * remove 'readability' tag * more tests
1 parent c96d024 commit ff1dc21

11 files changed

+118
-3
lines changed

DESCRIPTION

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ Collate:
139139
'namespace_linter.R'
140140
'nested_ifelse_linter.R'
141141
'nonportable_path_linter.R'
142+
'nrow_subset_linter.R'
142143
'numeric_leading_zero_linter.R'
143144
'nzchar_linter.R'
144145
'object_length_linter.R'

NAMESPACE

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ export(namespace_linter)
101101
export(nested_ifelse_linter)
102102
export(no_tab_linter)
103103
export(nonportable_path_linter)
104+
export(nrow_subset_linter)
104105
export(numeric_leading_zero_linter)
105106
export(nzchar_linter)
106107
export(object_length_linter)

NEWS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
* `which_grepl_linter()` for discouraging `which(grepl(ptn, x))` in favor of directly using `grep(ptn, x)` (part of #884, @MichaelChirico).
3232
* `list_comparison_linter()` for discouraging comparisons on the output of `lapply()`, e.g. `lapply(x, sum) > 10` (part of #884, @MichaelChirico).
3333
* `print_linter()` for discouraging usage of `print()` on string literals like `print("Reached here")` or `print(paste("Found", nrow(DF), "rows."))` (#1894, @MichaelChirico).
34+
* `nrow_subset_linter()` for discouraging usage like `nrow(subset(x, conditions))` in favor of something like `with(x, sum(conditions))` which doesn't require a full subset of `x` (part of #884, @MichaelChirico).
3435
* `pipe_return_linter()` for discouraging usage of `return()` inside a {magrittr} pipeline (part of #884, @MichaelChirico).
3536

3637
### Lint accuracy fixes: removing false positives

R/nrow_subset_linter.R

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
#' Block usage of `nrow(subset(x, .))`
2+
#'
3+
#' Using `nrow(subset(x, condition))` to count the instances where `condition`
4+
#' applies inefficiently requires doing a full subset of `x` just to
5+
#' count the number of rows in the resulting subset.
6+
#' There are a number of equivalent expressions that don't require the full
7+
#' subset, e.g. `with(x, sum(condition))` (or, more generically,
8+
#' `with(x, sum(condition, na.rm = TRUE))`).
9+
#'
10+
#' @examples
11+
#' # will produce lints
12+
#' lint(
13+
#' text = "nrow(subset(x, is_treatment))",
14+
#' linters = nrow_subset_linter()
15+
#' )
16+
#'
17+
#' # okay
18+
#' lint(
19+
#' text = "with(x, sum(is_treatment, na.rm = TRUE))",
20+
#' linters = nrow_subset_linter()
21+
#' )
22+
#'
23+
#' @evalRd rd_tags("nrow_subset_linter")
24+
#' @seealso [linters] for a complete list of linters available in lintr.
25+
#' @export
26+
nrow_subset_linter <- make_linter_from_xpath(
27+
xpath = "
28+
//SYMBOL_FUNCTION_CALL[text() = 'subset']
29+
/parent::expr
30+
/parent::expr
31+
/parent::expr[expr/SYMBOL_FUNCTION_CALL[text() = 'nrow']]
32+
",
33+
lint_message = paste(
34+
"Use arithmetic to count the number of rows satisfying a condition,",
35+
"rather than fully subsetting the data.frame and counting the resulting rows.",
36+
"For example, replace nrow(subset(x, is_treatment))",
37+
"with sum(x$is_treatment). NB: use na.rm = TRUE if `is_treatment` has",
38+
"missing values."
39+
)
40+
)

inst/lintr/linters.csv

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ namespace_linter,correctness robustness configurable executing
5858
nested_ifelse_linter,efficiency readability
5959
no_tab_linter,style consistency deprecated
6060
nonportable_path_linter,robustness best_practices configurable
61+
nrow_subset_linter,efficiency consistency best_practices
6162
numeric_leading_zero_linter,style consistency readability
6263
nzchar_linter,efficiency best_practices consistency
6364
object_length_linter,style readability default configurable executing

man/best_practices_linters.Rd

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

man/consistency_linters.Rd

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

man/efficiency_linters.Rd

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

man/linters.Rd

Lines changed: 4 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

man/nrow_subset_linter.Rd

Lines changed: 36 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
test_that("nrow_subset_linter skips allowed usage", {
2+
linter <- nrow_subset_linter()
3+
4+
expect_lint("nrow(foo(subset(x, y == z)))", NULL, linter)
5+
expect_lint("with(x, sum(y == z))", NULL, linter)
6+
})
7+
8+
test_that("nrow_subset_linter blocks subset() cases", {
9+
expect_lint(
10+
"nrow(subset(x, y == z))",
11+
rex::rex("Use arithmetic to count the number of rows satisfying a condition"),
12+
nrow_subset_linter()
13+
)
14+
})
15+
16+
test_that("lints vectorize", {
17+
lint_msg <- rex::rex("Use arithmetic to count the number of rows satisfying a condition")
18+
19+
expect_lint(
20+
trim_some("{
21+
nrow(subset(x, y == z))
22+
subset(x) %>% transform(m = 2)
23+
nrow(subset(a, b == c))
24+
}"),
25+
list(
26+
list(lint_msg, line_number = 2L),
27+
list(lint_msg, line_number = 4L)
28+
),
29+
nrow_subset_linter()
30+
)
31+
})

0 commit comments

Comments
 (0)