r语言 - 如果存在"correct"值,则有条件替换



我的数据由两个变量组成,一个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,但一无所获。

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

编辑

由于您已经提到在某些情况下某些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,我们将包含数字的名称更改为NA,然后将这些NA替换为组中的第一个非NA值。

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      

并对基本 R 使用相同的逻辑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]))

另一种选择是在两个方向上使用tidyrfill

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   

PS - 我刚刚在您的 data.frame 调用中添加了stringsAsFactors = FALSE,以使列成为字符。

具有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沙阿)

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)

快速肮脏的方式:

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的其他示例中找到的字母。

或者,这可以通过使用查找表更新连接来解决:

查找表是通过筛选非数字条目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
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

最新更新