在 R 中选择二元向量中的位置(我怎样才能固定?)
Selecting positions in a binary vector (how can I fasten up?) in R
我有一个如下所示的数据框:
SNP1 01010101000000100000010010001010011001010101
SNP2 01010010101000100000000000000001100001001000
SNP3 01010101000000000000000000000100011111111111
...但实际上包含约 800 万行,每个二进制向量的长度均为 1000。
我需要 select 这些二进制向量中的特定位置(跨所有行)。我发现这样做的肮脏方法是删除行名称,将每个数字转换为一列,然后创建一个包含我感兴趣的位置的对象。
以下内容适用于示例数据,但对我的真实数据来说效率不高(现在 运行 很长一段时间了)。有什么办法可以让它更快吗?
library(data.table)
library(stringr)
setwd("test/")
DATADIR="datadir/"
OUTPUTDIR="outputdir/"
dir.create(OUTPUTDIR, showWarnings = FALSE)
baseline<-read.table(paste0(DATADIR,"input.file"), colClasses = "character")
# Pass BP name to row name (so that I can split the binary vector into multiple columns)
row.names(baseline) <- baseline$V1
baseline$V1 <- NULL
# split cells containing the binary vectors into multiple columns - thank you @Onyambu for this!
baseline_new <- read.table(text = gsub('(.)','\1 ',baseline$V2),fill=TRUE)
# select columns of interest
columns_to_keep <- c(1, 4, 8, 10)
baseline_new_ss <- baseline_new[, columns_to_keep]
# create new object containing a column with the original row names, then recreate binary vector based on subsetted binary positions.
baseline_final <- as.data.frame(row.names(baseline))
baseline_final$V2 <- as.character(interaction(baseline_new_ss,sep=""))
输出(select仅位置 1、4、8 和 10)应如下所示:
SNP1 0110
SNP2 0100
SNP3 0110
我相信有一种不那么复杂的方法可以做到这一点。
谢谢!!
你可以试试这个:
at <- function(binary_strings, positions)
{
charvec <- character(length(binary_strings))
for(i in seq_along(positions))
{
charvec <- paste0(charvec, substr(binary_strings, positions[i], positions[i]))
}
return(charvec)
}
现在你可以做
at(baseline$`whatever your binary column is called`, c(1, 4, 8, 10))
#> [1] "0110" "0100" "0110"
所以用管道你可以做到
library(magrittr)
baseline$`whatever your binary column is called` %<>% at(c(1, 4, 8, 10))
print(baseline)
#> whatever your binary column is called
#> SNP1 0110
#> SNP2 0100
#> SNP3 0110
我使用相当慢的 Windows PC 在 800 万行上以 7 秒的速度对此进行了基准测试。
您可以将 strsplit
、select 元素与 mapply
和 paste
一起使用回到数据框中。虽然不知道这有多快,但它很简洁:)
`rownames<-`(data.frame(values=
mapply(function(x) Reduce(paste0, x[c(1, 4, 8, 10)]),
sapply(dat$V2, strsplit, ""))),
dat$V1)
# values
# SNP1 0110
# SNP2 0100
# SNP3 0110
也许有一个 data.table
解决方案不会在内部制作副本 -> fast.
数据:
"SNP1 01010101000000100000010010001010011001010101
SNP2 01010010101000100000000000000001100001001000
SNP3 01010101000000000000000000000100011111111111"->tx
dat <- data.table::fread(text=tx, header=F)
另一种选择是使用 stringi
:
时间码:
nr <- 1e6
nc <- 1e3
l <- rep(paste(rep(1L, nc), collapse=""), nr)
writeLines(l, "test.txt")
cols <- c(1,4,8,10)
library(stringi)
library(iotools)
microbenchmark::microbenchmark(times=1L,
stringi=lapply(cols, function(n) stri_sub(l, n, n)),
iotools=input.file("test.txt", formatter=dstrfw,
col_types=rep("character", nc), widths=rep(1L, nc))[, cols]
)
时间:
Unit: seconds
expr min lq mean median uq max neval
stringi 1.329223 1.329223 1.329223 1.329223 1.329223 1.329223 1
iotools 76.250773 76.250773 76.250773 76.250773 76.250773 76.250773 1
我有一个如下所示的数据框:
SNP1 01010101000000100000010010001010011001010101
SNP2 01010010101000100000000000000001100001001000
SNP3 01010101000000000000000000000100011111111111
...但实际上包含约 800 万行,每个二进制向量的长度均为 1000。
我需要 select 这些二进制向量中的特定位置(跨所有行)。我发现这样做的肮脏方法是删除行名称,将每个数字转换为一列,然后创建一个包含我感兴趣的位置的对象。
以下内容适用于示例数据,但对我的真实数据来说效率不高(现在 运行 很长一段时间了)。有什么办法可以让它更快吗?
library(data.table)
library(stringr)
setwd("test/")
DATADIR="datadir/"
OUTPUTDIR="outputdir/"
dir.create(OUTPUTDIR, showWarnings = FALSE)
baseline<-read.table(paste0(DATADIR,"input.file"), colClasses = "character")
# Pass BP name to row name (so that I can split the binary vector into multiple columns)
row.names(baseline) <- baseline$V1
baseline$V1 <- NULL
# split cells containing the binary vectors into multiple columns - thank you @Onyambu for this!
baseline_new <- read.table(text = gsub('(.)','\1 ',baseline$V2),fill=TRUE)
# select columns of interest
columns_to_keep <- c(1, 4, 8, 10)
baseline_new_ss <- baseline_new[, columns_to_keep]
# create new object containing a column with the original row names, then recreate binary vector based on subsetted binary positions.
baseline_final <- as.data.frame(row.names(baseline))
baseline_final$V2 <- as.character(interaction(baseline_new_ss,sep=""))
输出(select仅位置 1、4、8 和 10)应如下所示:
SNP1 0110
SNP2 0100
SNP3 0110
我相信有一种不那么复杂的方法可以做到这一点。
谢谢!!
你可以试试这个:
at <- function(binary_strings, positions)
{
charvec <- character(length(binary_strings))
for(i in seq_along(positions))
{
charvec <- paste0(charvec, substr(binary_strings, positions[i], positions[i]))
}
return(charvec)
}
现在你可以做
at(baseline$`whatever your binary column is called`, c(1, 4, 8, 10))
#> [1] "0110" "0100" "0110"
所以用管道你可以做到
library(magrittr)
baseline$`whatever your binary column is called` %<>% at(c(1, 4, 8, 10))
print(baseline)
#> whatever your binary column is called
#> SNP1 0110
#> SNP2 0100
#> SNP3 0110
我使用相当慢的 Windows PC 在 800 万行上以 7 秒的速度对此进行了基准测试。
您可以将 strsplit
、select 元素与 mapply
和 paste
一起使用回到数据框中。虽然不知道这有多快,但它很简洁:)
`rownames<-`(data.frame(values=
mapply(function(x) Reduce(paste0, x[c(1, 4, 8, 10)]),
sapply(dat$V2, strsplit, ""))),
dat$V1)
# values
# SNP1 0110
# SNP2 0100
# SNP3 0110
也许有一个 data.table
解决方案不会在内部制作副本 -> fast.
数据:
"SNP1 01010101000000100000010010001010011001010101
SNP2 01010010101000100000000000000001100001001000
SNP3 01010101000000000000000000000100011111111111"->tx
dat <- data.table::fread(text=tx, header=F)
另一种选择是使用 stringi
:
时间码:
nr <- 1e6
nc <- 1e3
l <- rep(paste(rep(1L, nc), collapse=""), nr)
writeLines(l, "test.txt")
cols <- c(1,4,8,10)
library(stringi)
library(iotools)
microbenchmark::microbenchmark(times=1L,
stringi=lapply(cols, function(n) stri_sub(l, n, n)),
iotools=input.file("test.txt", formatter=dstrfw,
col_types=rep("character", nc), widths=rep(1L, nc))[, cols]
)
时间:
Unit: seconds
expr min lq mean median uq max neval
stringi 1.329223 1.329223 1.329223 1.329223 1.329223 1.329223 1
iotools 76.250773 76.250773 76.250773 76.250773 76.250773 76.250773 1