满足特定条件时更改列值

时间:2021-03-30 16:31:41

标签: r dataframe if-statement dplyr

我有一个问题,我想了一段时间,但找不到解决方案。

让我们考虑一些人工数据框,它是 0.9 和 0.1 分位数:

set.seed(42)
x = data.frame("Norm" = rnorm(100),
               "Unif" = runif(100),
               "Exp" = rexp(100))

quants_b <- apply(x, 2, quantile, 0.90)
quants_s <- apply(x, 2, quantile, 0.10)

我想在这个数据框中做的是检查哪些值大于其对应的分位数 0.9 和小于对应的分位数 0.1,并将所有这些值更改为限制。

简单来说:

我想检查哪些值超过 0.9 分位数,并且所有这些值都转换为 0.9 分位数

我想用 0.1 分位数做同样的事情。

问题的麻烦

我认为这个问题乍一看很容易,但它有一个陷阱——我们必须同时进行转换,因为如果我们先改变上限然后再降低,转换分位数之间可以改变。

(请注意,我们要用 quants_squants_b 的元素替换第一个变量,用第二个元素替换第二个变量,依此类推)。

我的想法

我的第一个想法是使用 dplyr 包和其中的函数 mutate_all

x %>% dplyr::mutate_all(
  function(x) {
    ifelse(sweep(x, 2,STATS=quants_s, `<`), quants_s,
           ifelse(sweep(x, 2,STATS=quants_b, `>`), 
                 quants_b, x)
    )
  }
)

这段代码直观上非常简单——我们只需将所有小于 quants_s 的值更改为 quants_s,将大于 quants_b 的值更改为 quants_b。其余数据保持不变。 但是我遇到了以下错误,我不知道如何省略它:

Error: Problem with `mutate()` input `Norm`.
x 'dims' cannot be of length 0
i Input `Norm` is `(function (x) ...`.
Run `rlang::last_error()` to see where the error occurred.

你能帮我解决这个问题/指出另一个解决方案吗?

3 个答案:

答案 0 :(得分:3)

我认为使用“钳位”方法中的 pminpmax 应该可以轻松解决此问题。

从预先计算限制开始:

quants <- apply(x, 2, quantile, c(0.1, 0.9))
quants
#          Norm       Unif       Exp
# 10% -1.211724 0.08499473 0.1257829
# 90%  1.372974 0.88512802 2.5315087

然后一步应用:

head(x, 15)
#           Norm       Unif       Exp  # outside bounds
# 1   1.37095845 0.88511769 0.7350033
# 2  -0.56469817 0.51711106 0.2718374
# 3   0.36312841 0.85193098 1.6570686
# 4   0.63286260 0.44279627 0.9729376
# 5   0.40426832 0.15788010 0.9210097
# 6  -0.10612452 0.44232464 2.4238688
# 7   1.51152200 0.96773367 2.5686363  # <-- Norm Unif Exp
# 8  -0.09465904 0.48458793 1.5920526
# 9   2.01842371 0.25245844 0.3064365  # <-- Norm
# 10 -0.06271410 0.25968998 0.2982843
# 11  1.30486965 0.54201594 1.2682549
# 12  2.28664539 0.64987584 1.5215655  # <-- Norm
# 13 -1.38886070 0.33641913 0.8123740  # <-- Norm
# 14 -0.27878877 0.06094975 0.1296444  # <--      Unif
# 15 -0.13332134 0.45131085 0.2484241

x[] <- Map(function(x, q1, q9) pmax(q1, pmin(q9, x)), x, quants[1,], quants[2,])
head(x, 15)
#           Norm       Unif       Exp
# 1   1.37095845 0.88511769 0.7350033
# 2  -0.56469817 0.51711106 0.2718374
# 3   0.36312841 0.85193098 1.6570686
# 4   0.63286260 0.44279627 0.9729376
# 5   0.40426832 0.15788010 0.9210097
# 6  -0.10612452 0.44232464 2.4238688
# 7   1.37297365 0.88512802 2.5315087  # <-- Norm Unif Exp
# 8  -0.09465904 0.48458793 1.5920526
# 9   1.37297365 0.25245844 0.3064365  # <-- Norm
# 10 -0.06271410 0.25968998 0.2982843
# 11  1.30486965 0.54201594 1.2682549
# 12  1.37297365 0.64987584 1.5215655  # <-- Norm
# 13 -1.21172411 0.33641913 0.8123740  # <-- Norm
# 14 -0.27878877 0.08499473 0.1296444  # <--      Unif
# 15 -0.13332134 0.45131085 0.2484241

答案 1 :(得分:1)

也许我遗漏了一些微妙的东西,但这里有一个使用 dplyr 的直接方法:

library(dplyr)
x %>%
  mutate(across(everything(), ~case_when(. > quantile(.,0.9) ~ quantile(.,0.9),
                                         . < quantile(.,0.1) ~ quantile(.,0.1),
                                         TRUE ~ .)))

这里我们可以看到效果:

x %>%
  mutate(across(everything(), ~case_when(. > quantile(.,0.9) ~ "High",
                                         . < quantile(.,0.1) ~ "Low",
                                         TRUE ~ ""),.names = "{.col}Δ")) %>%
  mutate(across(!contains("Δ"),~case_when(. > quantile(.,0.9) ~ quantile(.,0.9),
                                          . < quantile(.,0.1) ~ quantile(.,0.1),
                                          TRUE ~ .))) %>%
  select(sort(tidyselect::peek_vars())) %>%
  head(n=15)
#         Exp ExpΔ        Norm NormΔ       Unif UnifΔ
#1  0.7350033       1.37095845       0.88511769      
#2  0.2718374      -0.56469817       0.51711106      
#3  1.6570686       0.36312841       0.85193098      
#4  0.9729376       0.63286260       0.44279627      
#5  0.9210097       0.40426832       0.15788010      
#6  2.4238688      -0.10612452       0.44232464      
#7  2.5315087 High  1.37297365  High 0.88512802  High
#8  1.5920526      -0.09465904       0.48458793      
#9  0.3064365       1.37297365  High 0.25245844      
#10 0.2982843      -0.06271410       0.25968998      
#11 1.2682549       1.30486965       0.54201594      
#12 1.5215655       1.37297365  High 0.64987584      
#13 0.8123740      -1.21172411   Low 0.33641913      
#14 0.1296444      -0.27878877       0.08499473   Low
#15 0.2484241      -0.13332134       0.45131085      

答案 2 :(得分:1)

一个选项是

library(dplyr)
x %>%
   mutate(across(everything(), ~ifelse(. > quantile(., 0.9), quantile(., 0.9),
                                 ifelse(
                                     . < quantile(., 0.1), quantile(., 0.1),
                                      .))))