【问题标题】:Finding if a value is within the range of other columns查找值是否在其他列的范围内
【发布时间】:2021-08-26 03:10:36
【问题描述】:

我有一个数据框 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 '&lt;' in: "Q1_overall &gt; Q1_TP ~ 'above', Q1_PM &lt; Q1_overall &lt;"

编辑 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
))

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

【问题讨论】:

  • 你得到的错误是因为data$Q1-overall被解析为data$Q1 - overall;你想要data$`Q1-overall`
  • 为什么列名不一致?例如,为什么是Q1-TP 而不是Q1-LS
  • 在我的情况下有多种不同的措施,为了模拟它,我制作了Q1-TSQ1-LS。如果两者都作为TP 或两者都作为LS 有助于简化解决方案,请提出建议。我可以做其他操作。
  • 感谢@AnilGoyal 的快速回复。我已经更新了我的问题以解决您的评论。现在清楚了吗?
  • 我还包含了扩展版本的 dput(df)。

标签: r range max comparison min


【解决方案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-06-09 创建

【讨论】:

  • 谢谢@Yuriy Saraykin,我们是否可以更新函数来计算两列以上的pmin()pmax()
【解决方案2】:

如果您的列名称相似,您可以同时对任意数量的 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 )
    • 为此,我在glue 参数中使用了stringr::str_remove .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-06-09 创建

【讨论】:

  • 谢谢@AnilGoyal,能否请您解释一下您的解决方案,以便我更好地理解?
  • 是的,为什么不@Sandy,请检查编辑。将 - 更改为 _ 的原因只是 R 要求/命名约定不使用 - 内部名称
  • 谢谢@AnilGoyal。我稍微更新了我的问题,并包含了需要与overall 列进行比较的实际列名。我已经按照它们的实际顺序复制了它们(提及以防万一)。您能否就此提出建议?
  • @Sandy,您已包含列名,但可能您忘记包含输出的逻辑/标准!
  • 我总共有九 (9) 个问题场景,即Q1:Q9,并且对于每个问题场景以相同的顺序重复相同的项目。数据为宽格式,没有重复的行。
【解决方案3】:
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]

【讨论】:

    【解决方案4】:

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

    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>
    

    【讨论】:

    • 谢谢@Ronak Shah,我怎样才能将它应用到多个列(比如说 8 个不同的列),这会使这个解决方案变得相当复杂......
    • 什么意思?因此,您的数据中还有其他列,而不是 PMTPOverall?检查它们的规则是什么。
    • 我只想进行列比较,看看overall 列是否在其他列的范围内。例如,有一些最大值和最小值,然后查看整个列是否在该最大值和最小值之内。它是否回答了您的问题@Ronak Shah?
    • 我已经编辑了我的问题@Ronak Shah,并且我提供了我的实际列名,请您根据此建议吗?
    【解决方案5】:

    很大程度上基于 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>
    

    【讨论】:

    • 为了比较或查找超过2列的范围,很难应用这个解决方案?
    • 我试图根据你在 Ronak 的解决方案下写的内容给出一个解决方案。它的作用是获取名称中包含“.overall”的所有列的前缀(在您的示例中为 Q1 和 Q2)并创建一个检查列,无论您将其应用于 2 列还是 200 列。唯一的潜力缺陷是每个前缀需要 3 列,并且它们的顺序必须与您在示例中输入的顺序相同(最小、最大、总体)
    • 我已经编辑了我的问题并提供了我的实际列名,请您根据此建议吗?
    • 编辑了我的解决方案以满足您的要求。它现在检查与前缀匹配的列中每一行的最小值和最大值(不包括总体),并将其与所述前缀的总体进行比较
    • 感谢您的更新,我已经包含了一个参考 dput(df) 我正在尝试运行代码的示例。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-03-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-12-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多