R:根据列预先修复和后缀有条件地替换值

时间:2017-06-05 01:10:47

标签: r database merge data-manipulation

我有两个数据框。数据框A具有许多观察/行,每个观察的ID以及许多其他列。对于观察X的子集,缺少一组列的值/ NA。数据框B包含X中的观察的子集(可以使用ID在数据帧中匹配)和具有与数据框A中相同名称的变量,但包含用于替换缺少/的列集中的缺失值的值NA。

下面的代码(使用连接操作)只是添加列而不是替换缺失值。对于B中的每个附加变量(让它们命名为W),结果表生成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")

我正在尝试使用基于ID的B中的值替换A中的缺失值,但是由于我有许多列和许多行,因此以有效的方式执行此操作。我的目标是:

  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 

一种想法是在加入后使用ifelse(),但为所有变量键入ifelse()函数是不可行的。有没有办法在没有数据库连接的情况下执行此操作,或者是否有一种方法可以在.x结尾的所有列中应用函数,以便将.x中的值替换为.y中的值,如果.x中的值丢失了?

4 个答案:

答案 0 :(得分:3)

编辑

使用@alistaire的示例数据框更新答案。

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

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

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中的相应值。

答案 1 :(得分:3)

另一种尝试应该基本上只是一个赋值操作。再次使用@ 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

答案 2 :(得分:2)

如果您不介意详细的baseR方法,那么您可以使用merge()轻松完成此操作并仔细分组数据框。

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

答案 3 :(得分:2)

您可以在非分组列的交叉处迭代dplyr::coalesce。它并不优雅,但它应该能够很好地扩展:

library(tidyverse)

foo <- data.frame(id = seq(1:6), 
                  x = c(NA, NA, NA, 1, 3, 8), 
                  y = 1:6,    # add extra shared variable
                  z = seq_along(10:15))
bar <- data.frame(id = seq(1:2), 
                  y = c(1L, NA),
                  x = c(10, 9))

# names of non-grouping variables in both
vars <- intersect(names(foo), names(bar))[-1]

foobar <- left_join(foo, bar, by = 'id')

foobar <- vars %>% 
    map(paste0, c('.x', '.y')) %>%    # make list of columns to coalesce
    map(~foobar[.x]) %>%    # for each set, subset foobar to a two-column data.frame 
    invoke_map(.f = coalesce) %>%    # ...and coalesce it into a vector
    set_names(vars) %>%   # add names to list elements
    bind_cols(foobar) %>%   # bind into data.frame and cbind to foobar
    select(union(names(foo), names(bar)))    # drop duplicated columns

foobar
#> # A tibble: 6 x 4
#>      id     x     y     z
#>   <int> <dbl> <int> <int>
#> 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