查找值是否在其他列的范围内

时间:2021-06-09 06:23:33

标签: r range max comparison min

我有一个看起来像这样的数据框 df

输入:

df <- read.table(text = 

"ID  Q1_PM Q1_TP Q1_overall  Q2_PM  Q2_LS  Q2_overall
 1   1     2     3           1       2     2       
 2   0     NA    NA          2       1     1 
 3   2     1     1           3       4     0  
 4   1     0     2           4       0     2 
 5   NA    1     NA          0       NA    0  
 6   2     0     1           1       NA    NA"   

, header = TRUE)

期望输出:

进一步解释一下,我想要的输出如下:

 ID  Q1_PM Q1_TP Q1_overall  Q2_PM  Q2_LS  Q2_overall Q1_check  Q2_check
 1   1     2     3           1       2     2          "above"   "within"
 2   0     NA    NA          2       1     1           NA       "within"
 3   2     1     1           3       4     0          "within"  "below"
 4   1     0     2           4       0     2          "above"   "within"
 5   NA    1     NA          0       NA    0           NA       "within"
 6   2     0     1           1       NA    NA         "within"   NA

说明:

示例 1:

根据 Q1_PMQ1_TP 列中的值,我想查看 Q1_overall 列中的值是否它们的范围内?如果不在范围内,值是高于还是低于范围?为了跟踪这一点,我想添加一个额外的列 Q1_check

示例 2:

同样,根据 Q2_PMQ2_LS 的值,我想检查 Q2_overall 的值是否它们的范围内?如果不在范围内,是高于还是低于范围?同样,为了跟踪这一点,我想添加一个额外的列 Q2_check

要求:

1- 为此,我想添加额外的列 Q1_checkQ2_check,其中第一列用于涉及 Q1 项的比较,第二列用于比较涉及 Q2 项。

2- 列可以包含以下值:abovebelowwithin

3- 如果名为 overall 的列具有 NAs,则额外的列也可能具有 NAs

相关帖子:

我找过相关的帖子,例如: Add column with values depending on another column to a dataframeCreate categories by comparing a numeric column with a fixed value 但我遇到了如下所述的错误。

部分解决方案:

我能想到的唯一解决方案是:

df$Q1_check <- ifelse(data$Q1_overall < data$Q1_PM, 'below',
                        ifelse(data$Q1_overall > data$Q1_TP, 'above', 
                               ifelse(is.na(data$Q1_overall), NA, 'within')))

但它导致以下错误:Error in data$Q1_overall : object of type 'closure' is not subsettable。我不明白可能的问题是什么。

df %>%
  mutate(Regulation = case_when(Q1_overall < Q1_PM ~ 'below',
                                Q1_overall > Q1_TP ~ 'above', 
                                Q1_PM < Q1_overall < Q1_TP, 'within'))

这也会导致错误 Error: unexpected '<' in: "Q1_overall > Q1_TP ~ 'above', Q1_PM < Q1_overall <"

编辑 1:

如果(假设)列是这些,如何扩展解决方案:

"Q1 Comm - 01 Scope Thesis"
"Q1 Comm - 02 Scope Project"
"Q1 Comm - 03 Learn Intern"
"Q1 Comm - 04 Biography"
"Q1 Comm - 05 Exhibit"
"Q1 Comm - 06 Social Act"
"Q1 Comm - 07 Post Project"
"Q1 Comm - 08 Learn Plant"
"Q1 Comm - 09 Study Narrate"
"Q1 Comm - 10 Learn Participate"
"Q1 Comm - 11 Write 1"
"Q1 Comm - 12 Read 2"
"Q1 Comm - Overall Study Plan"

我们如何识别列 Q1 Comm - Overall Study Plan 何时是:

1 - Below 所有其他列的 min(),或

2 - Above 所有其他列的 max(),或

3 - Within 所有其他列的范围

编辑 2:

对于更新的字段,我还包括 dput(df)

dput(df)

