如何在 R 中舍入包含一些字符变量的 data.frame?

Posted

技术标签:

【中文标题】如何在 R 中舍入包含一些字符变量的 data.frame?【英文标题】:How to round a data.frame in R that contains some character variables? 【发布时间】:2012-01-30 12:31:07 【问题描述】:

我有一个数据框,我希望对所有数字进行四舍五入(准备导出)。这一定很简单,但我遇到了问题,因为数据帧的某些位不是数字。例如,我想在下面的示例中将数字四舍五入到最接近的整数:

ID = c("a","b","c","d","e")
Value1 = c("3.4","6.4","8.7","1.1","0.1")
Value2 = c("8.2","1.7","6.4","1.9","10.3")
df<-data.frame(ID,Value1,Value2)

谁能帮帮我?我可以对单个列进行舍入(例如,round(df$Value1, 2)),但我想对包含一些非数字列的整个表进行舍入。

【问题讨论】:

舍入对“数字”有意义,而不是字符。您必须将Value1Value2 转换为numeric,例如round(as.numeric(Value1), 0) 可以完成这项工作,但您没有指定应该如何进行舍入(查找 truncceilingfloor 之一)。 【参考方案1】:

我认为现在最简洁的方法是使用dplyr

library(dplyr)
df %>% 
 mutate_if(is.numeric, round)

这将舍入数据框中的所有数字列

【讨论】:

太棒了! library(dplyr); df %&gt;% mutate_if(is.numeric, round, digits=3) mutate_if 已被across 取代!现在使用library(dplyr); df %&gt;% mutate(across(is.numeric, round, digits=3))【参考方案2】:

认识到这是一个老问题并且接受了一个答案,我想提供另一种解决方案,因为该问题在 Google 上显示为排名靠前的结果。

更通用的解决方案是创建一个单独的函数来搜索所有数值变量并将它们四舍五入到指定的位数:

round_df <- function(df, digits) 
  nums <- vapply(df, is.numeric, FUN.VALUE = logical(1))

  df[,nums] <- round(df[,nums], digits = digits)

  (df)

定义好后,可以如下使用:

> round_df(df, digits=3)

【讨论】:

我从未想过的出色、简单的解决方案!谢谢!【参考方案3】:

首先确保您的数字列是数字:

ID = c("a","b","c","d","e")
Value1 = as.numeric(c("3.4","6.4","8.7","1.1","0.1"))
Value2 = as.numeric(c("8.2","1.7","6.4","1.9","10.3"))
df<-data.frame(ID,Value1,Value2, stringsAsFactors = FALSE)

然后,只对数字列进行四舍五入:

df[,-1] <-round(df[,-1],0) #the "-1" excludes column 1
df

  ID Value1 Value2
1  a      3      8
2  b      6      2
3  c      9      6
4  d      1      2
5  e      0     10

【讨论】:

【参考方案4】:

我知道这是一个迟到的回复,但我也遇到了同样的问题。经过一番搜索,我发现这是最优雅的解决方案:

data.frame(lapply(x, function(y) if(is.numeric(y)) round(y, 2) else y)) 

解决方案最初来自: 让·V·亚当斯 统计员 美国地质调查局 大湖科学中心 223 东斯坦菲斯特路 美国威斯康星州安提戈 54409

http://r.789695.n4.nabble.com/round-a-data-frame-containing-character-variables-td3732415.html

【讨论】:

这是标准解决方案。【参考方案5】:

这是我喜欢使用的单线: (这会将round 函数仅应用于classes 参数中指定的类类型的列)

df2 <- rapply(object = df, f = round, classes = "numeric", how = "replace", digits = 0) 

【讨论】:

【参考方案6】:

其他答案并不能完全回答 OP 的问题,因为他们假设示例数据与 OP 提供的数据不同。

如果我们从字面上理解这个问题,并且我们想要一个通用的解决方案,该解决方案将找到其中包含数字的列(任何向量类型),将它们转换为数字,然后执行另一个数字运算,例如舍入。我们可以使用purrr:dmap 并这样做:

