[英]large string vector to data.frame
我有一個大型矢量(100M元素)的單詞類型:
words <- paste(letters,letters,letters,letters,sep="_")
(實際數據中的單詞並不完全相同,但全長為8)
我想將它們轉換為一個數據框,每個字母的每個字母都有一列,每個字有一行。 為此我在結果上嘗試了str_split_fixed
和rbind
但是在大矢量R上凍結/需要永遠。
如此期望的形式輸出:
l1 l2 l3 l4
1 a a a a
2 b b b b
3 c c c c
有更快的方法嗎?
paste()
將向量元素折疊在一起 fread()
將折疊的向量解析為data.table / data.frame 作為一個功能:
collapse2fread <- function(x,sep) {
require(data.table)
fread(paste0(x,collapse="\n"),sep=sep,header=FALSE)
}
也可以嘗試通過Rcpp
包在c ++中實現它以獲得更多的東西嗎? 就像是:
std::string collapse_cpp(CharacterVector subject,const std::string collapseBy){
int n = subject.size();
std::string collapsed;
for(int i=0;i<n;i++){
collapsed += std::string(subject[i]) + collapseBy;
}
return(collapsed);
}
然后我們得到:
collapse_cpp2fread <- function(x,sep) {
require(data.table)
fread(collapse_cpp(x,collapse="\n"),sep=sep,header=FALSE)
}
microbenchmark(
paste0(words,collapse="\n"),
collapse_cpp(words,"\n"),
times=100)
不多但是有點兒:
> Unit: microseconds
> expr min lq median uq max neval
> paste0(words, collapse = "\\n") 7.297 7.7695 8.162 8.4255 33.824 100
> collapse_cpp(words, "\\n") 4.477 5.0095 5.117 5.3525 17.052 100
做一個更真實的輸入
words <- rep(paste0(letters[1:8], collapse = '_'), 1e5) # 100K elements
風向標:
microbenchmark(
do.call(rbind, strsplit(words, '_')),
fread(paste0(words,collapse="\n"),sep="_",header=FALSE),
fread(collapse_cpp(words,"\n"),sep="_",header=FALSE),
times=10)
得到:
> Unit: milliseconds
> expr min lq median uq
> do.call(rbind, strsplit(words, "_")) 782.71782 796.19154 822.73694 854.22211
> fread(paste0(words, collapse = "\\n"), sep = "_", header = FALSE) 62.56164 64.13504 68.22512 71.96075
> fread(collapse_cpp(words, "\\n"), sep = "_", header = FALSE) 47.16362 47.78030 50.12867 52.23102
> max neval
> 863.0790 10
> 151.5969 10
> 109.9770 10
這么大約20倍的改進? 希望能幫助到你!
擴展基於Rcpp的解決方案。 如果你可以假定輸入的結構,那么很容易在Rcpp中完成所有這一切,只需要最少的數據復制。
// [[Rcpp::export]]
List bazinga( CharacterVector txt, int nc ){
int n = txt.size() ;
std::vector<CharacterVector> columns(nc) ;
for( int i=0; i<nc; i++){
columns[i] = CharacterVector(n) ;
}
std::string tmp ;
for( int i=0; i<n; i++){
const char* p = txt[i];
for(int j=0; j<nc; j++){
tmp = *p ;
columns[j][i] = tmp ;
p +=2 ;
}
}
List out = wrap(columns) ;
return out ;
}
我明白了:
> microbenchmark(f(), bazinga(words, 8), collapse2fread(words,
+ "_"), collapse_cpp2fread(words, "_"), times = 10)
Unit: milliseconds
expr min lq median uq max neval
f() 830.21571 871.38955 899.07207 1001.18561 1299.15783 10
bazinga(words, 8) 26.26454 30.61620 33.37360 46.24160 64.09243 10
collapse2fread(words, "_") 59.96217 61.58535 67.20007 93.61615 97.85007 10
collapse_cpp2fread(words, "_") 46.79471 48.58391 49.99636 82.69684 119.88587 10
如果您使用的是Unix,那么您應該利用命令行。 在那里處理大數據通常會更快,然后在減少后將其帶入R中。 在這里,我將words
vector寫入文件,然后在system
R函數中使用Unix命令重寫它。
> words <- rep(paste0(letters[1:8], collapse = '_'), 1e5)
> cat(words, file = 'out.txt', sep = '\n')
> write.table(system(' cat out.txt | tr "_" " " ', intern = TRUE),
row.names = FALSE, col.names = FALSE,
quote = FALSE, file = 'out.txt')
> head(read.table('out.txt'))
# V1 V2 V3 V4 V5 V6 V7 V8
# 1 a b c d e f g h
# 2 a b c d e f g h
# 3 a b c d e f g h
# 4 a b c d e f g h
# 5 a b c d e f g h
# 6 a b c d e f g h
和典型的R do.call(rbind, ...)
方法:
f <- function()
{
x <- do.call(rbind, strsplit(words, '_'))
y <- data.frame(x)
names(y) <- paste0('l', ncol(y))
return(y)
}
> microbenchmark(f())
# Unit: milliseconds
# expr min lq median uq max neval
# f() 818.2391 959.088 964.1105 989.081 997.8625 100
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.