Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

211 [.teal_data S3 method #346

Merged
merged 40 commits into from
Nov 8, 2024
Merged
Show file tree
Hide file tree
Changes from 33 commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
967c1c5
`[.teal_data` S3 method
m7pr Oct 28, 2024
362b982
provide prefix for utils::
m7pr Oct 28, 2024
a2610a3
fixing rdname
m7pr Oct 29, 2024
c01612f
rename teal_data.R to teal_data-constructor.R
m7pr Oct 29, 2024
fd4fe27
an attempt with NextMethod
m7pr Oct 29, 2024
f7894e5
add a warning
m7pr Oct 29, 2024
e53afc8
extend vignettes with `[` for teal_data
m7pr Oct 29, 2024
1ea9d8f
write tests for [.teal_data
m7pr Oct 29, 2024
220e46c
[skip style] [skip vbump] Restyle files
github-actions[bot] Oct 29, 2024
5f17af0
extned tests for different input class
m7pr Oct 30, 2024
6d3c3e8
Merge branch '211_subset@main' of https://github.com/insightsengineer…
m7pr Oct 30, 2024
39d9f37
remove NextMethod comments
m7pr Oct 30, 2024
b6c6955
extend tests with expect_warning
m7pr Oct 30, 2024
009bbc0
Update R/teal_data-extract.R
m7pr Oct 30, 2024
fc0d0c0
Update tests/testthat/test-extract.R
m7pr Oct 30, 2024
f74635a
remove unneeded roxygen2 tags
m7pr Oct 30, 2024
d3eb395
Update R/teal_data-constructor.R
m7pr Oct 30, 2024
5839f0d
warn about skipped names
m7pr Oct 30, 2024
269b77b
move tests to teal.code
m7pr Oct 30, 2024
1c2f05a
[skip style] [skip vbump] Restyle files
github-actions[bot] Oct 30, 2024
5b1e1a8
simplify teal_code extract as it reuses qenv extract
m7pr Oct 31, 2024
7fd89a1
Merge branch '211_subset@main' of https://github.com/insightsengineer…
m7pr Oct 31, 2024
a93b2d4
add .raw_data to names that need to be extracted
m7pr Nov 6, 2024
a0d4890
update teal.data constructor after changes in the code structure in q…
m7pr Nov 6, 2024
ee05635
use getFromNamespace instead of teal.code:::
m7pr Nov 6, 2024
84f8ceb
use code2list wrapper from teal.code
m7pr Nov 6, 2024
b465322
create code2list in teal.daata
m7pr Nov 6, 2024
d629a04
revert names to not add .raw_data
m7pr Nov 6, 2024
a8dd885
fix tests and improve [.teal_data
m7pr Nov 8, 2024
1121dc0
[skip style] [skip vbump] Restyle files
github-actions[bot] Nov 8, 2024
9aa4d94
Empty-Commit
m7pr Nov 8, 2024
0669e3e
bulletprove teal_data's code2list
m7pr Nov 8, 2024
98d6194
remove trimws
m7pr Nov 8, 2024
e31d65a
Merge remote-tracking branch 'origin/main' into 211_subset@main
gogonzo Nov 8, 2024
2b9da8e
postmerge fixes
gogonzo Nov 8, 2024
5c3bb7c
O M G
m7pr Nov 8, 2024
4900c3a
fix last test
m7pr Nov 8, 2024
0c5e677
fix
gogonzo Nov 8, 2024
159b6dd
fix tests
gogonzo Nov 8, 2024
a24bac3
fix for unverified
gogonzo Nov 8, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -72,10 +72,11 @@ Collate:
'join_keys.R'
'teal.data.R'
'teal_data-class.R'
'teal_data-constructor.R'
'teal_data-datanames.R'
'teal_data-extract.R'
'teal_data-get_code.R'
'teal_data-show.R'
'teal_data.R'
'testhat-helpers.R'
'topological_sort.R'
'verify.R'
Expand Down
1 change: 1 addition & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Generated by roxygen2: do not edit by hand

S3method("[",join_keys)
S3method("[",teal_data)
S3method("[<-",join_keys)
S3method("[[<-",join_keys)
S3method("join_keys<-",join_keys)
Expand Down
55 changes: 44 additions & 11 deletions R/teal_data-class.R
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,10 @@ setOldClass("join_keys")
#' @slot env (`environment`) environment containing data sets and possibly auxiliary variables.
#' Access variables with [get_var()] or [`[[`].
#' No setter provided. Evaluate code to add variables into `@env`.
#' @slot code (`character`) vector representing code necessary to reproduce the contents of `@env`.
#' @slot code (`list` of `character`) representing code necessary to reproduce the contents of `@env`.
#' Access with [get_code()].
#' No setter provided. Evaluate code to append code to the slot.
#' @slot id (`integer`) random identifier assigned to each element of `@code`. Used internally.
#' @slot warnings (`character`) vector of warnings raised when evaluating code.
#' Access with [get_warnings()].
#' @slot messages (`character`) vector of messages raised when evaluating code.
#' Read more in Code section.
#' @slot join_keys (`join_keys`) object specifying joining keys for data sets in `@env`.
#' Access or modify with [join_keys()].
#' @slot datanames (`character`) vector of names of data sets in `@env`.
Expand All @@ -32,6 +29,15 @@ setOldClass("join_keys")
#' @slot verified (`logical(1)`) flag signifying that code in `@code` has been proven to yield contents of `@env`.
#' Used internally. See [`verify()`] for more details.
#'
#' @section Code:
#'
#' Each code element is a character representing one call. Each element has possible attributes:
#' - `warnings` (`character`) the warnings output when evaluating the code element
#' - `messages` (`character`) the messages output when evaluating the code element
#' - `id (`integer`) random identifier of the code element to make sure uniqueness when joining
#' - `dependency` (`character`) names of objects that appear in this call and gets affected by this call,
#' separated by `<-` (objects on LHS of `<-` are affected by this line, and objects on RHS are affecting this line)
#'
#' @import teal.code
#' @keywords internal
setClass(
Expand Down Expand Up @@ -77,8 +83,6 @@ new_teal_data <- function(data,
}
verified <- (length(code) == 0L && length(data) == 0L)

id <- sample.int(.Machine$integer.max, size = length(code))

new_env <- rlang::env_clone(list2env(data), parent = parent.env(.GlobalEnv))
lockEnvironment(new_env, bindings = TRUE)

Expand All @@ -87,12 +91,41 @@ new_teal_data <- function(data,
methods::new(
"teal_data",
env = new_env,
code = code,
warnings = rep("", length(code)),
messages = rep("", length(code)),
id = id,
code = code2list(code),
join_keys = join_keys,
datanames = datanames,
verified = verified
)
}


#' Reshape code to the list
#'
#' List will be divided by the calls. Each element of the list contains `id` and `dependency` attributes.
#'
#' @param code `character` with the code.
#'
#' @return list of `character`s of the length equal to the number of calls in `code`.
#'
#' @keywords internal
#' @noRd
code2list <- function(code) {
checkmate::assert_character(code, null.ok = TRUE)
if (length(code) == 0) return(list())

parsed_code <- parse(text = code, keep.source = TRUE)

if (length(parsed_code)) {
lapply(split_code(code), function(current_code) {
attr(current_code, "id") <- sample.int(.Machine$integer.max, 1)
parsed_code <- parse(text = current_code, keep.source = TRUE)
attr(current_code, "dependency") <- extract_dependency(parsed_code)
current_code
})
} else {
# empty code like "", or just comments
attr(code, "id") <- sample.int(.Machine$integer.max, size = 1)
attr(code, "dependency") <- extract_dependency(parsed_code) # in case comment contains @linksto tag
list(code)
}
}
File renamed without changes.
33 changes: 33 additions & 0 deletions R/teal_data-extract.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#'
#' @section Subsetting:
#' `x[names]` subsets objects in `teal_data` environment and limit the code to the necessary needed to build limited
#' objects.
#'
#' @param names (`character`) names of objects included in `teal_subset` to subset
m7pr marked this conversation as resolved.
Show resolved Hide resolved
#' @param x (`teal_data`)
m7pr marked this conversation as resolved.
Show resolved Hide resolved
#'
#' @examples
#'
#' # Subsetting
#' data <- teal_data()
#' data <- eval_code(data, "a <- 1;b<-2")
#' data["a"]
#' data[c("a", "b")]
#'
#' join_keys(data) <- join_keys(join_key("a", "b", "x"))
#' join_keys(data["a"]) # should show empty keys
#' join_keys(data["b"])
#' join_keys(data)["a"] # should show empty keys
#' join_keys(data)["b"]
#'
#' @rdname teal_data
m7pr marked this conversation as resolved.
Show resolved Hide resolved
#'
#' @export
`[.teal_data` <- function(x, names) {
x <- NextMethod("`[`", x, check_names = FALSE) # takes 'names' from function's environment
gogonzo marked this conversation as resolved.
Show resolved Hide resolved
if (inherits(x, "qenv")) {
return(teal_data())
} # all 'names' not in object
m7pr marked this conversation as resolved.
Show resolved Hide resolved
x@join_keys <- x@join_keys[names]
x
}
2 changes: 2 additions & 0 deletions R/zzz.R
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
# use non-exported function from teal.code
lang2calls <- getFromNamespace("lang2calls", "teal.code")
extract_dependency <- getFromNamespace("extract_dependency", "teal.code")
split_code <- getFromNamespace("split_code", "teal.code")
25 changes: 16 additions & 9 deletions man/teal_data-class.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

28 changes: 27 additions & 1 deletion man/teal_data.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

39 changes: 39 additions & 0 deletions tests/testthat/test-extract.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
testthat::test_that("`[.` handles empty names", {
m7pr marked this conversation as resolved.
Show resolved Hide resolved
data <- teal_data(x = 1, a = 2)
testthat::expect_warning(
testthat::expect_equal(data[character(0)], teal_data())
)
})

testthat::test_that("`[.` handles names as NA_character_", {
data <- teal_data(x = 1, a = 2)
testthat::expect_warning(
testthat::expect_equal(data[NA_character_], teal_data())
)
})

testthat::test_that("`[.` throws warning if names is NULL", {
data <- teal_data(x = 1, a = 2)
testthat::expect_error(
data[NULL],
"Assertion on 'names' failed: Must inherit from class 'character', but has class 'NULL'."
)
})

testthat::test_that("`[.` thorws warnings if names is numeric", {
data <- teal_data(x = 1, a = 2)
testthat::expect_error(
data[1],
"Assertion on 'names' failed: Must inherit from class 'character', but has class 'numeric'."
)
})

testthat::test_that("`[.` returns limited join_keys", {
data <- teal_data(a = 1, b = 2)

join_keys(data) <- join_keys(join_key("a", "b", "x"))
testthat::expect_equal(
join_keys(data["a"]),
join_keys()
)
})
gogonzo marked this conversation as resolved.
Show resolved Hide resolved
13 changes: 5 additions & 8 deletions tests/testthat/test-teal_data.R
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,8 @@ testthat::test_that("teal_data allows to initialize empty teal_data object", {
testthat::expect_s4_class(teal_data(), "teal_data")
})

testthat::test_that("empty teal_data returns empty code, id, wartnings and messages and verified=TRUE", {
testthat::expect_identical(teal_data()@code, character(0))
testthat::expect_identical(teal_data()@id, integer(0))
testthat::expect_identical(teal_data()@messages, character(0))
testthat::expect_identical(teal_data()@warnings, character(0))
testthat::test_that("empty teal_data returns empty code and verified=TRUE", {
testthat::expect_identical(teal_data()@code, list(character(0)))
testthat::expect_identical(teal_data()@verified, TRUE)
})

Expand Down Expand Up @@ -79,16 +76,16 @@ testthat::test_that("teal_data accepts code as language", {

testthat::test_that("teal_data code unfolds code-block wrapped in '{'", {
testthat::expect_identical(
teal_data(iris1 = iris, code = quote({
get_code(teal_data(iris1 = iris, code = quote({
iris1 <- iris
}))@code,
}))),
"iris1 <- iris"
)
})

testthat::test_that("teal_data code is concatenated into single string", {
testthat::expect_identical(
teal_data(iris1 = iris, code = c("iris1 <- iris", "iris2 <- iris1"))@code,
get_code(teal_data(iris1 = iris, code = c("iris1 <- iris", "iris2 <- iris1"))),
"iris1 <- iris\niris2 <- iris1"
)
})
Expand Down
14 changes: 12 additions & 2 deletions vignettes/teal-data.Rmd
Original file line number Diff line number Diff line change
Expand Up @@ -30,18 +30,25 @@ my_data <- within(
{
data1 <- data.frame(id = 1:10, x = 11:20)
data2 <- data.frame(id = 1:10, x = 21:30)
data3 <- data.frame(id = 1:10, x = 31:40)
}
)

# get objects stored in teal_data
my_data[["data1"]]
my_data[["data1"]]
my_data[["data2"]]

# limit objects stored in teal_data
my_data[c("data1", "data3")]

# get reproducible code
get_code(my_data)

# get code just for specific object
get_code(my_data, names = "data2")

# get or set datanames
datanames(my_data) <- c("data1", "data2")
datanames(my_data) <- c("data1", "data2", "data3")
datanames(my_data)

# print
Expand Down Expand Up @@ -93,4 +100,7 @@ join_keys(my_data) <- join_keys(
)

join_keys(my_data)

# join_keys for limited object
join_keys(my_data["child"])
```
Loading