Как округлить только числовые значения в кадре данных? - PullRequest
2 голосов
/ 20 июня 2019

Я пытаюсь округлить все числовые значения в моем фрейме данных.

Проблема в том, что мой фрейм данных также содержит строки, а не только в каком-либо конкретном столбце или строке. Я хочу избежать необходимости кодировать цикл, в котором я прохожу каждую отдельную пару ячеек строки-столбца и проверяю, является ли значение числовым, перед округлением.

Есть ли функция (или комбинация функций), которая позволит мне достичь этого?

До сих пор я пробовал round_df() и различные комбинации lapply() и apply() с лямбдами. Тем не менее, я получил только то, где оно округляется на основе первого значения в столбце (то есть, если первое значение является числовым, оно обрабатывает весь столбец как числовое и только округляет его).

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

Моя функция:

library(readxl)
library(knitr)
library(gplots)
library(doBy)
library(dplyr)
library(plyr)
library(printr)   
library(xtable)   
library(gmodels)
library(survival)
library(pander)
library(psych)
library(questionr)
library(DT)
library(data.table)
library(expss)
library(xtable)
options(xtable.floating = FALSE)
options(xtable.timestamp = "")
library(kableExtra)
library(magrittr)
library(Hmisc)
library(forestmangr)
library(summarytools)
library(gmodels)
library(stats)

summaryTable <- function(y, bygroup, digit, 
                         title="", caption_heading="", caption="", freq.tab, y.label="",
                         y.names="", boxplot) {
  if (freq.tab) {
    m = multi.fun(y)
  }
  else if (!missing(bygroup)) {
    m = data.frame(y.label = "")
    m = merge(m, data.frame(describeBy(y, bygroup, mat = T)))
    m = select(m, y.label, n, mean, sd, min, median, max)
  }
  else {
    m = data.frame(y.label = "")
    m = merge(m, data.frame(sumconti(y)))
  }
  if (!freq.tab) {
    m$y.label = y.names
  }
  m = round_df(m, digit, "signif")
  if (freq.tab) {
    colnames(m) = c(y.label, "Frequency", "%")
  }
  else if (missing(freq.tab) | !freq.tab) {
    colnames(m) = c(y.label, "n", "Mean", "Std", "Min", "Median", "Max")
  }
  if (!missing(boxplot)) {
    if (boxplot) {
      attach(m)
      layout(matrix(c(1, 1, 2, 1)), 2, 1)
       
      kable(m, align = "c", "latex", booktabs = T, caption=figTitle(x, title, y.label)) %>% 
        kable_styling(position = 'center', 
                      latex_options = c("striped", "repeat_header", "hold_position")) %>% 
        footnote(general = caption, general_title = caption_heading, footnote_as_chunk = T, 
                 title_format = c("italic", "underline"), threeparttable = T)
      
      boxplot(y ~ bygroup, main = figTitle(y, title, y.label), names = y.names, ylab = title, 
              xlab = y.label, col = c("red", "blue", "orange", "pink", 
                                      "green", "purple", "grey", "yellow"), border = "black", 
              horizontal = F, varwidth = T)
    }
  }
  kable(m, 
        align = "c", 
        "latex", 
        booktabs = T, 
        caption = figTitle(x, title, y.label)) %>% 
    kable_styling(position = 'center', 
                  latex_options = c("striped", "repeat_header", "hold_position")) %>% 
    footnote(general = caption, 
             general_title = caption_heading, 
             footnote_as_chunk = T, 
             title_format = c("italic", "underline"), 
             threeparttable = T)
}


figTitle = function(x, title, y.label) {
  if (y.label != "") {
    paste("Summary of", title, "by", y.label)
  }
  else if (title != "") {
    paste("Summary of", title)
  }
  else {
    paste("")
  }
}

Ответы [ 2 ]

3 голосов
/ 20 июня 2019

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

Только для округления чисел

Если проблема в том, что у вас есть сочетание чисел и символов, и вы хотите только округлить число, то есть несколько способов.