structure(list(ï..ID = c(10L, 31L, 225L, 243L), Q1.Comm...01.Scope.Thesis = c(NA, 
2L, 0L, NA), Q1.Comm...02.Scope.Project = c(NA, NA, NA, 2L), 
    Q1.Comm...03.Learn.Intern = c(4L, NA, NA, NA), Q1.Comm...04.Biography = c(NA, 
    NA, NA, 1L), Q1.Comm...05.Exhibit = c(4L, 2L, NA, NA), Q1.Comm...06.Social.Act = c(NA, 
    NA, NA, 3L), Q1.Comm...07.Post.Project = c(NA, NA, 3L, NA
    ), Q1.Comm...08.Learn.Plant = c(NA, NA, NA, 4L), Q1.Comm...09.Study.Narrate = c(NA, 
    NA, 0L, NA), Q1.Comm...10.Learn.Participate = c(4L, NA, NA, 
    NA), Q1.Comm...11.Write.1 = c(NA, 2L, NA, NA), Q1.Comm...12.Read.2 = c(NA, 
    NA, 1L, NA), Q1.Comm...Overall.Study.Plan = c(4L, 1L, 2L, 
    NA), X = c(NA, NA, NA, NA), X.1 = c(NA, NA, NA, NA), X.2 = c(NA, 
    NA, NA, NA)), class = "data.frame", row.names = c(NA, -4L
))

关于如何实现这一目标的任何建议将不胜感激。谢谢!

5 个答案:

答案 0 :(得分:1)

df <- read.table(text = 
                   
                   "ID  Q1-PM Q1-TP Q1-overall  Q2-PM  Q2-LS  Q2-overall
 1   1     2     3           1       2     2       
 2   0     NA    NA          2       1     1 
 3   2     1     1           3       4     0  
 4   1     0     2           4       0     2 
 5   NA    1     NA          0       NA    0  
 6   2     0     1           1       NA    NA"   
                 
                 , header = TRUE)

library(tidyverse)


f <- function(x, y, z){
  case_when(
    z < pmin(x, y, na.rm = TRUE) ~ "below",
    z > pmax(x, y, na.rm = TRUE) ~ "abowe",
    between(z, pmin(x, y, na.rm = TRUE), pmax(x, y, na.rm = TRUE)) ~ "within"
  )
}

df %>%
  rowwise() %>% 
  mutate(Q1_check = f(Q1.PM, Q1.TP, Q1.overall),
         Q2_check = f(Q2.PM, Q2.LS, Q2.overall))
#> # A tibble: 6 x 9
#> # Rowwise: 
#>      ID Q1.PM Q1.TP Q1.overall Q2.PM Q2.LS Q2.overall Q1_check Q2_check
#>   <int> <int> <int>      <int> <int> <int>      <int> <chr>    <chr>   
#> 1     1     1     2          3     1     2          2 abowe    within  
#> 2     2     0    NA         NA     2     1          1 <NA>     within  
#> 3     3     2     1          1     3     4          0 within   below   
#> 4     4     1     0          2     4     0          2 abowe    within  
#> 5     5    NA     1         NA     0    NA          0 <NA>     within  
#> 6     6     2     0          1     1    NA         NA within   <NA>

reprex package (v2.0.0) 于 2021 年 6 月 9 日创建

答案 1 :(得分:1)

如果您的列名称相似,您可以同时对任意数量的 Q 执行此操作。

  • 将列名中的 - 更改为可接受的 _
  • 为了相似,将 Q2_LS 改为 Q2_TP

这是做什么 -

  • 它选取以 _overall 结尾的每一列(此处为 2,但可以是任意数字)
  • 将此列值检查为 -
    • 如果小于名称为 _PM / _TP 的列代替 _overall 分配值 below
    • 如果大于名称为 _PM/_TP 的列代替 _overall 分配值 above
      • 为了访问这些列值,我使用了 get 以及 cur_columnstringr 字符串替换函数
    • 如果当前值为 NA,则分配一个 NA_character
    • 否则分配值 within
  • 现在,对于最终变异的列(全部一次),它通过从这些列中删除 _overall 并粘贴 _check 来重命名这些列(我使用了 .names across 的参数在这里)
    • 为此,我在 stringr::str_remove 参数中使用了 glue .names 遵循公式的胶水样式)
