【问题标题】:For loop with if elseFor 循环与 if else
【发布时间】:2021-09-16 09:20:38
【问题描述】:

我有一个 df,其中一部分类似于以下内容

| Number|Category| A1|A2|B1|B2|C1|C2|A |B |C |
| ------| -------|---|--|--|--|--|--|--|--|--|
| 1     |  1     | 10|30|5 |15|NA|NA|5 |10|NA|
| 2     |  2     | 10|30|5 |15|25|35|40|20|45|

条件是

  • A1 & A2, B1 & B2, C1 & C2 分别是因子A, B, C的下限和上限
  • A、B、C 列代表测量值。
  • 如果测量值低于下限,则系数为"passed"
  • 如果在两个限制之间,则因子在"danger"
  • 如果测量值高于上限,则为"failed"
  • 对于category=1,我们只允许1 在其中一个因素中失败,在这种情况下,我们将资产分类为"risk"
  • 但如果我们有 2 次失败,那么第 1 行中的资产 "fail"
  • Category=2 允许 2 次失败。如果一个因素失败是"at risk",如果我们有2个失败是"risk",我们有3个失败,那么它是"fail"

所以我想为每一行(资产)计算每个因素的状态,然后计算资产的状态。我正在尝试使用 for 循环和 if-else 语句来迭代每一行的所有这些列,但由于我是初学者,所以似乎很困难。最终结果是以下列附加到数据集。提前谢谢你

|Number|Aa    |Bb    |Cc    |Result |
|------|------|------|------|-------|
|1     |passed|danger|NA    | risk  |
|2     |failed|failed|failed| failed|

