使用Rcpp将因子列转换为R data.frame中的日期列

时间:2018-10-11 11:29:37

标签: r rcpp

基于此问题

How can I access the levels of the factors of a data.frame in Rcpp when it is passed as an argument from R?

我想使用Rcpp 将生成的字符列转换为日期。这是我的初始代码,它将因子级别转换为字符列。

样本数据:

df <- data.frame(
  col1 = c(1, 2, 3),
  col2 = c("a", "b", "c"),
  col3 = factor(
    x = c("01/01/2017 00:00:00", "01/06/2017 00:00:00", "05/01/2017 00:00:00"),
    levels = c("01/01/2017 00:00:00", "01/06/2017 00:00:00", "05/01/2017 00:00:00")
    ),
  col4 = factor(
    x = c("01/01/2018 00:00:00", "01/06/2018 00:00:00", "05/01/2018 00:00:00"),
    levels = c("01/01/2018 00:00:00", "01/06/2018 00:00:00", "05/01/2018 00:00:00")
    ),
  stringsAsFactors = FALSE
  )

Rcpp代码:

#include <Rcpp.h>
using namespace Rcpp;

// [[Rcpp::export]]
void GetDateFromFactorLevels(DataFrame df1) {
    CharacterVector varNames = df1.names();
    for(int i = 0; i < df1.length(); i++) {
        if(Rf_isFactor(df1[i]) == 1) {
            IntegerVector tempVec=df1[i];
            df1[i] = tempVec.attr("levels");
        }
   }
}

> GetDateFromFactorLevels(df)
> sapply(df, class)
       col1        col2        col3        col4 
  "numeric" "character" "character" "character"
> df
  col1 col2                col3                col4
1    1    a 01/01/2017 00:00:00 01/01/2018 00:00:00
2    2    b 01/06/2017 00:00:00 01/06/2018 00:00:00
3    3    c 05/01/2017 00:00:00 05/01/2018 00:00:00

是否可以这样做并得到类似的东西?

> sapply(df, class)
       col1        col2        col3        col4 
  "numeric" "character"      "Date"      "Date"

> df
  col1 col2                col3                col4
1    1    a          2017-01-01          2018-01-01
2    2    b          2017-06-01          2018-06-01
3    3    c          2017-01-05          2018-01-05

2 个答案:

答案 0 :(得分:3)

是的。如Dirk Eddelbuettel's answer所述,如果您可以使用DatetimeVector并将其转换为DateVector,则将容易得多。如果您确实必须处理各种因素,那么作为一种快速而肮脏的解决方案(其他人可能会提供更优雅的解决方案),您可以这样做:

#include <Rcpp.h>

using namespace Rcpp;

// [[Rcpp::export]]
void GetDateFromFactorLevels(DataFrame df1) {
    int n = df1.nrows();
    for ( int i = 0; i < df1.length(); i++ ) {
        if ( Rf_isFactor(df1[i]) == 1 ) {
            IntegerVector values = df1[i]; // Get the integer values
            CharacterVector levels = values.attr("levels"); // and the levels
            DateVector result(n); // Make an empty DateVector
            for ( int j = 0; j < n; ++j ) {
                // And for every element of the factor, look up the level
                // value corresponding to its integer value,
                // and construct a Date by turning it into an std::string
                // (and specifying the applicable date format)
                result[j] = Date(std::string(levels[values[j] - 1]), "%d/%m/%Y");
            }
            // Then just replace the df1 column with the DateVector
            df1[i] = result;
        }
    }
}

从R调用时:

Rcpp::sourceCpp("date-stuff.cpp")
df <- data.frame(
    col1 = c(1, 2, 3),
    col2 = c("a", "b", "c"),
    col3 = factor(
        x = c("01/01/2017 00:00:00", "01/06/2017 00:00:00", "05/01/2017 00:00:00"),
        levels = c("01/01/2017 00:00:00", "01/06/2017 00:00:00", "05/01/2017 00:00:00")
    ),
    col4 = factor(
        x = c("01/01/2018 00:00:00", "01/06/2018 00:00:00", "05/01/2018 00:00:00"),
        levels = c("01/01/2018 00:00:00", "01/06/2018 00:00:00", "05/01/2018 00:00:00")
    ),
    stringsAsFactors = FALSE
)
sapply(df, class)
#>        col1        col2        col3        col4 
#>   "numeric" "character"    "factor"    "factor"
df
#>   col1 col2                col3                col4
#> 1    1    a 01/01/2017 00:00:00 01/01/2018 00:00:00
#> 2    2    b 01/06/2017 00:00:00 01/06/2018 00:00:00
#> 3    3    c 05/01/2017 00:00:00 05/01/2018 00:00:00
GetDateFromFactorLevels(df)
sapply(df, class)
#>        col1        col2        col3        col4 
#>   "numeric" "character"      "Date"      "Date"
df
#>   col1 col2       col3       col4
#> 1    1    a 2017-01-01 2018-01-01
#> 2    2    b 2017-06-01 2018-06-01
#> 3    3    c 2017-01-05 2018-01-05

reprex package(v0.2.1)于2018-10-11创建

答案 1 :(得分:1)

好的。看一下RcppExamples软件包和source repository,其中有

实际上,您可以通过使用向量成员函数attr()更改class属性来实现。但是构造新向量的简单方法也应该起作用。