问题描述
我有一些 DNA 数据(等位基因),比如 3 个人,每行代表一个 SNP。为了得到一些可共享的测试数据,我想将数据随机逐行采样到一个新的tibble中,以获得一些不代表真人的假DNA数据。
例如,我的初始标题 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 行,这使得上面的代码非常慢。我曾尝试通过 mutate()
包中的 rowwise()
、across
和 dplyr
进行操作,但未成功。
对其他更快的方法有什么建议吗?
解决方法
我们可以将 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))