df <- read.table(text = 
                   
                   "ID  Q1_PM Q1_TP Q1_overall  Q2_PM  Q2_TP  Q2_overall
 1   1     2     3           1       2     2       
 2   0     NA    NA          2       1     1 
 3   2     1     1           3       4     0  
 4   1     0     2           4       0     2 
 5   NA    1     NA          0       NA    0  
 6   2     0     1           1       NA    NA"   
 
 , header = TRUE)

df
#>   ID Q1_PM Q1_TP Q1_overall Q2_PM Q2_TP Q2_overall
#> 1  1     1     2          3     1     2          2
#> 2  2     0    NA         NA     2     1          1
#> 3  3     2     1          1     3     4          0
#> 4  4     1     0          2     4     0          2
#> 5  5    NA     1         NA     0    NA          0
#> 6  6     2     0          1     1    NA         NA
library(tidyverse)
df %>% mutate(across(ends_with('overall'), ~ case_when(. < pmin(get(str_replace(cur_column(), '_overall', '_PM')),
                                                                 get(str_replace(cur_column(), '_overall', '_TP'))) ~ 'below',
                                                       . > pmax(get(str_replace(cur_column(), '_overall', '_PM')),
                                                                 get(str_replace(cur_column(), '_overall', '_TP'))) ~ 'above',
                                                       is.na(.) ~ NA_character_,
                                                       TRUE ~ 'within'),
                     .names = '{str_remove(.col,"_overall")}_check'))
#>   ID Q1_PM Q1_TP Q1_overall Q2_PM Q2_TP Q2_overall Q1_check Q2_check
#> 1  1     1     2          3     1     2          2    above   within
#> 2  2     0    NA         NA     2     1          1     <NA>   within
#> 3  3     2     1          1     3     4          0   within    below
#> 4  4     1     0          2     4     0          2    above   within
#> 5  5    NA     1         NA     0    NA          0     <NA>   within
#> 6  6     2     0          1     1    NA         NA   within     <NA>

reprex package (v2.0.0) 于 2021 年 6 月 9 日创建

答案 2 :(得分:1)

comparison <- function(dt, group_cols, new_col, compare_col){
  
  dt[, 
     c("min", "max") := transpose(pmap(.SD, range, na.rm = TRUE)), .SDcols = group_cols
     ][,(new_col) := fcase(
       is.na(get(compare_col)), NA_character_,
       get(compare_col) < min, "below",
       get(compare_col) > max, "above",
       default = "within"
     )
     ][]
}

group_cols <- names(df) %>%
 str_subset("^Q[0-9]+") %>%
 str_subset("overall", negate = TRUE) %>%
 split(str_extract(., "^Q[0-9]+"))

new_cols <- names(group_cols) %>% str_c("_check")
compare_cols <- names(group_cols) %>% str_c("_overall")

setDT(df)

pwalk(list(group_cols, new_cols, compare_cols), ~comparison(df, ...))
df[, c("min", "max") := NULL]

答案 3 :(得分:0)

似乎是一个非常冗长的方法 -

library(dplyr)

comparison <- function(x, y, z) {
  case_when(is.na(z) ~ NA_character_,
            z >= x & z <= y | 
              z >= y & z <= x |
              is.na(x) & y == z |
              is.na(y) & x == z ~ 'within', 
            z > x & z > y ~ 'above', 
            TRUE ~ 'below')
}

df %>%
  mutate(Q1_check = comparison(Q1.PM, Q1.TP, Q1.overall), 
          Q2_check = comparison(Q2.PM, Q2.LS, Q2.overall))
  
         