1) Вычислить, какие столбцы являются числовыми, давая логический вектор ok, а затем округлить их.Мы используем встроенный набор данных Пуромицин в качестве примера.Пакеты не используются.

ok <- sapply(Puromycin, is.numeric)
replace(Puromycin, ok, round(Puromycin[ok], 1))

, давая:

   conc rate     state
1   0.0   76   treated
2   0.0   47   treated
3   0.1   97   treated
4   0.1  107   treated
5   0.1  123   treated
6   0.1  139   treated
...etc...

1a) Последняя строка также может быть написана так, если вы не возражаете перезаписать ввод.

Puromycin[ok] <- round(Puromycin[ok], 1)

2) Другой подход заключается в выполнении условия в lapply

Round <- function(x, k) if (is.numeric(x)) round(x, k) else x
replace(Puromycin, TRUE, lapply(Puromycin, Round, 1))

2a) или с перезаписью:

Puromycin[] <- lapply(Puromycin, Round, 1)

Округлить все

Если проблема заключается в том, что все столбцы должны быть числовыми, но некоторые из них на самом деле являются символьными, хотя они представляют собой числа, тогда, используя указанный кадр данных в качестве примера, применитеtype.convert.

# create test data having numeric, character and factor columns but
# all intended to represent numbers
DF <- structure(list(Time = c("0.1", "0.12", "0.3", "0.14", "0.5", 
"0.7"), demand = c(0.83, 1.03, 1.9, 1.6, 1.56, 1.98), Time2 = structure(c(1L, 
2L, 4L, 3L, 5L, 6L), .Label = c("0.1", "0.12", "0.14", "0.3", 
"0.5", "0.7"), class = "factor")), class = "data.frame", row.names = c(NA, 
-6L))

round(replace(DF, TRUE, lapply(DF, type.convert)), 1)
1 голос
/ 20 июня 2019

Чтобы добавить одну последнюю возможность к вышеуказанным опциям:

Предположим, у вас есть символьные столбцы, которые также содержат (не только) числа, но в строковом формате.Тогда следующий подход может помочь.

library(dplyr)
library(purrr)

# I use the data from above's answer with an additional mixed column
DF <- structure(
  list(
    Time = c("0.1", "0.12", "0.3", "0.14", "0.5",
             "0.7"),
    demand = c(0.83, 1.03, 1.9, 1.6, 1.56, 1.98),
    Mix = c("3.38", "4.403", "a", "5.34", "c", "9.32"),
    Time2 = structure(
      c(1L,
        2L, 4L, 3L, 5L, 6L),
      .Label = c("0.1", "0.12", "0.14", "0.3",
                 "0.5", "0.7"),
      class = "factor"
    )
  ),
  class = "data.frame",
  row.names = c(NA,-6L)
)

TBL <- as_tibble(DF)

# This are the functions we use
round_string_number <- function(x) {
  ifelse(!is.na(as.double(x)),
         as.character(round(as.double(x), digit = 1)),
         x)
}

round_string_factor <- compose(round_string_number, as.character)

# Here the recode is happening
TBL %>%
  mutate_if(is.numeric, ~ round(., digit = 1)) %>% 
  mutate_if(is.factor, round_string_factor) %>% 
  mutate_if(~!is.numeric(.), round_string_number)

Это превратит эти данные

  Time  demand Mix   Time2
  <chr>  <dbl> <chr> <fct>
1 0.1     0.83 3.38  0.1  
2 0.12    1.03 4.403 0.12 
3 0.3     1.9  a     0.3  
4 0.14    1.6  5.34  0.14 
5 0.5     1.56 c     0.5  
6 0.7     1.98 9.32  0.7  

в это:

  Time  demand Mix   Time2
  <chr>  <dbl> <chr> <chr>
1 0.1      0.8 3.4   0.1  
2 0.1      1   4.4   0.1  
3 0.3      1.9 a     0.3  
4 0.1      1.6 5.3   0.1  
5 0.5      1.6 c     0.5  
6 0.7      2   9.3   0.7 
...