Build Nesting or Hierarchical Structures.
nestr
Installation
You can install the stable version on CRAN:
install.packages("nestr")
Or alternatively, you can install the development version from Github using below.
# install.packages("remotes")
remotes::install_github("emitanaka/nestr")
Getting started
library(nestr)
The main purpose of nestr
R-package is to build nested (or hierarchical) structures with output as a list or a data frame. The syntax is particularly useful when the child units are unbalanced across the parental levels.
nest_in(c("a", "c", "a", "b"), # parental vector
2 ~ 3, # level 2 has 3 children
. ~ 1) # the remaining levels have 1 child
#> $a
#> [1] "1"
#>
#> $c
#> [1] "1"
#>
#> $a
#> [1] "1"
#>
#> $b
#> [1] "1" "2" "3"
The parental vector may be a factor with different ordering of the levels.
nest_in(factor(c("a", "c", "a", "b"), levels = c("a", "c", "b")), # parental vector
2 ~ 3, # level 2 has 3 children
. ~ 1) # the remaining levels have 1 child
#> $a
#> [1] "1"
#>
#> $c
#> [1] "1" "2" "3"
#>
#> $a
#> [1] "1"
#>
#> $b
#> [1] "1"
Or you can refer the parental level by character.
nest_in(c("a", "c", "a", "b"), # parental vector
"b" ~ 3, # "b" has 3 children
. ~ 1) # the remaining levels have 1 child
#> $a
#> [1] "1"
#>
#> $c
#> [1] "1"
#>
#> $a
#> [1] "1"
#>
#> $b
#> [1] "1" "2" "3"
A more interesting example.
nest_in(c("Math", "Science", "Economics", "Art"),
"Science" ~ 2,
c("Art", "Math") ~ 10,
. ~ 3,
prefix = "student-",
leading0 = 4)
#> $Math
#> [1] "student-0001" "student-0002" "student-0003"
#>
#> $Science
#> [1] "student-0001" "student-0002"
#>
#> $Economics
#> [1] "student-0001" "student-0002" "student-0003"
#>
#> $Art
#> [1] "student-0001" "student-0002" "student-0003" "student-0004" "student-0005"
#> [6] "student-0006" "student-0007" "student-0008" "student-0009" "student-0010"
edibble::nested_in
and nestr::nest_in
The syntax of nestr
is similar to edibble::nested_in
which is used within the edibble::set_units
to construct nested (or hierarchical) structures.
Unlike edibble::nested_in
, the nestr::nest_in
returns a data frame. You may also notice that the name of the function is different although both share virtually the same syntax. One way to remember the differences in function name is that nested_in
is for edibble
and is meant for construction of the experimental design (notice the “ed” in the function name). When the verb is written in present tense (i.e. nest_in
), it’s part of nestr
and your focus is to create a structure in the present.
amplify
The dplyr::mutate
function modifies, creates or deletes columns but doesn’t alter the number of rows. The nestr::amplify
function can create new columns which generally increase (or amplify) the size of the row dimension. The columns that were amplified as a result of the created column will be duplicated. If you are familiar with gene replication process then you can recall these functions in those terms. An amplified gene is just a duplication of the original. A mutated gene modifies the original state.
df <- data.frame(country = c("AU", "NZ", "JPN", "CHN", "USA")) %>%
amplify(soil = nest_in(country,
"AU" ~ 10,
"NZ" ~ 8,
. ~ 5,
prefix = "sample",
leading0 = TRUE),
rep = nest_in(soil,
1:3 ~ 3, # first 3 samples have 3 technical rep
. ~ 2)) # remaining have two rep
tibble::as_tibble(df)
#> # A tibble: 71 × 3
#> country soil rep
#> <chr> <chr> <chr>
#> 1 AU sample01 1
#> 2 AU sample01 2
#> 3 AU sample01 3
#> 4 JPN sample01 1
#> 5 JPN sample01 2
#> 6 JPN sample01 3
#> 7 NZ sample01 1
#> 8 NZ sample01 2
#> 9 NZ sample01 3
#> 10 CHN sample01 1
#> # … with 61 more rows
tidyverse
For those who want to stay within the tidyverse framework, this is in fact doable just using dplyr
and tidyr
.
The semantics are less direct, however and chaining of nesting structure is cumbersome. For example, see the equivalent example from before below.
library(dplyr)
library(tidyr)
data.frame(country = c("AU", "NZ", "JPN", "CHN", "USA")) %>%
mutate(soil = case_when(country=="AU" ~ list(sprintf("sample%.2d", 1:10)),
country=="NZ" ~ list(sprintf("sample%.2d", 1:8)),
TRUE ~ list(sprintf("sample%.2d", 1:5)))) %>%
unnest_longer(soil) %>%
mutate(rep = case_when(soil %in% c("sample01", "sample02", "sample03") ~ list(1:3),
TRUE ~ list(1:2))) %>%
unnest_longer(rep)
#> # A tibble: 81 × 3
#> country soil rep
#> <chr> <chr> <int>
#> 1 AU sample01 1
#> 2 AU sample01 2
#> 3 AU sample01 3
#> 4 AU sample02 1
#> 5 AU sample02 2
#> 6 AU sample02 3
#> 7 AU sample03 1
#> 8 AU sample03 2
#> 9 AU sample03 3
#> 10 AU sample04 1
#> # … with 71 more rows
The intent I think is more clear from the above amplify
example. It’s a personal preference so use what suits your own situation!