Простая функция Rcpp с try catch, возвращающим ошибку 'memory not mapped' - PullRequest
0 голосов
/ 23 октября 2018

Фон

Функция имеет простую задачу: перебирать фактор-элементы и пытаться преобразовать каждый элемент в double, integer и, наконец, оставить его как символ.При каждом подсчете увеличивается соответствующий счетчик.В конце возвращается строка, соответствующая наибольшему счетчику.

Обоснование

В основном это обучающий пример.Я наткнулся на грязную структуру данных. Некоторые данные, которые я хочу использовать, сохраняются как факторы.Переменные в действительности имеют значения типа double, целые числа или строки.Я хочу привести их к этим типам.Есть лучшие способы сделать это в базе R, но эта проблема выглядит как хорошая возможность узнать больше .

Код

#include <Rcpp.h>

// [[Rcpp::plugins(cpp11)]]

//' @title Guess Vector Type
//'
//' @description Function analyses content of a factor vector and attempts to
//'   guess the correct type.
//'
//' @param x A vector of factor class.
//'
//' @return A scalar string with class name.
//'
//' @export
//'
// [[Rcpp::export]]
Rcpp::String guess_vector_type(Rcpp::IntegerVector x) {

    // Define counters for all types
    int num_doubles = 0;
    int num_integers = 0;
    int num_strings = 0;

    // Converted strings
    double converted_double;
    int converted_integer;


    // Get character vector with levels
    Rcpp::StringVector levels = x.attr("levels");
    // Get integer vector with values
    // Rcpp::String type = x.sexp_type();
    // Returns integer vector type
    // Use iterator: https://teuder.github.io/rcpp4everyone_en/280_iterator.html
    for(Rcpp::IntegerVector::iterator it = x.begin(); it != x.end(); ++it) {
        // Get [] for vector element
        int index = std::distance(x.begin(), it);
        // Get value of a specific vector element
        int element = x[index];
        // Convert to normal string
        std::string temp = Rcpp::as<std::string>(levels[element]);
        // Try converting to an integer
        try
        {
            converted_integer = std::stoi(temp);
        }
        catch(...)
        {
            // Try converting to a doubke
            try
            {
                // Convert to ineteges
                converted_double = std::stod(temp);
            }
            catch(...)
            {
                ++num_integers;
            }
            ++num_doubles;
        }
        ++num_strings;

    }

    // Get max value of three variables
    // https://stackoverflow.com/a/2233412/1655567
    int max_val;
    max_val = num_doubles > num_integers? (num_doubles > num_strings? num_doubles: num_strings): (num_integers > num_strings? num_integers: num_strings);

    // Create results storage
    Rcpp::String res;


    // Check which value is matching max val
    if (max_val == num_doubles) {
        // Most converted to doubles
        res = "double";

    } else if (max_val == num_integers) {
        res = "integer";
    } else {
        res = "character";
    }

    // Return results vector
    return res;
}

Тесты

test_factor <- as.factor(rep(letters, 3))

Должен возвращать скалярную строку "character".

Ошибка

guess_vector_type(test_factor)

 *** caught segfault ***
address 0xe1000013, cause 'memory not mapped'

Я понимаю, что это похоже на проблему , обсуждаемую здесь , но мне не ясно, где ошибка.


Обновления

После комментариев я обновил функцию:

Rcpp::String guess_vector_type(Rcpp::IntegerVector x) {

    // Define counters for all types
    int num_doubles = 0;
    int num_integers = 0;
    int num_strings = 0;

    // Converted strings
    double converted_double;

    // flag for runnig more tests
    bool is_number;

    // Get character vector with levels
    Rcpp::StringVector levels = x.attr("levels");
    // Get integer vector with values
    // Rcpp::String type = x.sexp_type();
    // Returns integer vector type
    // Use iterator: https://teuder.github.io/rcpp4everyone_en/280_iterator.html
    for(Rcpp::IntegerVector::iterator it = x.begin(); it != x.end(); ++it) {
        // Get [] for vector element
        int index = std::distance(x.begin(), it);
        // Get value of a specific vector element
        int element = x[index];
        // Convert to normal string
        std::string temp = Rcpp::as<std::string>(levels[element - 1]);

        // Reset number checking flag
        is_number = 1;

        // Attempt conversion to double
        try {
            converted_double = std::stod(temp);
            } catch(...) {
                // Conversion failed, increase string count
                ++num_strings;
                // Do not run more test
                is_number = 0;
            }

        // If number run more tests
        if (is_number == 1) {
            // Check if converted string is an integer
            if(floor(converted_double) == converted_double) {
                // Increase counter for integer
                ++num_integers;
            } else {
                // Increase count for doubles
                ++num_doubles;
            }
        }
    }

    // Get max value of three variables
    // https://stackoverflow.com/a/2233412/1655567
    int max_val;
    max_val = num_doubles > num_integers? (num_doubles > num_strings? num_doubles: num_strings): (num_integers > num_strings? num_integers: num_strings);

    // Create results storage
    Rcpp::String res;


    // Check which value is matching max val
    if (max_val == num_doubles) {
        // Most converted to doubles
        res = "double";

    } else if (max_val == num_integers) {
        res = "integer";
    } else {
        res = "character";
    }

    // Return results vector
    return res;
}
Тесты
>> guess_vector_type(x = as.factor(letters))
[1] "character"
>> guess_vector_type(as.factor(1:10))
[1] "integer"
>> guess_vector_type(as.factor(runif(n = 1e3)))
[1] "double"

