R: преобразование дробей в десятичные во фрейме данных

Я пытаюсь преобразовать фрейм данных чисел, хранящихся в виде символов в дробной форме, для хранения в виде чисел в десятичной форме. (Есть также некоторые целые числа, также сохраненные как char.) Я хочу сохранить текущую структуру фрейма данных, т.е. мне не нужен список в результате.

Пример фрейма данных (примечание: реальный фрейм данных имеет все элементы как символы, здесь это фактор, но я не мог понять, как реплицировать фрейм данных с символами):

    a <- c("1","1/2","2")
    b <- c("5/2","3","7/2")
    c <- c("4","9/2","5")
    df <- data.frame(a,b,c)

Я пробовал df[] <- apply(df,1, function(x) eval(parse(text=x))). Это правильно вычисляет числа, но только для последнего столбца, заполняя фрейм данных этим.

Результат:

   a  b    c
1  4  4.5  5
2  4  4.5  5
3  4  4.5  5

Я также попробовал df[] <- lapply(df, function(x) eval(parse(text=x))), который дал следующий результат (и я понятия не имею, почему):

   a  b  c
1  3  3  2
2  3  3  2
3  3  3  2

Желаемый результат:

   a   b    c
1  1   2.5  4
2  0.5 3    4.5
3  2   3.5  5

Большое спасибо!


person fredibeni    schedule 23.02.2018    source источник


Ответы (3)


Вероятно, вы ищете:

df[] <- apply(df, c(1, 2), function(x) eval(parse(text = x)))
df
    a   b   c
1 1.0 2.5 4.0
2 0.5 3.0 4.5
3 2.0 3.5 5.0

eval(parse(text = x))

оценивает одно выражение за раз, поэтому вам нужно запускать ячейку за ячейкой.

РЕДАКТИРОВАТЬ: если некоторые элементы фрейма данных не могут быть оценены, вы можете учесть это, добавив оператор ifelse внутри функции:

df[] <- apply(df, c(1, 2), function(x) if(x %in% skip){NA} else {eval(parse(text = x))}) 

Где skip — это вектор элемента, который не должен оцениваться.

person missuse    schedule 23.02.2018
comment
да, это была опечатка, уже удалил комментарий. извините за путаницу. ваше решение отлично работает на примере df, к сожалению, ни одно из решений пока не работает с моими реальными данными. В итоге я получаю правильные десятичные дроби из первого столбца, неоднократно вставленные боком в строки. - person fredibeni; 24.02.2018
comment
Я уже собирался, но при сокращении понял, что в самом конце осталась одна пустая колонка. После его удаления ваш код запускается волшебным образом. Большое спасибо за Вашу помощь - person fredibeni; 24.02.2018
comment
помимо пустых столбцов, которые я удалил, иногда встречаются случайные пустые ячейки (), которые, кажется, искажают результат функции. Не могли бы вы помочь, как изменить функцию, чтобы она по-прежнему работала, когда в кадре данных есть такие ячейки? Спасибо - person fredibeni; 25.02.2018
comment
вы можете добавить оператор if else, например: function(x) if(x %in% dont_eval) {NA} else {eval(parse(text = x)))}, и определить vactor dont_eval, содержащий c(NA_character_, "") и все, что мешает вашим функциям. Я могу добавить это в свой пост, если вы можете предоставить новый пример фрейма данных с проблемными значениями. - person missuse; 25.02.2018
comment
благодарю вас!! df[] <- apply(df, c(1, 2), function(x) if(x=="") {NA} else {eval(parse(text = x))}) прекрасно работает как окончательное решение. Я все еще только учусь кодировать, поэтому ваша помощь действительно ценится - person fredibeni; 25.02.2018
comment
рад, что смог помочь, если вы нашли какой-либо ответ на свой вопрос полезным, рассмотрите возможность пометить его как принятый. Подробнее здесь - person missuse; 25.02.2018

  1. Во-первых, вы должны предотвратить превращение ваших персонажей в факторы в data.frame()

    df ‹- data.frame(a, b, c, stringsAsFactors = F)

    Затем вы можете обернуть простой sapply/lapply внутри вашего lapply, чтобы добиться того, чего вы хотите.

    sapply(X = df, FUN = function(v) {
                                  sapply(X = v,
                                         FUN = function(w) eval(parse(text=w)))
                                 }
      )
    

    Примечания

  2. Если вы передадите eval неправильное выражение, такое как expression(1, 1/2, 2), оно оценивается как последнее значение. Это объясняет вывод 4 4.5 5. Правильный expression(c(1, 1/2, 2)) соответствует ожидаемому ответу.

  3. Код lapply(df, function(x) eval(parse(text=x))) возвращает 3 3 2, потому что sapply(data.frame(a,b,c), as.numeric) возвращает:

         a b c
    [1,] 1 2 1
    [2,] 2 1 3
    [3,] 3 3 2
    

    Эти числа соответствуют levels() факторам, с помощью которых вы хранили свои дроби.

person Vlo    schedule 23.02.2018
comment
Ваши решения отлично работают, и ответ также был очень познавательным. Большое спасибо - person fredibeni; 24.02.2018

Для тех, кто ищет однострочник: вы можете использовать parse_ratio из пакета DOSE, чтобы преобразовать дроби символов в числовые.

library(DOSE)

b <- c("5/2","3","7/2")
parse_ratio(b)
[1] 2.5 1.0 3.5
person Stefan Petkov    schedule 15.03.2021