dplyr join定义NA值

Tri*_*mus 17 r left-join na dplyr

我可以在dplyr join中为NA定义"填充"值吗?例如,在连接中定义所有NA值应为1?

require(dplyr)
lookup <- data.frame(cbind(c("USD","MYR"),c(0.9,1.1)))
names(lookup) <- c("rate","value")
fx <- data.frame(c("USD","MYR","USD","MYR","XXX","YYY"))
names(fx)[1] <- "rate"
left_join(x=fx,y=lookup,by=c("rate"))
Run Code Online (Sandbox Code Playgroud)

上面的代码将为值"XXX"和"YYY"创建NA.在我的情况下,我加入了大量的列,将会有很多不匹配.所有不匹配应具有相同的值.我知道我可以分几步完成,但问题是一切都可以完成吗?谢谢!

tal*_*lat 17

首先,我建议不要使用该组合data.frame(cbind(...)).原因如下:如果只传递原子向量,则默认cbind创建一个matrix.R中的矩阵只能有一种类型的数据(将矩阵看作具有维度属性的向量,即行数和列数).因此,你的代码

cbind(c("USD","MYR"),c(0.9,1.1))
Run Code Online (Sandbox Code Playgroud)

创建一个字符矩阵:

str(cbind(c("USD","MYR"),c(0.9,1.1)))
# chr [1:2, 1:2] "USD" "MYR" "0.9" "1.1"
Run Code Online (Sandbox Code Playgroud)

虽然您可能期望最终数据框具有字符或因子列(速率)和数字列(值).但你得到的是:

str(data.frame(cbind(c("USD","MYR"),c(0.9,1.1))))
#'data.frame':  2 obs. of  2 variables:
# $ X1: Factor w/ 2 levels "MYR","USD": 2 1
# $ X2: Factor w/ 2 levels "0.9","1.1": 1 2
Run Code Online (Sandbox Code Playgroud)

因为data.frame默认情况下使用时字符串(字符)会转换为因子(您可以通过stringsAsFactors = FALSEdata.frame()调用中指定来绕过它).

我建议使用以下替代方法来创建示例数据(还要注意,您可以在同一个调用中轻松指定列名):

lookup <- data.frame(rate = c("USD","MYR"), 
                     value = c(0.9,1.1))

fx <- data.frame(rate = c("USD","MYR","USD","MYR","XXX","YYY"))
Run Code Online (Sandbox Code Playgroud)

现在,对于你的实际问题,如果我理解正确,你想NA1连接数据中的a 替换所有s .如果这是正确的,这是一个使用left_joinmutate_each执行此操作的自定义函数:

library(dplyr)
left_join_NA <- function(x, y, ...) {
  left_join(x = x, y = y, by = ...) %>% 
    mutate_each(funs(replace(., which(is.na(.)), 1)))
}
Run Code Online (Sandbox Code Playgroud)

现在您可以将它应用于您的数据,如下所示:

> left_join_NA(x = fx, y = lookup, by = "rate")
#  rate value
#1  USD   0.9
#2  MYR   1.1
#3  USD   0.9
#4  MYR   1.1
#5  XXX   1.0
#6  YYY   1.0
#Warning message:
#joining factors with different levels, coercing to character vector 
Run Code Online (Sandbox Code Playgroud)

请注意,最终会得到一个字符列(rate)和一个数字列(value),并且所有NA都将替换为1.

str(left_join_NA(x = fx, y = lookup, by = "rate"))
#'data.frame':  6 obs. of  2 variables:
# $ rate : chr  "USD" "MYR" "USD" "MYR" ...
# $ value: num  0.9 1.1 0.9 1.1 1 1
Run Code Online (Sandbox Code Playgroud)

  • @nelsonauner还有`tidyr`的`replace_na()`和`fill()`命令. (10认同)
  • 首先感谢全面的数据创建提示.这实际上对未来的问题很有帮助.其次,你的配方很整洁,我会用它.非常感谢! (2认同)
  • 为什么不使用`fx [is.na(fx)] < - 1`? (2认同)

Raf*_*yas 9

如果您仍在使用dplyr,则不妨利用dplyr::coalesce,并使用dplyr语法将其传递为1或0。我认为这看起来不错...

... %>%
mutate_if(is.numeric,coalesce,0)
Run Code Online (Sandbox Code Playgroud)

哪里0是传递给dplyr::coalesceNA 的参数。

在问题的示例中,存在带有因子的数据框。我有信心不会将汇率作为因素,也不会将NA替换为零,因此我继续在下面添加此步骤,只是为了使答案在提供的示例之后可执行。

# replace NAs with zeros for all numeric columns
#
# ... code from question above
left_join(x=fx,y=lookup,by=c("rate")) %>%
    # ignore if factors in value column are because it's a toy example
    mutate(value = as.numeric(as.character(value))) %>%
    # the good stuff here
    mutate_if(is.numeric,coalesce,0)
Run Code Online (Sandbox Code Playgroud)

  • 或者,使用较新的 tidy-select 语法:`mutate(across(where(is.numeric), coalesce, 0))` (4认同)

Maë*_*aël 7

解决方案是在 join 之后tidyverse使用:tidyr::replace_na

left_join(x = fx, y = lookup, by = c("rate")) %>% 
  replace_na(list(value = 0))
Run Code Online (Sandbox Code Playgroud)

或者,对于更一般的情况:

left_join(x = fx, y = lookup, by = c("rate")) %>% 
  mutate(across(where(is.numeric), ~ replace_na(.x, 0)))
Run Code Online (Sandbox Code Playgroud)


小智 6

我偶然发现了与 dplyr 相同的问题,并编写了一个小函数来解决我的问题。(解决方案需要 tidyr 和 dplyr)

left_join0 <- function(x, y, fill = 0L){
  z <- left_join(x, y)
  tmp <- setdiff(names(z), names(x))
  z <- replace_na(z, setNames(as.list(rep(fill,   length(tmp))), tmp))
  z
}
Run Code Online (Sandbox Code Playgroud)

最初回答于:R Left Outer Join with 0 Fill 而不是 NA While Preserving Valid NA's in Left Table