在 tibble 列表列中创建“NULL”变量

Create `NULL` variable in tibble list column

我想创建一个列表与 3 个命名元素的组合,即 Bmreplace。例如 list(B = 50, m = 100, replace = FALSE) 就是一个这样的列表实例。我想将这些组合存储为 tibble 中的列表列,以及 Bmreplace 的网格值组合作为单独的列,用于构建个人 list 值。

目前为了构建此类列表,我为单个值 Bmreplace 创建了一个序列网格,然后只是交叉连接和变异。一个reprex如下所示:

library(tidyverse)

# Create individual sequences of grid values to cross join
B <- seq(from = 50, to = 250, by = 50)
m <- seq(from = 100, to = 300, by = 100)

# Uncommenting this line - causes issues
# m <- c(NA, m)

# Don't call it replace, so function replace function is not overidden
replc <- c(TRUE, FALSE)

# Inspect the individual grid values
B
#> [1]  50 100 150 200 250
m
#> [1] 100 200 300
replc
#> [1]  TRUE FALSE

# TODO: Need to construct just NULL value
out_crossing <- tidyr::crossing(B, m, replc) %>% 
    dplyr::arrange(.data = ., B, m) %>% 
    dplyr::rename("replace" = replc) %>% 
    dplyr::rowwise(data = .) %>% 
    dplyr::mutate(boot_emp = list(tibble::lst("B" = B, 
                                              "m" = ifelse(!is.na(m), m, NULL), 
                                              "replace" = replace)))

# Seems to work, but would be nice to have 
head(out_crossing, 3)
#> # A tibble: 3 x 4
#> # Rowwise: 
#>       B     m replace boot_emp        
#>   <dbl> <dbl> <lgl>   <list>          
#> 1    50   100 FALSE   <named list [3]>
#> 2    50   100 TRUE    <named list [3]>
#> 3    50   200 FALSE   <named list [3]>

# Check an individual element is a list with 3 elements
out_crossing[1, 4]$boot_emp
#> [[1]]
#> [[1]]$B
#> [1] 50
#> 
#> [[1]]$m
#> [1] 100
#> 
#> [[1]]$replace
#> [1] FALSE

reprex package (v2.0.0)

于 2021-04-07 创建

这行得通。虽然有一个小转折。对于 m 的值,我希望列表包含 NULL 值,即希望 list(B = 50, m = NULL, replace = FALSE) 作为输出之一。不幸的是,您不能将 NULL 值添加到向量中。所以作为黑客,我试图添加 NA 值,即通过取消注释上面 reprex 中的 # m <- c(NA, m) 行,然后在 mutate 行 [=33] 中将 NA 值调整为 NULL =] 在最终列表中。这给出了 error:

Error: Problem with `mutate()` input `boot_emp`.
x replacement has length zero
ℹ Input `boot_emp` is `list(tibble::lst(B = B, m = ifelse(!is.na(m), m, NULL), replace = replace))`.
ℹ The error occurred in row 7.

如果可能的话,谁能告诉我如何让它工作吗?

问题出在 ifelse 步骤中,每个参数应该具有相同的 length,但是

length(NULL)
#[1] 0

因此,我们可能需要将条件更改为 NA 作为 'no' 值而不是 ifelse

中的 NULL

如果我们需要 NULL 个值,则在 vector 中是不可能的,但可以包含在 list 中,即

out_crossing <- tidyr::crossing(B, m, replc) %>% 
    dplyr::arrange(.data = ., B, m) %>% 
    dplyr::rename("replace" = replc) %>% 
    dplyr::rowwise(data = .) %>% 
    dplyr::mutate(boot_emp = list(tibble::lst("B" = B, 
                              "m" = ifelse(!is.na(m), m, list(NULL)), 
                                              "replace" = replace)))

然后,我们移除 list 包装

out_crossing$boot_emp <- purrr::map(out_crossing$boot_emp, 
    ~ purrr::map(.x, ~ if(is.list(.x) && is.null(.x[[1]])) NULL else .x ))

-输出

out_crossing$boot_emp[40]
#[[1]]
#[[1]]$B
#[1] 250

#[[1]]$m
#NULL

#[[1]]$replace
#[1] TRUE

或者如果只有一个元素,就做if/else

out_crossing <- tidyr::crossing(B, m, replc) %>% 
    dplyr::arrange(.data = ., B, m) %>% 
    dplyr::rename("replace" = replc) %>% 
    dplyr::rowwise(data = .) %>% 
    dplyr::mutate(boot_emp = list(tibble::lst("B" = B, 
                              "m" = if(!is.na(m)) m else NULL, 
                                              "replace" = replace)))
              
              
out_crossing$boot_emp[40]
#[[1]]
#[[1]]$B
#[1] 250

#[[1]]$m
#NULL

#[[1]]$replace
#[1] TRUE

在试验和阅读 this helpful thread 之后,我意识到有一种方法可以做到这一点,但是使用 switch 语句而不是 ifelse 语句。完整的reprex如下:

library(tidyverse)

# Create individual sequences of grid values to cross join
B <- seq(from = 50, to = 250, by = 50)
m <- seq(from = 100, to = 300, by = 100)

# Allow NULL values for m. However, vectors can't take NULL values so we
# set add NA values instead. We will coerce these to NULL values at the time
# the list is constructed
m <- c(NA, m)

# Don't call it replace, so function replace function is not overidden
replc <- c(TRUE, FALSE)

# Inspect the individual grid values
B
#> [1]  50 100 150 200 250
m
#> [1]  NA 100 200 300
replc
#> [1]  TRUE FALSE

# TODO: Need to construct just NULL value
out_crossing <- tidyr::crossing(B, m, replc) %>% 
    dplyr::arrange(.data = ., B, m) %>% 
    dplyr::rename("replace" = replc) %>% 
    dplyr::rowwise(data = .) %>% 
    dplyr::mutate(boot_emp = list(tibble::lst("B" = B, 
                                              "m" = switch(!is.na(m), m, NULL),
                                              # "m" = ifelse(!is.na(m), m, NULL), 
                                              "replace" = replace)))

# Seems to work, but would be nice to have 
head(out_crossing, 3)
#> # A tibble: 3 x 4
#> # Rowwise: 
#>       B     m replace boot_emp        
#>   <dbl> <dbl> <lgl>   <list>          
#> 1    50   100 FALSE   <named list [3]>
#> 2    50   100 TRUE    <named list [3]>
#> 3    50   200 FALSE   <named list [3]>
# out_crossing %>% dplyr::glimpse()
# out_crossing %>% View()

# Check an individual element is a list with 3 elements
out_crossing[8, 4]$boot_emp
#> [[1]]
#> [[1]]$B
#> [1] 50
#> 
#> [[1]]$m
#> NULL
#> 
#> [[1]]$replace
#> [1] TRUE

reprex package (v2.0.0)

于 2021-04-07 创建

希望这对其他人有帮助。我希望看到其他方法来解决这个问题,这些方法可能比这种方法更有优势。所以请继续添加到这个线程,以便我自己和其他人可以学习。