【问题标题】:How to rowwise select random elements in a tibble via dplyr in R?如何通过 R 中的 dplyr 逐行选择 tibble 中的随机元素?
【发布时间】:2020-12-30 22:10:03
【问题描述】:

我有 3 个人的一些 DNA 数据(等位基因),每行代表一个 SNP。为了获得一些可共享的测试数据,我想将数据按行随机抽样到一个新的 tibble 中,以获取一些不代表真人的假 DNA 数据。

例如,我最初的 tibble data 可能如下所示:

person_1,   person_2,   person_3

AA,         AG,         GG (i.e. data from person_1   person_2   person_3)

AC,         CC,         AC (i.e. data from person_1   person_2   person_3)

..         ..         ..

我希望结果是这样的:

random_1,  random_2,  random_3

GG,         AA,        AG (i.e. randomly assigned to person_3, person_1, person_2)

CC,         AC,        AC (i.e. randomly assigned to person_2, person_3, person_1)

...

我已经可以用下面的代码做到这一点:

data %>% 
  split(f = 1:nrow(.)) %>% 
  purrr::map_dfr(~ .x[,sample(1:ncol(.x),ncol(.x))] %>% 
                   rename( setNames(object = names(.),
                                    nm = paste0("test_",sprintf("%02d", 1:length(.))))
                   )
  )

但是,我的挑战是我的 tibble 有超过 700.000 行,这使得上面的代码非常慢。我曾尝试通过dplyr 包中的mutate()、rowwise() 和across 进行操作,但没有成功。

对其他更快的方法有什么建议吗?

【问题讨论】:

  • 元素是指观察/行吗?
  • 我的意思是每一行中的观察 - 所以我希望,对于我的 tibble 中的每一行,返回一个与原始行长度相同的新行,但新行中的观察是随机的从旧行采样。然后对 tibble 中的每一行重复此操作。希望这是有道理的(我试图通过上面的例子来澄清这一点)

标签: r dplyr rowwise


【解决方案1】:

我们可以使用pmap(来自purrr)和sample。

library(dplyr)
library(purrr)
library(stringr)
df1 %>%
    pmap_dfr(~ sample(c(...))) %>%
    rename_all(~ str_c('random_', seq_along(.)))

-输出

# A tibble: 2 x 3
#  random_1 random_2 random_3
#  <chr>    <chr>    <chr>   
#1 AG       AA       GG      
#2 CC       AC       AC    

或者另一种选择是重塑为“长”格式,按slice_sample 进行分组,然后重新整形为“宽”

library(tidyr)
df1 %>%
   mutate(rn = row_number()) %>% 
   pivot_longer(cols = -rn) %>% 
   group_by(rn) %>% 
   slice_sample(prop = 1) %>% 
   mutate(name = str_c('random_', row_number())) %>% 
   ungroup %>% 
   pivot_wider(names_from = name, values_from = value)
# A tibble: 2 x 4
#     rn random_1 random_2 random_3
#  <int> <chr>    <chr>    <chr>   
#1     1 AG       GG       AA      
#2     2 CC       AC       AC   

有一个使用rowwise的选项,但是假设行数为700000,效率会降低

df1 %>% 
   rowwise %>%
   transmute(col1 = list(sample(c_across(everything())))) %>%
   unnest_wider(c(col1), names_repair =  ~ str_c('random_', seq_along(.)))
# A tibble: 2 x 3
#  random_1 random_2 random_3
#  <chr>    <chr>    <chr>   
#1 AG       AA       GG      
#2 CC       AC       AC      

在base R 中,这可以使用apply 完成

out <- as.data.frame(t(apply(df1, 1, sample)))
names(out) <- paste0('random_', seq_along(out))

数据

df1 <- structure(list(person_1 = c("AA", "AC"), person_2 = c("AG", "CC"
), person_3 = c("GG", "AC")), class = "data.frame", row.names = c(NA, 
-2L))

【讨论】:

  • 非常感谢。 purrr 解决方案非常优雅!它确实比我自己的解决方案运行得更快,但是它仍然在我的 4 核 mac 上运行 5-7 分钟,以执行 700.000 行和 16 列的计算。也许这是一个公平的运行时间。再次感谢您的帮助!
  • @aleksandereiken 如果您可以根据n 的行数将数据拆分为多个块并在每个块上并行使用foreach,则可以加快速度
猜你喜欢
  • 2019-08-13
  • 1970-01-01
  • 2012-03-12
  • 2018-04-28
  • 2013-04-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多