Преобразование цикла for с t (apply ()) из R в C ++ для использования с Rcpp - PullRequest
1 голос
/ 10 июня 2019

Я пытаюсь преобразовать цикл for, написанный на R, в C ++ для использования с Rcpp; в частности, функция типа apply с транспонированием.

Функция берет файл .gen и преобразует его в аллели:

Я прочитал "Начало работы Ника" с Rcpp и большую часть Масаки Э. Цуда Rcpp4Everyone и https://thecoatlessprofessor.com/programming/unofficial-rcpp-api-documentation/#vmld, чтобы узнать, где я сейчас нахожусь.

Это код R:

library(tidyverse)

geno <- data.frame(x1 = c(1,1,1),
                 x2 = c("rs001", "rs002", "rs003"),
                 x3 = c(224422,225108,225167),
                 x4 = c("T","A", "G"),
                 x5 = c("C", "C", "A"),
                 x6 = c(1,1,1),
                 x7 = c(0,0,0),
                 x8 = c(0,0,0),
                 x9 = c(1,0,1),
                 x10 = c(0,1,0),
                 x11 = c(0,0,0),
                 stringsAsFactors = F)

# What I'd like to turn into C++
geno_to_alleles <- function(geno) {
        # Pre-allocate final output - always initialize output variable to required length and data type
        tmp = matrix(nrow = (ncol(geno)-5)/3, ncol = nrow(geno), byrow= T)
        #j is subject index
        j =1
        for (i in seq(from=6,to=ncol(geno), by=3)){
                tmp[j,1:nrow(geno)] <- t(apply(geno[, i:(i+2)], 1, paste, collapse = ""))
                j = j + 1
        }
        return(tmp)
}

df_out <- geno_to_alleles(df)

В результате получается matrix, который выглядит следующим образом:

     [,1]  [,2]  [,3] 
[1,] "100" "100" "100"
[2,] "100" "010" "100"

Пока у меня есть следующий код C ++, который читает DataFrame и создает ComplexMatrix объект, который будет варьироваться в зависимости от размера ввода DataFrame.

Мне нужна помощь в переносе следующего кода в C ++ tmp[j,1:nrow(geno)] <- t(apply(geno[, i:(i+2)], 1, paste, collapse = "")):

#include <Rcpp.h>
using namespace Rcpp;

// [[Rcpp::export]]
DataFrame modifyDataFrame(DataFrame df) {
        int input_rows = df.nrow(); // output
        int input_cols = df.ncol();
        Rcout << "Input DataFrame df has " << input_rows << " rows and "  << input_cols << " columns." << std::endl;

        int total_rows = (input_cols-5)/3;

        ComplexMatrix tmp(total_rows, input_rows);
        Rcout << "Output ComplexMatrix tmp has " << total_rows << " rows and "  << input_rows << " columns." << std::endl;


        // Below needs to be transpiled into C++
        //tmp[j,1:nrow(df)] <- t(apply(df[, i:(i+2)], 1, paste, collapse = ""))

        // return the new data frame
        return tmp;
}

1 Ответ

2 голосов
/ 11 июня 2019

Вы можете сделать это, используя комбинацию std::to_string() и +. У нас есть следующий код C ++:

#include <Rcpp.h>

// [[Rcpp::export]]
Rcpp::CharacterMatrix geno_to_alleles_cpp(Rcpp::DataFrame x) {
    // Set up result object
    int n = x.nrow();
    int m = x.ncol();
    Rcpp::CharacterMatrix result( (m - 5) / 3, n );

    // We'll loop over columns in x, at the same time going over rows in result
    for ( int i = 0, j = 5; j < m; ++i, j += 3 ) {
        Rcpp::IntegerVector x1 = Rcpp::as<Rcpp::IntegerVector>(x[j]);
        Rcpp::IntegerVector x2 = Rcpp::as<Rcpp::IntegerVector>(x[j + 1]);
        Rcpp::IntegerVector x3 = Rcpp::as<Rcpp::IntegerVector>(x[j + 2]);
        // Then we go over the columns in result / rows in x
        for ( int k = 0; k < n; ++k ) {
            result(i, k) = std::to_string(x1[k]) + std::to_string(x2[k])
                           + std::to_string(x3[k]);
        }
    }

    return result;
}

Что выполняет то, что мы хотим:

geno <- data.frame(x1 = c(1,1,1),
                   x2 = c("rs001", "rs002", "rs003"),
                   x3 = c(224422,225108,225167),
                   x4 = c("T","A", "G"),
                   x5 = c("C", "C", "A"),
                   x6 = c(1,1,1),
                   x7 = c(0,0,0),
                   x8 = c(0,0,0),
                   x9 = c(1,0,1),
                   x10 = c(0,1,0),
                   x11 = c(0,0,0),
                   stringsAsFactors = F)

geno_to_alleles <- function(geno) {
    # Pre-allocate final output - always initialize output variable to required length and data type
    tmp = matrix(nrow = (ncol(geno)-5)/3, ncol = nrow(geno), byrow= T)
    #j is subject index
    j =1
    for (i in seq(from=6,to=ncol(geno), by=3)){
        tmp[j,1:nrow(geno)] <- t(apply(geno[, i:(i+2)], 1, paste, collapse = ""))
        j = j + 1
    }
    return(tmp)
}

Rcpp::sourceCpp("geno_to_alleles_cpp.cpp")
geno_to_alleles(geno)
#      [,1]  [,2]  [,3] 
# [1,] "100" "100" "100"
# [2,] "100" "010" "100"
geno_to_alleles_cpp(geno)
#      [,1]  [,2]  [,3] 
# [1,] "100" "100" "100"
# [2,] "100" "010" "100"

И, по крайней мере, с этими данными, намного быстрее, чем база R (я не проверял, как это масштабируется):

library(microbenchmark)
microbenchmark(base = geno_to_alleles(geno), rcpp = geno_to_alleles_cpp(geno))

Unit: microseconds
 expr      min        lq       mean    median        uq      max neval
 base 1296.948 1305.4190 1328.34660 1316.4780 1340.8675 1573.943   100
 rcpp   33.893   35.5445   77.57828   38.9405   41.0365 3851.134   100
...