1 Ответ

0 голосов
/ 23 октября 2018

Проблема, вызывающая ваш segfault, связана с этой строкой

std::string temp = Rcpp::as<std::string>(levels[element]);

Так как R индексируется 1, вам нужно

std::string temp = Rcpp::as<std::string>(levels[element - 1]);

Однако я также заметил, что вы увеличиваете свои счетчики внеправильное место (вам нужно увеличивать строку в самом внутреннем элементе catch и integer вне элементов catch) и требовать операторов продолжения после приращений (в противном случае вы в конечном итоге будете делать неприменимые приращения в дополнение к тому, который вы хотите сделать).Как только вы исправите эти вещи, код запускается в тестовом примере, как и ожидалось (но посмотрите в конце обновления, касающиеся двойных и целых чисел).

guess_vector_type(test_factor)
# [1] "character"

Полный рабочий код

#include <Rcpp.h>

// [[Rcpp::plugins(cpp11)]]

//' @title Guess Vector Type
//'
//' @description Function analyses content of a factor vector and attempts to
//'   guess the correct type.
//'
//' @param x A vector of factor class.
//'
//' @return A scalar string with class name.
//'
//' @export
//'
// [[Rcpp::export]]
Rcpp::String guess_vector_type(Rcpp::IntegerVector x) {

    // Define counters for all types
    int num_doubles = 0;
    int num_integers = 0;
    int num_strings = 0;

    // Converted strings
    double converted_double;
    int converted_integer;


    // Get character vector with levels
    Rcpp::StringVector levels = x.attr("levels");
    // Get integer vector with values
    // Rcpp::String type = x.sexp_type();
    // Returns integer vector type
    // Use iterator: https://teuder.github.io/rcpp4everyone_en/280_iterator.html
    for(Rcpp::IntegerVector::iterator it = x.begin(); it != x.end(); ++it) {
        // Get [] for vector element
        int index = std::distance(x.begin(), it);
        // Get value of a specific vector element
        int element = x[index];
        // Convert to normal string
        std::string temp = Rcpp::as<std::string>(levels[element - 1]);
        // Try converting to an integer
        try
        {
            converted_integer = std::stoi(temp);
        }
        catch(...)
        {
            // Try converting to a doubke
            try
            {
                // Convert to ineteges
                converted_double = std::stod(temp);
            }
            catch(...)
            {
                ++num_strings;
                continue;
            }
            ++num_doubles;
            continue;
        }
        ++num_integers;
    }

    // Get max value of three variables
    // https://stackoverflow.com/a/2233412/1655567
    int max_val;
    max_val = num_doubles > num_integers? (num_doubles > num_strings? num_doubles: num_strings): (num_integers > num_strings? num_integers: num_strings);

    // Create results storage
    Rcpp::String res;


    // Check which value is matching max val
    if (max_val == num_doubles) {
        // Most converted to doubles
        res = "double";

    } else if (max_val == num_integers) {
        res = "integer";
    } else {
        res = "character";
    }

    // Return results vector
    return res;
}

Updates

Я попробовал это на еще нескольких примерах и обнаружил, что он не работает так, как ожидалось для двойников, поскольку программа способна конвертировать "42.18" в целое число (например).Он четко различает целые / двойные числа и символы, хотя:

test_factor <- as.factor(rep(letters, 3))
guess_vector_type(test_factor)
# [1] "character"

test_factor <- as.factor(1:3)
guess_vector_type(test_factor)
# [1] "integer"

test_factor <- as.factor(c(letters, 1))
guess_vector_type(test_factor)
# [1] "character"

test_factor <- as.factor(c(1.234, 42.1138, "a"))
guess_vector_type(test_factor)
# [1] "integer"

В любом случае, это совершенно отдельная проблема от проблемы, представленной в вопросе, по которой вы можете обратиться к этого стекаСообщение переполнения , например.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...