使用 ifelse 仅舍入数值变量

Rounding only numeric variables with ifelse

我有一个非常大的数据框(大约 100 行,200 列)。我的数据子集如下所示:

example <- data.frame("Station" = c("012", "013", "014"), "Value1" = c(145.23453, 1.022342, 0.4432), 
"Value2" = c(2.1221213, 4445.2231412, 0.3333421), "Name" = c("ABC", "SDS", "EFG"))

我想根据这些条件对 table 中的所有数值变量进行舍入。

如果 x<1,则 1 sig fig

如果 1<= x < 99,则 2 个数字

如果 x>= 100,则 3 个 sig figs

我知道为特定专栏做这样的事情:

example$Value1 <- ifelse(example$Value1 < 1, signif(example$Value1, 1), example$Value1)

但我不确定如何处理混合了数字和字符值的大型数据框。

只需将 ifelse 放入 lapply。要识别数字列,​​请在 sapply 中使用否定 is.character。您还可以 Vectorize 一个小的替换 FUN 函数,其中包含您想要在 lapply 中使用的所有条件,这可能很方便。但是,请注意@GKi 的评论,您的条件不完整。

nums <- sapply(example, is.numeric)

FUN <- Vectorize(function(x) {
  if (x < 1) x <- signif(x, 1)
  if (1 <= x & x < 99) x <- signif(x, 2)
  if (x >= 100) x <- signif(x, 3)
  x
})

example[nums] <- lapply(example[nums], FUN)
#   Station Value1 Value2 Name
# 1     012  145.0    2.1  ABC
# 2     013    1.0 4450.0  SDS
# 3     014    0.4    0.3  EFG

使用apply并嵌套ifelse:

如果您事先不知道哪些列是数字,并且想保留原始数据框:

example[sapply(example, is.numeric)] <- apply(example[sapply(example, is.numeric)], 2, 
                                              function(x) ifelse(x < 1, signif(x, 1), 
                                                                 ifelse(x >= 1 & x < 99 , signif(x, 2), signif(x, 3))))
example
  Station Value1 Value2 Name
1     012  145.0    2.1  ABC
2     013    1.0 4450.0  SDS
3     014    0.4    0.3  EFG

我将使用 data.table 而不是 data.frame 给出答案,因为它更好,而且我不再记得那么清楚的 data.frame 语法。

library(data.table)

example = data.table(
  Station = c("012", "013", "014"),
  Value1 = c(145.23453, 1.022342, 0.4432),
  Value2 = c(2.1221213, 4445.2231412, 0.3333421),
  Name = c("ABC", "SDS", "EFG"))

numeric_colnames = names(example)[sapply(example,is.numeric)]

for(x in numeric_colnames){
  example[,(x):=ifelse(
    get(x)<1,
    signif(get(x),1),
    ifelse(
      get(x)<99,
      signif(get(x),2),
      signif(get(x),3)
  ))]
}

结果:

   Station Value1 Value2 Name
1:     012  145.0    2.1  ABC
2:     013    1.0 4450.0  SDS
3:     014    0.4    0.3  EFG

PS:不用担心145.0和4450.0;这是显示问题,不是数据问题:

> example[,as.character(Value1)]
[1] "145" "1"   "0.4"
> example[,as.character(Value2)]
[1] "2.1"  "4450" "0.3"

PPS:截断值 99 会产生一些奇怪的结果,例如,

> signif(98.9,2)
[1] 99
> signif(99.1,3)
[1] 99.1

为什么不使用 100 作为截止值呢?

> signif(99.4,2)
[1] 99
> signif(99.5,2)
[1] 100
> signif(100.1,3)
[1] 100

代码

example %>%
  pivot_longer(contains("Value")) %>%
  mutate(
    signf = case_when(
      value < 1 ~ 1,
      value >= 1 & value < 99 ~ 2,
      TRUE ~ 3
    ),
    value = map2_dbl(value, signf, ~signif(.x, .y))
  ) %>%
  select(-signf) %>%
  pivot_wider(names_from = "name", values_from = "value")

输出

# A tibble: 3 x 4
  Station Name  Value1 Value2
  <fct>   <fct>  <dbl>  <dbl>
1 012     ABC    145      2.1
2 013     SDS      1   4450  
3 014     EFG      0.4    0.3

您可以使用findInterval设置signif:

i <- sapply(example, is.numeric)
x <- unlist(example[,i])
example[,i] <- signif(x, findInterval(x, c(1, 99))+1)
example
#  Station Value1 Value2 Name
#1     012  145.0    2.1  ABC
#2     013    1.0 4450.0  SDS
#3     014    0.4    0.3  EFG

findIntervall 结果来自 @webb(谢谢!)评论中给出的示例:

findInterval(c(145.23453, 1.022342, 0.4432, 2.1221213, 4445.2231412
 , 0.3333421), c(1, 99))
#[1] 2 1 0 1 2 0