【问题标题】:Binding dataframes with different column names by row按行绑定具有不同列名的数据框
【发布时间】:2020-10-16 11:00:34
【问题描述】:

我将此 Excel 工作表作为数据框列表导入。我想将列表合并到一个数据框中。 bind_rows() 让我可以轻松地将数据帧相加,但问题是我有一个变量/列在每个数据帧中具有不同的名称。 bind_row() 默认情况下会创建两个单独的列,其他数据帧中的数据为空值。如何加入这些专栏?

示例代码:

# Sample dataframes
df1 <- tibble(A = c(1,2,3),
              B = c("X","Y","Z"),
              C = c(T,F,F)
                  )
df2 <- tibble(A = c(3,4,5),
              B = c("U","V","W"),
              D = c(T,T,F)
)

# List of dataframes
my_ls <- list(df1, df2)
my_ls
[[1]]
# A tibble: 3 x 3
      A B     C    
  <dbl> <chr> <lgl>
1     1 X     TRUE 
2     2 Y     FALSE
3     3 Z     FALSE

[[2]]
# A tibble: 3 x 3
      A B     D    
  <dbl> <chr> <lgl>
1     3 U     TRUE 
2     4 V     TRUE 
3     5 W     FALSE

# Creating joined dataframe:
my_df <- bind_rows(my_ls)
my_df
# Current outcome: A tibble: 6 x 4
      A B     C     D    
  <dbl> <chr> <lgl> <lgl>
1     1 X     TRUE  NA   
2     2 Y     FALSE NA   
3     3 Z     FALSE NA   
4     3 U     NA    TRUE 
5     4 V     NA    TRUE 
6     5 W     NA    FALSE

期望的结果:

# Desired outcome: A tibble: 6 x 3
      A B     C         
  <dbl> <chr> <lgl> 
1     1 X     TRUE    
2     2 Y     FALSE    
3     3 Z     FALSE    
4     3 U     TRUE 
5     4 V     TRUE 
6     5 W     FALSE

目前,我一直在使用mutate()case_when(),我检查哪一列不为空(!is.na())。这行得通,但我不禁认为一定有更简单的方法。

# Example using mutate
my_df <- my_df %>% 
  mutate(
    C = case_when(is.na(C)  & !is.na(D) ~ D,
                  !is.na(C) & is.na(D)  ~ C,
                  # The lines below may be a bit redundant for my purpose, since the dataframes either have the C or D variable.
                  !is.na(C) & !is.na(D) ~ C, # Better would be to return that variable has overlapping information
                  is.na(C)  & is.na(D)  ~ NA
                 )
         ) %>%
        select(-D)
my_df
# A tibble: 6 x 3
      A B     C    
  <dbl> <chr> <lgl>
1     1 X     TRUE 
2     2 Y     FALSE
3     3 Z     FALSE
4     3 U     TRUE 
5     4 V     TRUE 
6     5 W     FALSE

【问题讨论】:

  • 这不行吗? names(df2) = names(df1) df1 %>% bind_rows(df2) 即更改第二个数据帧的列名然后绑定行?
  • 可能是。这将要求数据框的名称以相同的顺序出现,不是吗?我也许应该更好地澄清这一点。实际上,我有多个数据帧,具有变量 C 或 D,并且不一定顺序相同。

标签: r dataframe dplyr


【解决方案1】:

您可以bind_rows 然后使用coalesce 选择非NA 值:

library(dplyr)

bind_rows(my_ls) %>% mutate(C = coalesce(C, D)) %>% select(A:C)

#      A  B     C    
#  <dbl> <chr> <lgl>
#1     1 X     TRUE 
#2     2 Y     FALSE
#3     3 Z     FALSE
#4     3 U     TRUE 
#5     4 V     TRUE 
#6     5 W     FALSE

【讨论】:

  • 谢谢!这个答案很简单,并且适用于我已经编写的代码。也许最好放弃D (select(-D)),而不是选择A:C,因为这将删除冗余变量,无论其在其他变量中的位置如何..
【解决方案2】:

在@KarthikS 发表评论之后,您可以在绑定之前重命名列。我使用rename_with 的方法不需要列按特定顺序排列。为了说明这一点,我使用了一些不同的示例数据框:

library(purrr)
library(dplyr)

d1 <- data.frame(A = 1, B = 2, C = 3)
d2 <- data.frame(A = 4, B = 5, D = 6)
d3 <- data.frame(D = 7, A = 8, B = 9)

d <- list(d1, d2, d3)

