使用R中的Data.Table或Rcpp用字符串快速替换NA
我有一个很大的表:10M行乘以33列,其中28列有一些NA值。这些NA值需要使用locf进行修补。我读了一些关于这个话题的文章和文章。然而,这些线程是关于替换数值向量的。我不太熟悉Rcpp,所以我不知道如何更改它们的代码以适应字符串——我的数据都是字符串 以下是我的示例数据: 输入数据 下面是我使用data.table的代码: 虽然上面的代码需要几分之一秒的时间来执行,但处理原始数据集中的一列大约需要10分钟,即使使用data.table,处理28列也需要280分钟 我假设我没有真正利用上面的data.table的功能。我不是很确定。我真诚地感谢任何有助于加快na.locf功能的帮助使用R中的Data.Table或Rcpp用字符串快速替换NA,r,data.table,rcpp,zoo,R,Data.table,Rcpp,Zoo,我有一个很大的表:10M行乘以33列,其中28列有一些NA值。这些NA值需要使用locf进行修补。我读了一些关于这个话题的文章和文章。然而,这些线程是关于替换数值向量的。我不太熟悉Rcpp,所以我不知道如何更改它们的代码以适应字符串——我的数据都是字符串 以下是我的示例数据: 输入数据 下面是我使用data.table的代码: 虽然上面的代码需要几分之一秒的时间来执行,但处理原始数据集中的一列大约需要10分钟,即使使用data.table,处理28列也需要280分钟 我假设我没有真正利用上面的d
有没有更有效的方法来取代上述NA 为了这个例子的目的,我简化了这个问题,但我想它很容易推广。下面的代码使用C++11语法定义Rcpp中的locppf函数:
#include <Rcpp.h>
using namespace Rcpp;
// [[Rcpp::plugins(cpp11)]]
using Map = std::unordered_map<double, int> ;
using Pair = Map::value_type ;
// [[Rcpp::export]]
CharacterVector locppf(NumericVector g, CharacterVector s) {
auto n = g.size() ;
CharacterVector out = clone(s) ;
Map map ;
for(int i=n-1; i>=0; i--){
double value = g[i] ;
auto it = map.find( value ) ;
if( it == map.end() ){
map.insert( Pair(value, i) ) ;
} else {
// if the current value is NA, replace it with the data at correct idx
auto current = s[i] ;
if( CharacterVector::is_na( current ) ){
out[i] = s[ it->second ] ;
} else {
it->second = i ;
}
}
}
return out ;
}
在这里,我们检查地图是否已经看到当前值,如果没有,我们将保留当前索引
auto current = s[i] ;
if( CharacterVector::is_na( current ) ){
out[i] = s[ it->second ] ;
} else {
it->second = i ;
}
这里我们用CharacterVector::is_NA检查当前值是否为NA
如果是,则使用我们先前保留的索引中的值填充结果向量
如果没有,我们将更改映射为该组记住的索引
现在让我们给自己一些数据:
library("zoo")
library("dplyr")
library("data.table")
with_holes <- function(x, p = .2){
n <- length(x)
x[ sample(n, n*p) ] <- NA
x
}
n <- 1e6
x <- sample( as.numeric(1:100), n, replace= TRUE )
y <- with_holes( sample( letters, n, replace = TRUE) )
d <- data_frame( x = x, y = y )
将data.table语法与na.locf一起使用。但我不能保证这是最好的data.table方法
> d2 <- as.data.table(d)
> system.time( d2[ , y := na.locf(y, fromLast = TRUE, na.rm = FALSE) , x ] )
user system elapsed
0.159 0.030 0.188
为了这个例子的目的,我简化了这个问题,但我想它很容易推广。下面的代码使用C++11语法定义Rcpp中的locppf函数:
#include <Rcpp.h>
using namespace Rcpp;
// [[Rcpp::plugins(cpp11)]]
using Map = std::unordered_map<double, int> ;
using Pair = Map::value_type ;
// [[Rcpp::export]]
CharacterVector locppf(NumericVector g, CharacterVector s) {
auto n = g.size() ;
CharacterVector out = clone(s) ;
Map map ;
for(int i=n-1; i>=0; i--){
double value = g[i] ;
auto it = map.find( value ) ;
if( it == map.end() ){
map.insert( Pair(value, i) ) ;
} else {
// if the current value is NA, replace it with the data at correct idx
auto current = s[i] ;
if( CharacterVector::is_na( current ) ){
out[i] = s[ it->second ] ;
} else {
it->second = i ;
}
}
}
return out ;
}
在这里,我们检查地图是否已经看到当前值,如果没有,我们将保留当前索引
auto current = s[i] ;
if( CharacterVector::is_na( current ) ){
out[i] = s[ it->second ] ;
} else {
it->second = i ;
}
这里我们用CharacterVector::is_NA检查当前值是否为NA
如果是,则使用我们先前保留的索引中的值填充结果向量
如果没有,我们将更改映射为该组记住的索引
现在让我们给自己一些数据:
library("zoo")
library("dplyr")
library("data.table")
with_holes <- function(x, p = .2){
n <- length(x)
x[ sample(n, n*p) ] <- NA
x
}
n <- 1e6
x <- sample( as.numeric(1:100), n, replace= TRUE )
y <- with_holes( sample( letters, n, replace = TRUE) )
d <- data_frame( x = x, y = y )
将data.table语法与na.locf一起使用。但我不能保证这是最好的data.table方法
> d2 <- as.data.table(d)
> system.time( d2[ , y := na.locf(y, fromLast = TRUE, na.rm = FALSE) , x ] )
user system elapsed
0.159 0.030 0.188
回答得好。遗憾的是,由于引用语义的原因,我们不能真正地对其进行微基准标记。找到了一种先复制的方法。那么,您的Rcpp/C++11解决方案比data.table快3到4倍,比dplyr快5倍。@DirkEddelbuettel这里实际上没有来自data.table或dplyr IIRC的任何内容。这只是Rcpp na.locf vs.zoo::na.locf。我记得看到一个data.table版本的na.locf使用eddie编写的滚动联接。我认为没有经过测试。是的,我正要对OP的问题做出评论——只是在data.table中分组并发送到na.locf,这并不是很慢。这就是为什么我想看到一些基准测试。看起来Romain得到了一个5倍的分数,在某些情况下这可能是值得的,并且可能对OP有帮助。总的来说,当然,你是对的,这里没有那么多问题。@Romain-谢谢你的努力和指导。老实说,这是我的第一个Rcpp程序,从我开始使用data.table到现在只有4天了。所以,非常恭敬地说,您是否认为您可以帮助我理解如何使用您的函数在我的数据集中循环?我能够编译您的代码并使用一个带有索引数字向量和一个字符向量的虚拟对象进行测试。我非常感谢您的帮助和从这次练习中学到的知识。我继续搜索,但找不到如何使用data.table循环遍历数据集中的所有列。提前谢谢。回答得好。遗憾的是,由于引用语义的原因,我们不能真正地对其进行微基准标记。找到了一种先复制的方法。那么,您的Rcpp/C++11解决方案比data.table快3到4倍,比dplyr快5倍。@DirkEddelbuettel这里实际上没有来自data.table或dplyr IIRC的任何内容。这只是Rcpp na.locf vs.zoo::na.locf。我记得看到一个data.table版本的na.locf使用eddie编写的滚动联接。我认为没有经过测试。是的,我正要对OP的问题做出评论——只是在data.table中分组并发送到na.locf,这并不是很慢。这就是为什么我想看到一些基准测试。看起来Romain得到了一个5倍的分数,在某些情况下这可能是值得的,并且可能对OP有帮助。总的来说,当然,你是对的,这里没有那么多问题。@Romain-谢谢你的努力和指导。老实说,这是我的第一个Rcpp程序,从我开始使用data.table到现在只有4天了。所以,非常恭敬地说,您是否认为您可以帮助我理解如何使用您的函数在我的数据集中循环?我能够编译您的代码并使用带有索引数字向量的虚拟对象进行测试 和一个字符向量。我非常感谢您的帮助和从这次练习中学到的知识。我继续搜索,但找不到如何使用data.table循环遍历数据集中的所有列。提前谢谢。
> d2 <- as.data.table(d)
> system.time( d2[ , y := na.locf(y, fromLast = TRUE, na.rm = FALSE) , x ] )
user system elapsed
0.159 0.030 0.188
> system.time( locppf(d$x, d$y) )
user system elapsed
0.028 0.001 0.028