【问题标题】:How to find if ANY column has a specific value I am looking for?如何查找任何列是否具有我正在寻找的特定值?
【发布时间】:2019-08-19 11:42:19
【问题描述】:
   id first  middle  last       Age
    1 Carol  Jenny   Smith      15
    2 Sarah  Carol   Roberts    20
    3 Josh   David   Richardson 22

我正在尝试在任何名称列(第一个、中间、最后一个)中查找特定名称。例如,如果我找到一个叫 Carol 的人(不管是名字/中间名/姓氏),我想改变一列 'Carol' 并给出 1。所以我想要的是以下内容

   id first  middle  last       Age  Carol
    1 Carol   Jenny   Smith      15   1
    2 Sarah  Carol   Roberts    20    1
    3 Josh   David   Richardson 22    0

我一直在努力 ifelse(c(first, middle, last) == "Carol" , 1, 0 ) 或 "Carol" %in% first...等 但由于某种原因,我只能处理一列而不是多列。有人可以帮我吗?提前谢谢!

【问题讨论】:

标签: r filter dplyr


【解决方案1】:

使用tidyverse

library(tidyverse)
f1 <- function(data, wordToCompare, colsToCompare) {
          wordToCompare <- enquo(wordToCompare)
          data %>%
              select(colsToCompare) %>%
              mutate(!! wordToCompare :=  map(.,  ~ 
       .x == as_label(wordToCompare)) %>% 
           reduce(`|`) %>%
           as.integer)
              }
          
f1(df1, Carol, c("first", 'middle', 'last'))
# first middle       last Carol
#1 Carol  Jenny      Smith     1
#2 Sarah  Carol    Roberts     1
#3  Josh  David Richardson     0

f1(df1, Sarah, c("first", 'middle', 'last'))
#   first middle       last Sarah
#1 Carol  Jenny      Smith     0
#2 Sarah  Carol    Roberts     1
#3  Josh  David Richardson     0

或者这也可以通过pmap来完成

df1 %>%
  mutate(Carol = pmap_int(.[c('first', 'middle', 'last')],
          ~ +('Carol' %in% c(...))))
#   id first middle       last Age Carol
#1  1 Carol  Jenny      Smith  15     1
#2  2 Sarah  Carol    Roberts  20     1
#3  3  Josh  David Richardson  22     0

可以封装成函数

f2 <- function(data, wordToCompare, colsToCompare) {
      wordToCompare <- enquo(wordToCompare)
      data %>%
           mutate(!! wordToCompare := pmap_int(.[colsToCompare],
          ~ +(as_label(wordToCompare) %in% c(...))))
  } 

f2(df1, Carol, c("first", 'middle', 'last'))
#  id first middle       last Age Carol
#1  1 Carol  Jenny      Smith  15     1
#2  2 Sarah  Carol    Roberts  20     1
#3  3  Josh  David Richardson  22     0

注意:两种 tidyverse 方法都不需要任何整形


使用base R,我们可以遍历'first'、'middle'、'last'列并使用==进行比较,得到逻辑vectors的list,我们Reduce使用| 转换为单个逻辑vector 并使用+ 将其强制转换为二进制

df1$Carol <- +(Reduce(`|`, lapply(df1[2:4], `==`, 'Carol')))
df1
#  id first middle       last Age Carol
#1  1 Carol  Jenny      Smith  15     1
#2  2 Sarah  Carol    Roberts  20     1 
#3  3  Josh  David Richardson  22     0

注意:这篇文章有骗子。例如here

数据

df1 <- structure(list(id = 1:3, first = c("Carol", "Sarah", "Josh"), 
middle = c("Jenny", "Carol", "David"), last = c("Smith", 
"Roberts", "Richardson"), Age = c(15L, 20L, 22L)),
  class = "data.frame", row.names = c(NA, 
 -3L))

          
   