map(d, ~ rename_with(.x, ~ "C", matches("^D$"))) %>% 
  bind_rows()
#>   A B C
#> 1 1 2 3
#> 2 4 5 6
#> 3 8 9 7

现在是你的四个数据集:

d <- list(df1, df2)
map(d, ~ rename_with(.x, ~ "C", matches("^D$"))) %>% 
  bind_rows()
#> # A tibble: 6 x 3
#>       A B     C    
#>   <dbl> <chr> <lgl>
#> 1     1 X     TRUE 
#> 2     2 Y     FALSE
#> 3     3 Z     FALSE
#> 4     3 U     TRUE 
#> 5     4 V     TRUE 
#> 6     5 W     FALSE

如果我们以不同的顺序添加一个额外的:

df3 <- tibble(D = c(T,T,F),
              A = c(7,8,9),
              B = c("A","B","C"))

d <- list(df1, df2, df3)
map(d, ~ rename_with(.x, ~ "C", matches("^D$"))) %>% 
  bind_rows()
#> # A tibble: 9 x 3
#>       A B     C    
#>   <dbl> <chr> <lgl>
#> 1     1 X     TRUE 
#> 2     2 Y     FALSE
#> 3     3 Z     FALSE
#> 4     3 U     TRUE 
#> 5     4 V     TRUE 
#> 6     5 W     FALSE
#> 7     7 A     TRUE 
#> 8     8 B     TRUE 
#> 9     9 C     FALSE

reprex package (v0.3.0) 于 2020 年 10 月 16 日创建

【讨论】:

  • 这是一个简短而简单的解决方案。你能详细说明“^D$”中的“^”和“$”是做什么的吗?是否可以在重命名之前检查数据框中是否存在“C”和“D”列?
  • "^" 标记字符串的开始,而"$" 标记结束。因此模式"^D$" 只匹配完全命名为“D”的列。
【解决方案3】:

为了快速回答而跳出tidyverse道歉

expl <- read.table(text= " A B     C     D    
1     1 X     TRUE  NA   
2     2 Y     FALSE NA   
3     3 Z     FALSE NA   
4     3 U     NA    TRUE 
5     4 V     NA    TRUE 
6     5 W     NA    FALSE")

expl$E <- ifelse(is.na(expl$C), expl$D, expl$C)

print(expl)

或许

expl[,c("C", "D")] %>% rowMeans(na.rm = TRUE) %>% as.logical()

编辑:将后者翻译成整洁:

expl %>% select("C", "D") %>% rowMeans(na.rm = TRUE) %>% as.logical()

第一条评论后编辑:

如果您想要更多控制权,您可能应该在类似于以下示例的函数中编写每种情况下您想要做的事情:

library(magrittr)

expl <- read.table(text= " A B     C     D    
1     1 X     TRUE  NA   
2     2 Y     FALSE NA   
3     3 Z     FALSE NA   
4     3 U     NA    TRUE 
5     4 V     NA    TRUE 
6     5 W     NA    FALSE
7     7 I     NA    NA
8     9 J     TRUE  TRUE")

myfun <- function(a, b){
  if(is.na(a) & is.na(b)) 
     return(NA)
  if(!is.na(a) & !is.na(b)) {
    warning("too much information, a and b set!")
    return(NaN)
  }
  return(max(a, b, na.rm=TRUE))
}

myfun = Vectorize(myfun)

myfun(expl$C, expl$D) %>% as.logical()

【讨论】:

  • 您的上层解决方案似乎是我想要的。假设,对于更白痴的解决方案。假设两列都有信息,我想在转移信息并删除 D 列之前检查是否有信息丢失?
  • 我添加了一个解决方案,可以为您提供非常精细的控制:只需编写一个函数,在这种情况下究竟应该发生什么,将其向量化,其余的就很容易了。 HTH
  • 您能否详细说明矢量化的作用?我是否正确理解代码,这仅适用于逻辑输入?我更喜欢通用的单线。但是使用条件是测试重叠信息的好方法。
  • 这只是将结果转换为逻辑的最后一步,因为那是问题所在。如果您删除它,它适用于所有数字。对于字符,您需要明智地更改最大部分。向量化是使接受两个值的函数接受两个向量的一种聪明方法。
猜你喜欢
  • 1970-01-01
  • 2018-08-12
  • 1970-01-01
  • 1970-01-01
  • 2016-09-28
  • 1970-01-01
  • 2019-12-05
  • 2017-01-01
  • 2019-12-06
相关资源
最近更新 更多