使用 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
我有一个非常大的数据框(大约 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