#  ID Q1.PM Q1.TP Q1.overall Q2.PM Q2.LS Q2.overall Q1_check Q2_check
#1  1     1     2          3     1     2          2    above   within
#2  2     0    NA         NA     2     1          1     <NA>   within
#3  3     2     1          1     3     4          0   within    below
#4  4     1     0          2     4     0          2    above   within
#5  5    NA     1         NA     0    NA          0     <NA>   within
#6  6     2     0          1     1    NA         NA   within     <NA>

答案 4 :(得分:0)

主要基于 Ronak 的出色解决方案:

df <- structure(list(ID = c(10L, 31L, 225L, 243L), 
                      `Q1 Comm - 01 Scope Thesis` = c(NA, 2L, 0L, NA), 
                      `Q1 Comm - 02 Scope Project` = c(NA, NA, NA, 2L), 
                      `Q1 Comm - 03 Learn Intern` = c(4L, NA, NA, NA), 
                      `Q1 Comm - 04 Biography` = c(NA, NA, NA, 1L), 
                      `Q1 Comm - 05 Exhibit` = c(4L, 2L, NA, NA), 
                      `Q1 Comm - 06 Social Act` = c(NA, NA, NA, 3L), 
                      `Q1 Comm - 07 Post Project` = c(NA, NA, 3L, NA), 
                      `Q1 Comm - 08 Learn Plant` = c(NA, NA, NA, 4L), 
                      `Q1 Comm - 09 Study Narrate` = c(NA, NA, 0L, NA), 
                      `Q1 Comm - 10 Learn Participate` = c(4L, NA, NA,NA), 
                      `Q1 Comm - 11 Write 1` = c(NA, 2L, NA, NA), 
                      `Q1 Comm - 12 Read 2` = c(NA, NA, 1L, NA), 
                      `Q1 Comm - Overall Study Plan` = c(4L, 1L, 2L, NA), 
                      X = c(NA, NA, NA, NA), 
                      `X 1` = c(NA, NA, NA, NA), 
                      `X 2` = c(NA, NA, NA, NA)), 
                class = "data.frame", row.names = c(NA, -4L))

library(dplyr)

comparison <- function(df, prefix) {
  df <- df[grep(prefix, colnames(df))]
  min <- apply(df[-grep("Overall", colnames(df))], 1, min, na.rm = T)
  max <- apply(df[-grep("Overall", colnames(df))], 1, max, na.rm = T)
  z <- df[grep("Overall", colnames(df))]
  case_when(is.na(z) ~ NA_character_,
            z >= min & z <= max ~ 'within', 
            z > max ~ 'above', 
            TRUE ~ 'below')
}

prefixes <- sub(" \\- Overall.*", "", colnames(df[grep("Overall", colnames(df))]))

for (i in prefixes) {
  df <- df %>%
    mutate("{i} - Check" := comparison(df, i))
}


> print(df)
   ID Q1 Comm - 01 Scope Thesis Q1 Comm - 02 Scope Project Q1 Comm - 03 Learn Intern Q1 Comm - 04 Biography
1  10                        NA                         NA                         4                     NA
2  31                         2                         NA                        NA                     NA
3 225                         0                         NA                        NA                     NA
4 243                        NA                          2                        NA                      1
  Q1 Comm - 05 Exhibit Q1 Comm - 06 Social Act Q1 Comm - 07 Post Project Q1 Comm - 08 Learn Plant
1                    4                      NA                        NA                       NA
2                    2                      NA                        NA                       NA
3                   NA                      NA                         3                       NA
4                   NA                       3                        NA                        4
  Q1 Comm - 09 Study Narrate Q1 Comm - 10 Learn Participate Q1 Comm - 11 Write 1 Q1 Comm - 12 Read 2
1                         NA                              4                   NA                  NA
2                         NA                             NA                    2                  NA
3                          0                             NA                   NA                   1
4                         NA                             NA                   NA                  NA
  Q1 Comm - Overall Study Plan  X X 1 X 2 Q1 Comm - Check
1                            4 NA  NA  NA          within
2                            1 NA  NA  NA           below
3                            2 NA  NA  NA          within
4                           NA NA  NA  NA            <NA>