【问题标题】:Function to check the value and add values according to the result检查值并根据结果添加值的功能
【发布时间】:2020-08-13 15:51:13
【问题描述】:

那里! 我有一张桌子:

532 obs. of  44 variables

它看起来像这样:

A tibble: 10 x 44
       ID   PVD Vasculitis   CVA   CHF    MI   HTN  COPD
           <dbl> <dbl>      <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
     1 11    NA         NA    NA    NA    NA     1    NA
     2 22     1         NA     1    NA     1     1     1
     3 33    NA         NA    NA    NA     1     1     1
     4 44    NA         NA     1    NA    NA    NA     1
     5 55     1         NA    NA     1     1     1     1
     6 66    NA         NA    NA     1     1     1     1
     7 77    NA         NA    NA    NA    NA    NA    NA
     8 88    1         NA     1     1     1     1     1
     9 99    NA         NA    NA    NA    NA     1     1
    10 1010    NA         NA    NA     1     1     1    NA
    # ... with 36 more variables: TB <dbl>, Diabetes <dbl>,
    #   Liver <dbl>, CRF <dbl>, Dementia <dbl>, Obesity <dbl>,
    #   Hearing_loss <dbl>, Paraplegia <dbl>, `Peptic
    #   _ulcer` <dbl>, Autoimmune <dbl>, Breast_Cancer <dbl>,
    #   Colon_Cancer <dbl>, Anus_Cancer <dbl>,
    #   Stomach_Cancer <dbl>, Pancreas_Cancer <dbl>,
    #   Ovarian_Cancer <dbl>, Cervix_uteri_Cancer <dbl>,
    #   Uterus_Cancer <dbl>, Prostate_Cancer <dbl>,
    #   Melanoma <dbl>, Lymphoma <dbl>, Leukemia <dbl>,
    #   Thyroid_Cancer <dbl>, Head_and_neck_Cancer <dbl>,
    #   Kidney_Cancer <dbl>, Adrenal_Cancer <dbl>,
    #   Bone_Cancer <dbl>, Testicular_Cancer <dbl>,
    #   Skin_Cancer <dbl>, Urinary_Cancer <dbl>,
    #   Liver_Cancer <dbl>, Musculoskeletal_Cancer <dbl>,
    #   Multiple_myeloma <dbl>, CNS_Cancer <dbl>,
    #   Unknown_primary_Cancer <dbl>, solid <dbl>

所以第一列是唯一的 ID,后面的列是不同疾病的名称(不重复)。第 1 行中的值分别是是否存在疾病和不存在疾病。 例如,55 号患者(第 5 行)有“PVD”、“CHF”、“MI”、“HTN”、“COPD”等。 我还创建了一个带有癌症名称的向量(这些是第 19 到 43 列的名称)。 我想编写一个函数来检查这个表,如果列名与我的向量的癌症名称匹配,它将检查这一行(患者人数)在匹配列中是否有癌症(如果有标志 1 ),它会在最后一列添加一个名为“solid”的标志。至少一个巧合就足够了。对于所有的患者也是如此。 例如,同一个患者 55,如果他患有“结肠癌”(第 20 列),他应该在“固体”列中加 1,如果他患有其他癌症也没关系。 我尝试这样的事情,但没有成功,我被卡住了:

solid_tumor <- function(x){
    x <- as.data.frame(x)
    for (i in length(x)) {
        if (colnames(x) %in% tumors) {
            if(any(x==1)) {
               x[i] <- 1 
            }
        }
    }
}

谢谢。

【问题讨论】:

  • 我建议将您的函数视为始终在自己的环境中。添加tumors 作为要拉取的函数的参数。

标签: r function dataframe


【解决方案1】:

其他一些看待这个的方式。

简单功能

这展示了简单函数式方法的简单性和灵活性:

func <- function(x, candidates) {
  cnames <- intersect(candidates, colnames(x))
  if (length(cnames)) {
    +(rowSums(!is.na(subset(x, select = cnames))) > 0)
  } else rep(0L, nrow(x))
}

dat$solid <- func(dat, c("CHF", "MI"))
dat
#      ID PVD Vasculitis CVA CHF MI HTN COPD solid
# 1    11  NA         NA  NA  NA NA   1   NA     0
# 2    22   1         NA   1  NA  1   1    1     1
# 3    33  NA         NA  NA  NA  1   1    1     1
# 4    44  NA         NA   1  NA NA  NA    1     0
# 5    55   1         NA  NA   1  1   1    1     1
# 6    66  NA         NA  NA   1  1   1    1     1
# 7    77  NA         NA  NA  NA NA  NA   NA     0
# 8    88   1         NA   1   1  1   1    1     1
# 9    99  NA         NA  NA  NA NA   1    1     0
# 10 1010  NA         NA  NA   1  1   1   NA     1

整洁友好

不幸的是,dplyr 并不能特别容易地将 所有列 传递给 mutate 中的函数。 (我敢肯定有人会推荐c_across() ...我还没有发现它在这样的应用中很优雅。)

第一次刺可能使用点.

dat %>%
  mutate(solid = func(., c("CHF", "MI")))

在没有任何分组的情况下工作正常,但它总是使用整个帧而不考虑组,所以如果这是一个考虑因素,那么你会得到一个错误:

dat %>%
  group_by(ID) %>%
  # I know this is equivalent to rowwise() with this data
  mutate(solid = func(., c("CHF", "MI")))