这是 OP 提供的数据,其中所有列都是因子(一个令人讨厌的默认值,但我们可以处理它):

ID = c("a","b","c","d","e")
Value1 = c("3.4","6.4","8.7","1.1","0.1")
Value2 = c("8.2","1.7","6.4","1.9","10.3")
df<-data.frame(ID,Value1,Value2)

str(df)
'data.frame':   5 obs. of  3 variables:
 $ ID    : Factor w/ 5 levels "a","b","c","d",..: 1 2 3 4 5
 $ Value1: Factor w/ 5 levels "0.1","1.1","3.4",..: 3 4 5 2 1
 $ Value2: Factor w/ 5 levels "1.7","1.9","10.3",..: 5 1 4 2 3

我们将搜索包含数字的列,并制作一个索引数据框来标记数字:

library(dplyr)
library(purrr)

df_logical <- 
df %>% 
  dmap(function(i) grepl("[0-9]", i))

df_logical
     ID Value1 Value2
1 FALSE   TRUE   TRUE
2 FALSE   TRUE   TRUE
3 FALSE   TRUE   TRUE
4 FALSE   TRUE   TRUE
5 FALSE   TRUE   TRUE

str(df_logical)
'data.frame':   5 obs. of  3 variables:
 $ ID    : logi  FALSE FALSE FALSE FALSE FALSE
 $ Value1: logi  TRUE TRUE TRUE TRUE TRUE
 $ Value2: logi  TRUE TRUE TRUE TRUE TRUE

然后我们可以使用这些索引来选择原始数据框中的 cols 的子集并将它们转换为数字,并且还可以做其他事情(在这种情况下,舍入):

df_numerics <- 
map(1:ncol(df), function(i) ifelse(df_logical[,i], 
                                      as.numeric(as.character(df[,i])), 
                                      df[,i])) %>% 
  dmap(round, 0) %>% 
  setNames(names(df)) 

我们得到了想要的结果:

df_numerics
  ID Value1 Value2
1  1      3      8
2  2      6      2
3  3      9      6
4  4      1      2
5  5      0     10

str(df_numerics)
'data.frame':   5 obs. of  3 variables:
 $ ID    : num  1 2 3 4 5
 $ Value1: num  3 6 9 1 0
 $ Value2: num  8 2 6 2 10

这在具有大量列的数据框的情况下可能很有用,并且我们有许多字符/因子类型的列,其中充满了我们想要作为数字的数字,但是手工操作太乏味了。

【讨论】:

【参考方案7】:

上面的答案指出了最初问题中的几个绊脚石,这使得它比仅仅四舍五入更复杂,主要是:

    数字是作为字符输入的,并且 data.frame() 默认将字符数转换为因数

Ben 的回复详细说明了如何处理这些问题,并申请了purrr::dmap()purrr 包已被修改,dmap 函数已被弃用(支持map_df())。 还有一个较新的函数modify_if()可以解决多个数字列四舍五入的问题,所以我想更新这个答案。


我将以数字形式输入数据,并添加更多数字以使示例更广泛地适用:

df <- data.frame(ID = c("a","b","c","d","e"), 
                 Value1 =c(3.4532897,6.41325,8.71235,1.115,0.115), 
                 Value2 = c(8.2125,1.71235,6.4135,1.915,10.3235))

使用purrr::modify_if() 函数:

purrr::modify_if(df, ~is.numeric(.), ~round(., 0))

  ID Value1 Value2
1  a      3      8
2  b      6      2
3  c      9      6
4  d      1      2
5  e      0     10

只需将 round(digits= 0) 更改为适当的小数位

modify_if(df, ~is.numeric(.), ~round(., 2))
  ID Value1 Value2
1  a   3.45   8.21
2  b   6.41   1.71
3  c   8.71   6.41
4  d   1.12   1.92
5  e   0.12  10.32

有关语法的更多文档,请参阅http://purrr.tidyverse.org/

这也可以使用基本 R 应用函数分两步完成,即为列创建索引 (numVars),然后通过标准索引仅修改这些列:

numVars <-  sapply(df, is.numeric)
   ID Value1 Value2 
FALSE   TRUE   TRUE 

df[, numVars] <- lapply(df[, numVars], round, 0)
df
  ID Value1 Value2
1  a      3      8
2  b      6      2
3  c      9      6
4  d      1      2
5  e      0     10

【讨论】:

请注意 numVars &lt;- apply(df, 2, is.numeric) 失败,因为它将数据帧强制转换为矩阵(将所有列转换为相同的类型字符)。 sapply() 不会这样做。 修改如果当前 purrr 包中似乎不存在。下载并加载 purrr 包后,我收到提示未找到 modify_if 的错误 @Mark hmmm...我刚刚检查过,它在当前的 CRAN 版本中(purrr 0.2.3)。我稍微编辑了命令,所以它是purrr::modify_if,这样你就不必加载包了。看看有没有帮助。 @Mark 它看起来也像是在当前的 Github 版本中。然而,tidyverse 现在变化很大,以至于我目前主要坚持使用 CRAN。【参考方案8】:

请注意,上面提出的一些解决方案不考虑行名,这意味着它们丢失了。

例如,试试:

df <- data.frame(v1 = seq(1.11, 1.20, 0.01), v2 = letters[1:10])
row.names(df) = df$v2

然后,按照上面的建议,尝试:

data.frame( lapply(df, function(y) if(is.numeric(y)) round(y, 2) else y) ) 

请注意,行名不再存在。

Akhmed 的建议保留行名,因为它适用于替换。

【讨论】:

【参考方案9】:

为什么不直接使用 ID 作为行名?

...并从value1和value2数据中取出“”

试试这个:

ID = c("a","b","c","d","e")
Value1 = c(3.4,6.4,8.7,1.1,0.1)
Value2 = c(8.2,1.7,6.4,1.9,10.3)

df<-data.frame(ID,Value1,Value2,row.names=TRUE)

> df
  Value1 Value2
a    3.4    8.2
b    6.4    1.7
c    8.7    6.4
d    1.1    1.9
e    0.1   10.3

> str(df)
'data.frame':   5 obs. of  2 variables:
 $ Value1: num  3.4 6.4 8.7 1.1 0.1
 $ Value2: num  8.2 1.7 6.4 1.9 10.3

我不确定你想用这一轮做什么,但你在 R 中有一些选择:

?ceiling()
?floor()
?trunc()

【讨论】:

如果您知道要舍入哪些列并已转换,您也可以执行df[,c('Value1','Value2')] &lt;- round(as.numeric(df[,c('Value1','Value2')]))(如果有很多文本列但只有少数可以设为数字,这可能是可取的)。 此外,如果您只想查找数字列和四舍五入,您可以使用df[,sapply(df, is.numeric)] &lt;-round(df[,sapply(df, is.numeric)],0) 谢谢泰勒!这正是我想要的——你节省了我很多时间!!【参考方案10】:

这是另一种选择。这个函数可以很容易地指定实际的舍入函数并接受每一列的唯一数字值:

rounddf <- function(x, digits = rep(2, ncol(x)), func = round) 
  if (length(digits) == 1) 
    digits <- rep(digits, ncol(x))
   else if (length(digits) != ncol(x)) 
    digits <- c(digits, rep(digits[1], ncol(x) - length(digits)))
    warning('First value in digits repeated to match length.')
  

  for(i in 1:ncol(x)) 
    if(class(x[, i])[1] == 'numeric') x[, i] <- func(x[, i], digits[i])
  

  return(x)

它已在https://github.com/sashahafner/jumbled 发布(有时会更新)

【讨论】:

以上是关于如何在 R 中舍入包含一些字符变量的 data.frame?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 JavaScript 中舍入一个数字? .toFixed() 返回一个字符串?

如何在包含浮点数和 Nan 或 None 值的同一列中舍入浮点值?

如何在电报中舍入输出值?

如何在 MySQL 中舍入 DateTime?

如何在动作脚本中舍入数字

如何在 Datagridview 中舍入 double 类型的值