【问题讨论】:

    标签: r for-loop if-statement dplyr tidyverse


    【解决方案1】:

    您还可以使用以下解决方案,它是基础 R 和 tidyverse 的组合:

    library(dplyr)
    library(purrr)
    
    colnames <- c(1, 2) 
    tmp <- df[-colnames]
    
    
    lapply(split.default(tmp, gsub("(\\w)\\d+?", "\\1", names(tmp))), 
           function(x) cbind(df[colnames], x)) %>% 
      imap(~ .x %>% 
            mutate(!!{.y} := pmap_chr(., ~ 
                                ifelse(any(is.na(..3), is.na(..4), is.na(..5)), "NA", 
                                       ifelse(..5 > ..3 & ..5 < ..4, "danger", ifelse(..5 < ..3, "passed", "failed"))))) %>%
             select(-c(3, 4))) %>%
      reduce(~ full_join(..1, ..2, id = c("Number", "Category"))) %>%
      rowwise() %>%
      mutate(Result = case_when(
        Category == 1 & sum(c_across(A:C) == "failed") <= 1 ~ "Risk",
        Category == 1 & sum(c_across(A:C) == "failed") > 1 ~ "Fail",
        Category == 2 & sum(c_across(A:C) == "failed") == 1 ~ "At_Risk",
        Category == 2 & sum(c_across(A:C) == "failed") == 2 ~ "Risk",
        Category == 2 & sum(c_across(A:C) == "failed") == 3 ~ "Fail" 
      ))
    
    # A tibble: 2 x 6
    # Rowwise: 
      Number Category A      B      C      Result
       <dbl>    <dbl> <chr>  <chr>  <chr>  <chr> 
    1      1        1 passed danger NA     Risk  
    2      2        2 failed failed failed Fail
    

    【讨论】:

    • 我知道您可以尝试使用 split.default。确实不错的策略
    • 是的,我一直期待着尽快使用它。你可以在事情发生之前看到它们,哈哈。
    【解决方案2】:

    这可以在dplyr 中完成,甚至无需reshaping 数据或使用任何循环(for/while)。使用acrosscur_data()cur_column(),它们肯定是dplyr 的强大功能。

    library(dplyr, warn.conflicts = F)
    
    df
    #>   Number Category A1 A2 B1 B2 C1 C2  A  B  C
    #> 1      1        1 10 30  5 15 NA NA  5 10 NA
    #> 2      2        2 10 30  5 15 25 35 40 20 45
    
    df %>% group_by(Number, Category) %>%
      transmute(across(c('A', 'B', 'C'), ~ case_when(is.na(.) | is.na(get(paste0(cur_column(), 1))) | 
                                                    is.na(get(paste0(cur_column(), 2))) ~ NA_character_,
                                                  . < get(paste0(cur_column(), 1)) ~ 'passed',
                                                  . <= get(paste0(cur_column(), 2)) ~ 'danger',
                                                  TRUE ~ 'failed'),
                       .names = '{.col}{tolower(.col)}')) %>%
      mutate(Result = ifelse(rowSums(cur_data() == 'failed', na.rm = T) <= Category, 'risk', 'failed'))
    
    #> # A tibble: 2 x 6
    #> # Groups:   Number, Category [2]
    #>   Number Category Aa     Bb     Cc     Result
    #>    <int>    <int> <chr>  <chr>  <chr>  <chr> 
    #> 1      1        1 passed danger <NA>   risk  
    #> 2      2        2 failed failed failed failed
    

    reprex package (v2.0.0) 于 2021-07-06 创建

    【讨论】:

    • 检查我的解决方案亲爱的朋友。
    【解决方案3】:

    您的大部分问题是由数据框的 untidy 特性引起的。我开始根据您不整洁的数据和整洁的等价物提供解决方案,但不整洁的解决方案虽然可能,但变得太痛苦了。

    所以,这是一个基于与您的数据框相当的解决方案的解决方案。

    首先,整理一下。您的数据框不整洁的原因是您的列名包含信息,即A1A2 包含A 中值的接受限制,等等。我们可以通过延长数据框来纠正这个问题。

    由于原件的凌乱程度,这个过程有点长。或许可以使用names_patternpivot_longer() 的其他高级参数来创建更紧凑的转换版本,但长版本至少有清晰的好处。

    longDF <- df %>% 
      select(Number, Category, A, B, C) %>% 
      pivot_longer(
        c(-Category, -Number),
        names_to="Variable",
        values_to="Value"
      ) %>% 
      left_join(
        df %>% 
          select(Number, Category, A1, B1, C1) %>% 
          pivot_longer(
            c(-Category, -Number),
            names_to="Variable",
            values_to="Lower"
          ) %>% 
        mutate(Variable=str_sub(Variable, 1, 1)),
        by=c("Number", "Category", "Variable")
      ) %>% 
      left_join(
        df %>% 
          select(Number, Category, A2, B2, C2) %>% 
          pivot_longer(
          c(-Category, -Number),
          names_to="Variable",
          values_to="Upper"
        ) %>% 
        mutate(Variable=str_sub(Variable, 1, 1)),
        by=c("Number", "Category", "Variable")
      )
    longDF
    # A tibble: 6 x 6
      Number Category Variable Value Lower Upper
       <dbl>    <dbl> <chr>    <dbl> <dbl> <dbl>
    1      1        1 A            5    10    30
    2      1        1 B           10     5    15
    3      1        1 C           NA    NA    NA
    4      2        2 A           40    10    30
    5      2        2 B           20     5    15
    6      2        2 C           45    25    35
    

    因此,此时,我们的列定义了测试的Category、正在测量的Variable、其Value 和两个接受限制(LowerUpper)。

    现在,确定每个 Value 的可接受性很简单。

    longDF <- longDF %>% 
                mutate(
                  Result=ifelse(
                           Value < Lower, 
                           "Pass", 
                           ifelse(Value < Upper, "Danger", "Fail")
                         )
                )
    longDF
    # A tibble: 6 x 7
      Number Category Variable Value Lower Upper Result
       <dbl>    <dbl> <chr>    <dbl> <dbl> <dbl> <chr> 
    1      1        1 A            5    10    30 Pass  
    2      1        1 B           10     5    15 Danger
    3      1        1 C           NA    NA    NA NA    
    4      2        2 A           40    10    30 Fail  
    5      2        2 B           20     5    15 Fail  
    6      2        2 C           45    25    35 Fail  
    

    另外,请注意每个值的分类独立于Variable 和可能变量的数量。所以代码在这些方面是健壮的。

    现在我们可以按NumberCategory 对结果进行分类。

    longDF %>% 
      group_by(Number, Category, Result) %>% 
      summarise(N=n(), .groups="drop") %>% 
      pivot_wider(
        names_from=Result, 
        values_from=N, 
        values_fill=0
      )
    # A tibble: 2 x 7
      Number Category Danger  Pass  `NA`  Fail 
       <dbl>    <dbl>  <int> <int> <int> <int>  
    1      1        1      1     1     1     0  
    2      2        2      0     0     0     3  
    

    同样,我们在 Categorys 和 Numbers 的数量及其标签方面都很稳健。

    评估整体结果也很简单,但由于选项繁多,因此有些冗长。请注意,您的文本与所需的输出不一致,因为您没有解释如何获得 "warn" for Category = 1 的整体结果。我和文字一起去了。如果您想匹配示例输出,一旦定义了标准,对代码的更改应该很简单。

    longDF %>% 
      group_by(Number, Category, Result) %>% 
      summarise(N=n(), .groups="drop") %>% 
      pivot_wider(
        names_from=Result, 
        values_from=N, 
        values_fill=0
      ) %>% 
      mutate(
        Result=ifelse(
                 Category == 1, 
                 ifelse(Fail == 0, "Pass", ifelse(Fail == 1, "Risk", "Fail")),
                 ifelse(Fail < 2, "Pass", ifelse(Fail == 2, "Risk", "Fail"))
                )
      )
    # A tibble: 2 x 7
      Number Category Danger  Pass  `NA`  Fail Result
       <dbl>    <dbl>  <int> <int> <int> <int> <chr> 
    1      1        1      1     1     1     0 Pass  
    2      2        2      0     0     0     3 Fail  
    

    如果您需要知道是哪个Variable 导致了潜在故障,也可以从longDF 获得,只需对分组稍作更改即可。

    longDF %>% 
      group_by(Category, Variable, Result) %>% 
      summarise(N=n(), .groups="drop") %>% 
      pivot_wider(
        names_from=Variable, 
        values_from=Result
      )
    # A tibble: 2 x 5
      Category     N A     B      C    
         <dbl> <int> <chr> <chr>  <chr>
    1        1     1 Pass  Danger NA   
    2        2     1 Fail  Fail   Fail
    

    当然,您可以将这两个数据框连接在一起,以获得对整体结果和组件变量评估的全面描述。

    【讨论】:

    • 非常感谢您的解决方案,我将在我的代码中使用它
    猜你喜欢
    • 2015-07-20
    • 2017-08-02
    • 2019-12-04
    • 1970-01-01
    • 2023-04-09
    • 2012-11-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多