如何查找是否存在特定值的任何列?

6
   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中,检查字符串是否出现在数据框的行中(在任何列中)](https://dev59.com/rFcO5IYBdhLWcg3wbBA8) - camille
5个回答

6
我们可以使用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? - JNB
2
@Molly,我们可以这样做,但是它无法扩展。想象一下,如果你需要对10-20列进行操作。 - Ronak Shah
我的解决方案简单地使用了 mutate 和 if_else,虽然它能正常工作,但正如 @RonakShah 指出的那样,如果扩展到多个列会变得过于繁琐。 - Mojoesque

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

2
使用 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,我们可以循环遍历“第一列”、“中间列”和“最后一列”,并使用==进行比较,以获取逻辑vectorlist,然后使用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

注意:此帖子有重复内容。例如:这里

数据

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

          
   

1
一种使用apply系列的解决方案。
df$Carol = lapply(1:nrow(df), function(x) any(df[x,]=="Carol))

1

另一个选项是使用您建议的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 

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接