有条件替换,如果“正确”值存在

时间:2018-12-18 08:42:15

标签: r duplicates

我的数据由两个变量组成:id和相应的namename可以是两件事。 id或字母字符串。

如果存在非数字名称,则需要用该值替换所有数字名称。

数据示例

df <- data.frame(id = c("100", "100", "101", "102", "103", "104", "104", "105", "100", "106"), 
             name = c("100", "A", "B", "C", "D", "104", "E", "F", "100", "106"), 
             correct_name = c("A", "A", "B", "C", "D", "E", "E", "F", "A", "106"), stringsAsFactors = F)

第三列给出期望的结果。

我一直在和%in%duplicatedgroup_by纠缠不清,但无法到达任何地方。

编辑:我错过了关键部分-可能存在不存在的字符名称实例。更新了示例-对不起!

4 个答案:

答案 0 :(得分:3)

编辑

由于您已经提到有些id不需要替换name,在这种情况下,我们可以修改ave选项,检查条件并一次调用就替换所有值。

df$name <- with(df, ave(name, id, FUN = function(x) {
   inds = grepl("[0-9]+", x)
   if (any(!inds)) 
    replace(x, inds, x[which.max(!inds)])
   else
    x
}))

df
#    id name correct_name
#1  100    A            A
#2  100    A            A
#3  101    B            B
#4  102    C            C
#5  103    D            D
#6  104    E            E
#7  104    E            E
#8  105    F            F
#9  100    A            A
#10 106  106          106

原始答案

假设每个id只有一个唯一的name,使用dplyr我们可以将replace进行两次加倍,首先将其中带有数字的名称更改为{{ 1}},然后用组中的第一个非NA值替换这些NA

NA

并对基数R library(dplyr) df %>% group_by(id) %>% mutate(name = replace(name, grepl("[0-9]+", name), NA), name = replace(name, is.na(name), name[!is.na(name)][1])) # id name correct_name # <chr> <chr> <chr> #1 100 A A #2 100 A A #3 101 B B #4 102 C C #5 103 D D #6 104 E E #7 104 E E #8 105 F F #9 100 A A 使用相同的逻辑

ave

另一种选择是在两个方向上同时使用#Replace the numbers with NA df$name[grepl("[0-9]+", df$name)] <- NA #Change the NA's to first non-NA value in the group df$name <- with(df,ave(name, id, FUN = function(x) x[!is.na(x)][1])) tidyr

fill

PS-我刚刚在您的data.frame调用中添加了library(tidyverse) df %>% mutate(name = replace(name, grepl("[0-9]+", name), NA)) %>% group_by(id) %>% fill(name) %>% #default direction is "down" fill(name, .direction = "up") # id name correct_name # <chr> <chr> <chr> #1 100 A A #2 100 A A #3 100 A A #4 101 B B #5 102 C C #6 103 D D #7 104 E E #8 104 E E #9 105 F F ,以使列成为字符。

答案 1 :(得分:3)

使用dplyr并使用ifelsegrepl且模式设置为"\\d+"(即数字)的解决方案。

编辑:可能只有一个mutate

df %>% 
  group_by(id) %>% 
  mutate(namenew = ifelse(
    grepl("\\d+", name),   # match for digits in the string
    name[!grepl("\\d+", name)][1], # if TRUE, substitute with the first non-digit
    name # if FALSE, keep it
  )) 
#    id name correct_name namenew
# 1 100  100            A       A
# 2 100    A            A       A
# 3 101    B            B       B
# 4 102    C            C       C
# 5 103    D            D       D
# 6 104  104            E       A
# 7 104    E            E       E
# 8 105    F            F       F
# 9 100  100            A       A

与我上面的解决方案相比,可能更清楚发生了什么。 (类似于@Ronak Shah)

library(dplyr)
df %>% 
  group_by(id) %>%
  mutate(namenew = ifelse(
    grepl("\\d+", name), 
    NA,
    name
  )) %>% 
  mutate(namenew = ifelse(
    is.na(namenew),
    namenew[!is.na(namenew)][1],
    namenew
  ))


#    id name correct_name namenew
# 1 100  100            A       A
# 2 100    A            A       A
# 3 101    B            B       B
# 4 102    C            C       C
# 5 103    D            D       D
# 6 104  104            E       A
# 7 104    E            E       E
# 8 105    F            F       F
# 9 100  100            A       A

数据(stringsAsFactors很重要):

df <- data.frame(id = c("100", "100", "101", "102", "103", "104", "104", "105", "100"), 
                 name = c("100", "A", "B", "C", "D", "104", "E", "F", "100"), 
                 correct_name = c("A", "A", "B", "C", "D", "E", "E", "F", "A"), stringsAsFactors = F)

答案 2 :(得分:1)

快速肮脏的方式:

sapply(1:nrow(df),function(x){
  if (is.na(as.numeric(df$id[x]))==FALSE){
    ind=which(df$id==df$id[x])
    ind2=which(is.na(as.numeric(as.character((df$name[ind]))))==TRUE)
    df$name[x]<<-df$name[ind[ind2[1]]]
  }
})
df
   id name correct_name
1 100    A            A
2 100    A            A
3 101    B            B
4 102    C            C
5 103    D            D
6 104    E            E
7 104    E            E
8 105    F            F
9 100    A            A

将名称转换为numeric。如果出现NA,则名称为字母。如果不是,则为数字。遍历具有相同id的其他名称,并分配具有相同id的其他示例中找到的字母。

答案 3 :(得分:1)

或者,这可以通过使用查找表 update join 解决:

通过过滤$shownLast = $this->createQueryBuilder('b') ->select('b')->where('b.shownLast = 1') ->getQuery()->getResult(); 中的非数字条目来创建查找表:

df
library(data.table)
setDT(df)[!name %like% "^\\d+$"]

现在, id name correct_name 1: 100 A A 2: 101 B B 3: 102 C C 4: 103 D D 5: 104 E E 6: 105 F F 与查找表相连,并且在找到匹配项的地方,df被查找表中的相应条目所代替。否则,name保持不变:

name
setDT(df)[df[!name %like% "^\\d+$"], on = "id", name := i.name]
df