# Error: Problem with `mutate()` input `solid`.
# x Input `solid` can't be recycled to size 1.
# i Input `solid` is `func(., c("CHF", "MI"))`.
# i Input `solid` must be size 1, not 10.
# i The error occured in group 1: ID = 11.

所以简单的解决方法是 do 它。

dat %>%
  group_by(ID) %>%
  do(mutate(., solid = func(., c("CHF", "MI"))))

(我意识到分组与 this 数据无关,但是对当前帧的所有列应用“简单”的非 tidyverse 函数在其他地方可能很有用,并且尊重组很重要。 )

【讨论】:

    【解决方案2】:

    一种旋转方法,使用tidyverse

    在我看来,最好是对数据进行透视,以便更轻松地编写函数。

    示例数据框:

    首先让我们制作一个示例数据框,以便其他人更容易考虑这个问题和未来的解决方案:

    library(tidyverse)
    na_or_1 <- c("NA", 1)
    df <- tibble(
      ID     = sample(10:100, 30),
      car    = sample(na_or_1, 30, replace = T, prob = c(.05, .95)),
      bug    = sample(na_or_1, 30, replace = T, prob = c(.05, .95)),
      blast  = sample(na_or_1, 30, replace = T, prob = c(.05, .95)),
      opt    = sample(na_or_1, 30, replace = T, prob = c(.05, .95)),
      star   = sample(na_or_1, 30, replace = T, prob = c(.05, .95)),
      queue  = sample(na_or_1, 30, replace = T, prob = c(.05, .95)),
      man    = sample(na_or_1, 30, replace = T, prob = c(.05, .95)),
      ring   = sample(na_or_1, 30, replace = T, prob = c(.05, .95)),
      happy  = sample(na_or_1, 30, replace = T, prob = c(.05, .95)),
      after  = sample(na_or_1, 30, replace = T, prob = c(.05, .95)),
      hug    = sample(na_or_1, 30, replace = T, prob = c(.05, .95)),
      dragon = sample(na_or_1, 30, replace = T, prob = c(.05, .95))
    )
    

    转动更长的时间

    现在我们可以使用pivot_longer 函数将所有数据转置更长的时间来对照 ID 列。

    df %>% 
      pivot_longer(cols = -ID) %>% 
      filter(!is.na(value))
    

    这会产生如下输出: ID Name value 其中 name == 列的名称(癌症名称)和 value == 1(所有 NA 都被过滤掉了)

    这使得函数更容易将逻辑应用于您要求它执行的操作。

    使用case_when 进行变异

    使用tumors 的向量,我可以应用case_when 逻辑和mutate 一个新列solid。我取出了 NA 过滤器,因为我想到你会希望你的 DF 几乎没有变化。

    df %>% 
        pivot_longer(cols = -ID) %>% 
        # filter( !is.na(value) ) %>% 
        mutate(solid = case_when(name %in% tumors &
                               !is.na(value) ~ 1,
                                 T ~ NA_real_))
    

    枢轴更宽

    最后,我将使用pivot_wider 撤消较长的枢轴。我将从最初使用枢轴创建的名称列中获取名称。

     df %>% 
    pivot_longer(cols = -ID) %>% 
    # filter( !is.na(value) ) %>% 
    mutate(solid = case_when(name %in% tumors &
                               !is.na(value) ~ 1,
                             T ~ NA_real_)) %>% 
    pivot_wider(
      names_from = name
    )
    

    功能解决方案

    myfunction <- function(df, tumors){
     df %>% 
      pivot_longer(cols = -ID) %>% 
      # filter( !is.na(value) ) %>% 
      mutate(solid = case_when(name %in% tumors &
                               !is.na(value) ~ 1,
                             T ~ NA_real_)) %>% 
      pivot_wider(
        names_from = name
      )
    }
    

    示例

    现在,当我获取肿瘤向量时,我可以将其放入函数和 df 中,并得出您原始问题的答案。

    tumors <- c("hug", "happy", "man")
    myfunction(df, tumors)
    
    
    # # A tibble: 32 x 14
    #      ID solid car   bug   blast opt   star  queue man   ring  happy after hug   dragon
    #     <int> <dbl> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> 
    # 1    16    NA NA    NA    NA    NA    NA    NA    NA    NA    NA    NA    NA    NA    
    # 2    98    NA NA    NA    NA    NA    NA    NA    NA    NA    NA    NA    NA    NA    
    # 3    74    NA NA    NA    NA    NA    NA    NA    NA    NA    NA    NA    NA    NA    
    # 4    50    NA NA    NA    NA    NA    NA    1     NA    NA    NA    NA    NA    NA    
    # 5    50     1 NA    NA    NA    NA    NA    NA    1     NA    NA    NA    NA    NA    
    # 6    29    NA NA    NA    NA    NA    NA    NA    NA    NA    NA    NA    NA    NA    
    # 7    94    NA NA    NA    NA    1     NA    NA    NA    NA    NA    NA    NA    NA    
    # 8    19    NA NA    1     NA    NA    NA    NA    NA    NA    NA    NA    NA    NA    
    # 9    46    NA NA    NA    NA    NA    NA    NA    NA    NA    NA    NA    NA    NA    
    # 10   15    NA NA    1     NA    NA    NA    NA    NA    NA    NA    NA    NA    NA  
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-10-15
      • 2021-08-29
      • 2021-10-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多