【讨论】:

    【解决方案2】:

    我们可以使用rowSums

    df$Carol <- as.integer(rowSums(df[2:4] == "Carol") > 0)
    
    df
    #  id first middle       last Age Carol
    #1  1 Carol  Jenny      Smith  15     1
    #2  2 Sarah  Carol    Roberts  20     1
    #3  3  Josh  David Richardson  22     0
    

    如果我们需要它作为一个函数

    fun <- function(df, value) {
       as.integer(rowSums(df[2:4] == value) > 0)
    }
    
    fun(df, "Carol")
    #[1] 1 1 0
    fun(df, "Sarah")
    #[1] 0 1 0
    

    但这假设您要搜索的列位于位置2:4

    为列位置提供更大的灵活性

    fun <- function(df, cols, value) {
       as.integer(rowSums(df[cols] == value) > 0)
     }
    fun(df, c("first", "last","middle"), "Carol")
    #[1] 1 1 0
    fun(df, c("first", "last","middle"), "Sarah")
    #[1] 0 1 0
    

    【讨论】:

    • 我明白了..为什么我们不能在这里使用 ifelse 呢?只是想知道..是否可以使用 mutate?
    • @Molly 我们可以,但它不会扩大规模。想象一下,您需要 10-20 列。
    • 我的解决方案只是使用 mutate 和 if_else 并且可以工作,但正如@RonakShah 指出的那样,如果扩大到多列,工作量很大。
    【解决方案3】:

    这是一个tidyverse 选项。我们首先将数据重塑为长格式,按id 分组,并在至少一行中找到具有所需名称的id 级别。然后我们重新调整为宽幅格式。

    library(tidyverse)
    
    df %>% 
      gather(key, value, first:last) %>% 
      group_by(id) %>% 
      mutate(Carol = as.numeric(any(value=="Carol"))) %>% 
      spread(key, value)
    
         id   Age Carol first last       middle
    1     1    15     1 Carol Smith      Jenny 
    2     2    20     1 Sarah Roberts    Carol 
    3     3    22     0 Josh  Richardson David
    

    或者,作为一个函数:

    find.target = function(data, target) {
    
      data %>% 
        gather(key, value, first:last) %>% 
        group_by(id) %>% 
        mutate(!!target := as.numeric(any(value==target))) %>% 
        spread(key, value) %>% 
        # Move new target column to end
        select(-target, target)
    
    }
    
    find.target(df, "Carol")
    find.target(df, "Sarah")
    

    你也可以一次做几个。例如:

    map(c("Sarah", "Carol", "David"), ~ find.target(df, .x)) %>% 
      reduce(left_join)
    
         id   Age first last       middle Sarah Carol David
    1     1    15 Carol Smith      Jenny      0     1     0
    2     2    20 Sarah Roberts    Carol      1     1     0
    3     3    22 Josh  Richardson David      0     0     1
    

    【讨论】:

      【解决方案4】:

      按照您的建议使用mutateif_else() 的另一个选项:

      library(tidyverse)
      
      data = read_table("   id first  middle  last       Age
          1 Carol  Jenny   Smith      15
          2 Sarah  Carol   Roberts    20
          3 Josh   David   Richardson 22")
      data %>%
        mutate(carol = if_else(first == "Carol" | middle == "Carol" | last == "Carol",
                               "yes",
                               "no"))
      

      结果:

      # A tibble: 3 x 6
           id first middle last         Age carol
        <dbl> <chr> <chr>  <chr>      <dbl> <chr>
      1     1 Carol Jenny  Smith         15 yes  
      2     2 Sarah Carol  Roberts       20 yes  
      3     3 Josh  David  Richardson    22 no 
      

      【讨论】:

        【解决方案5】:

        使用applyfamily 的解决方案

        df$Carol = lapply(1:nrow(df), function(x) any(df[x,]=="Carol))
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-06-11
          • 2017-07-20
          • 2011-03-23
          • 2011-03-21
          • 1970-01-01
          • 2013-09-01
          相关资源
          最近更新 更多