我的数据由两个变量组成:id
和相应的name
。 name
可以是两件事。 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%
和duplicated
和group_by
纠缠不清,但无法到达任何地方。
编辑:我错过了关键部分-可能存在不存在的字符名称实例。更新了示例-对不起!
答案 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
并使用ifelse
加grepl
且模式设置为"\\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