R:基于列前缀和后缀有条件地替换值

R:基于列前缀和后缀有条件地替换值,r,database,merge,data-manipulation,R,Database,Merge,Data Manipulation,我有两个数据帧。数据框A有许多观察值/行、每个观察值的ID和许多附加列。对于观测值X的子集,缺少一组列的值/NA。数据框B包含X中观察值的子集,可使用ID和与数据框a中相同名称的变量在数据框之间进行匹配,但包含用缺失/NA替换列集合中缺失值的值 下面我的代码使用连接操作只是添加列,而不是替换缺少的值。对于每个附加变量,我们将它们命名为W in B,结果表生成W.x和W.y library(dplyr) foo <- data.frame(id = seq(1:6), x = c(NA,

我有两个数据帧。数据框A有许多观察值/行、每个观察值的ID和许多附加列。对于观测值X的子集,缺少一组列的值/NA。数据框B包含X中观察值的子集,可使用ID和与数据框a中相同名称的变量在数据框之间进行匹配,但包含用缺失/NA替换列集合中缺失值的值

下面我的代码使用连接操作只是添加列,而不是替换缺少的值。对于每个附加变量,我们将它们命名为W in B,结果表生成W.x和W.y

library(dplyr)

foo <- data.frame(id = seq(1:6), x = c(NA, NA, NA, 1, 3, 8), z = seq_along(10:15))
bar <- data.frame(id = seq(1:2), x = c(10, 9))
dplyr::left_join(x = foo, y = bar, by = "id")

一种想法是在加入后使用ifelse,但为所有变量键入ifelse函数是不可行的。有没有一种方法可以在不使用数据库连接的情况下实现这一点,或者有没有一种方法可以在所有以.x结尾的列中应用函数,以在.x中的值丢失时将.x中的值替换为.y中的值?

如果您不介意详细的baseR方法,然后,您可以使用合并和数据帧的仔细子集轻松地完成这项工作

df <- merge(foo, bar, by="id", all.x=TRUE)
names(df) <- c("id", "x", "z", "y")
df$x[is.na(df$x)] <- df$y[is.na(df$x)]
df <- df[c("id", "x", "z")]

> df
  id  x z
1  1 10 1
2  2  9 2
3  3 NA 3
4  4  1 4
5  5  3 5
6  6  8 6

如果您不介意使用冗长的baseR方法,那么您可以使用合并和仔细的数据帧子集来轻松实现这一点

df <- merge(foo, bar, by="id", all.x=TRUE)
names(df) <- c("id", "x", "z", "y")
df$x[is.na(df$x)] <- df$y[is.na(df$x)]
df <- df[c("id", "x", "z")]

> df
  id  x z
1  1 10 1
2  2  9 2
3  3 NA 3
4  4  1 4
5  5  3 5
6  6  8 6
编辑

以@alistaire的示例数据帧更新答案

我们可以使用mapply扩展下面给出的相同答案,以便它可以处理foo和bar的多个列

找出两个数据帧之间的公共列,并对它们进行排序,使它们的顺序相同

vars <- sort(intersect(names(foo), names(bar))[-1])
foo[vars] <- mapply(function(x, y) {
             ind = is.na(x)
             replace(x, ind, y[match(foo$id[ind], bar$id)])
             }, foo[vars], bar[vars])

foo
#  id  x y z
#1  1 10 1 1
#2  2  9 2 2
#3  3 NA 3 3
#4  4  1 4 4
#5  5  3 5 5
#6  6  8 6 6
原始答案

我想这正是你想要的:

foo[-1] <- sapply(foo[-1], function(x) {
    ind = is.na(x)
    replace(x, ind, bar$x[match(foo$id[ind], bar$id)])
})


foo
#  id  x z
#1  1 10 1
#2  2  9 2
#3  3 NA 3
#4  4  1 4
#5  5  3 5
#6  6  8 6
对于除id之外的每一列,我们都会在foo中找到缺少的值,并用bar中相应的值替换它

编辑

以@alistaire的示例数据帧更新答案

我们可以使用mapply扩展下面给出的相同答案,以便它可以处理foo和bar的多个列

找出两个数据帧之间的公共列,并对它们进行排序,使它们的顺序相同

vars <- sort(intersect(names(foo), names(bar))[-1])
foo[vars] <- mapply(function(x, y) {
             ind = is.na(x)
             replace(x, ind, y[match(foo$id[ind], bar$id)])
             }, foo[vars], bar[vars])

foo
#  id  x y z
#1  1 10 1 1
#2  2  9 2 2
#3  3 NA 3 3
#4  4  1 4 4
#5  5  3 5 5
#6  6  8 6 6
原始答案

我想这正是你想要的:

foo[-1] <- sapply(foo[-1], function(x) {
    ind = is.na(x)
    replace(x, ind, bar$x[match(foo$id[ind], bar$id)])
})


foo
#  id  x z
#1  1 10 1
#2  2  9 2
#3  3 NA 3
#4  4  1 4
#5  5  3 5
#6  6  8 6
对于除id之外的每一列,我们都会在foo中找到缺少的值,并用bar中相应的值替换它

您可以在非分组列的交叉点上迭代dplyr::coalesce。它并不优雅,但它的伸缩性应该相当好:

图书馆管理员 foo%向列表元素添加名称 bind_colsfoobar%>%bind到data.frame,cbind到foobar 选择UnionNamesFoo,namesbar删除重复的列 福巴 >一个tibble:6x4 >id x y z > > 1 1 10 1 1 > 2 2 9 2 2 >3 NA 3 3 > 4 4 1 4 4 > 5 5 3 5 5 > 6 6 8 6 6 您可以在非分组列的交叉点上迭代dplyr::coalesce。它并不优雅,但它的伸缩性应该相当好:

图书馆管理员 foo%向列表元素添加名称 bind_colsfoobar%>%bind到data.frame,cbind到foobar 选择UnionNamesFoo,namesbar删除重复的列 福巴 >一个tibble:6x4 >id x y z > > 1 1 10 1 1 > 2 2 9 2 2 >3 NA 3 3 > 4 4 1 4 4 > 5 5 3 5 5 > 6 6 8 6 6
另一次尝试,本质上只应该是一次赋值操作。再次使用@alistaire的数据:

vars <- c("x","y")
foo[vars] <- Map(pmax, foo[vars], bar[match(foo$id, bar$id), vars], na.rm=TRUE)
foo

#  id  x y z
#1  1 10 1 1
#2  2  9 2 2
#3  3 NA 3 3
#4  4  1 4 4
#5  5  3 5 5
#6  6  8 6 6

另一次尝试,本质上只应该是一次赋值操作。再次使用@alistaire的数据:

vars <- c("x","y")
foo[vars] <- Map(pmax, foo[vars], bar[match(foo$id, bar$id), vars], na.rm=TRUE)
foo

#  id  x y z
#1  1 10 1 1
#2  2  9 2 2
#3  3 NA 3 3
#4  4  1 4 4
#5  5  3 5 5
#6  6  8 6 6

这是更新加入问题,我还没有看到一个真正好的答案。目前,我选择了左边的joinfoo,bar,by='id%%>%mutatex=coalescex.x,x.y%>%select contains'。似乎应该有一种方法避免手动为每个变量键入mutatefoo=coalescefoo.x,foo.y,因为我有数千个变量。我一直在考虑使用apply的方法,但我有点不知所措。在data.table-foo[bar,on=.id,x:=I.x]@user3614648中,有一种编程方法可以对列名进行操作,但坦率地说,它并不简单或漂亮。nm这是更新连接问题,我还没有看到真正好的答案。目前,我选择了左边的joinfoo,bar,by='id%%>%mutatex=coalescex.x,x.y%>%select contains'。似乎应该有一种方法避免手动为每个变量键入mutatefoo=coalescefoo.x,foo.y,因为我有数千个变量。我一直在考虑使用apply的方法,但我有点不知所措。在data.table-foo[bar,on=.id,x:=I.x]@user3614648中,有一种编程方法可以对列名进行操作,但坦率地说,这不是简单或漂亮的.nm。如果你能想出一种不用键入$x就能跨数千列执行此操作的方法,我很乐意接受这个答案。也许这可以通过定义f来实现
我的解决方案有一个潜在的更大的问题,即合并可以更改列名。我的猜测是,您至少需要使用我的解决方案进行一些手动重命名。像@akrun这样的人会给你一个更好的答案。合并部分大部分是无关的。我仍然可以使用dplyr::left_join,然后继续使用df$x.x[is.nadf$x.x也许你应该更新你的问题,让我们知道实际问题是什么样子的。在这种情况下,一个最小的示例/解决方案似乎并不能解决这个问题。数据是相同的,只是数据框中有更多的变量,如x和Z。如果你能想出一种方法,不必键入$x,就可以跨数千列执行此操作,我很乐意接受这个答案。也许这可以通过定义一个函数来实现,这个函数可以实现上述功能,但是可以实现吗?我的解决方案有一个潜在的更大的问题,那就是合并可以更改列名。我想你至少需要用我的解决方案进行一些手动重命名。像@akrun这样的人会给你一个更好的答案er答案。合并部分大部分是无关的。我仍然可以使用dplyr::left_join,然后继续使用df$x.x[is.nadf$x.x也许你应该更新你的问题,让我们知道实际问题是什么样子的。在这种情况下,一个最小的例子/解决方案似乎并不能解决它。数据是相同的,只是数据框中有更多的变量,如x和Z。如果我没有弄错的话,这只在bar的ID位于fo的第一行时起作用o、 正确吗?@user3614648-不,它将以任何顺序工作-match将从bar中选择顺序。如果我没有弄错,请尝试foo,这仅在bar的ID位于foo的第一行时有效,正确吗?@user3614648-不,它将以任何顺序工作-match将从bar中选择顺序。尝试foo