Purrr-有条件地更改数据帧列表中的一列

时间:2019-10-31 12:03:50

标签: r list dplyr purrr

请考虑以下数据帧列表:

library(tidyverse)

df1 <- tibble(
  id = 1:5,
  A = LETTERS[1:5],
  B = letters[10:14]
)
df2 <- tibble(
  id = 1:3,
  A = LETTERS[1:3],
  B = paste(LETTERS[1:3], letters[10:12])
)
df3 <- tibble(
  id = 1:6,
  B = paste(LETTERS[1:6], letters[10:15])
)
df4 <- tibble(
  id = 1:4,
  C = paste(LETTERS[15:18], letters[20:23])
)

df_ls <- list(df1, df2, df3, df4) %>% 
  set_names(paste0("df", 1:4))

如果还不是这样,我想将AB的元素连接到B列中。请注意,并非所有数据框都具有B列。

执行此操作的条件如下:

  1. 数据框必须同时具有AB
  2. B中的第一个字母必须与A中的第一个字母

我正在使用map函数。到目前为止,我的尝试(没有“条件2”):

df_ls %>% 
  map(
    ~ .x %>% 
      mutate_at(
        vars(matches("B")),
        ~ {
          if (c("A", "B") %in% colnames(.) %>% sum() == 2)
            paste(A, B)
          else
            B
        }
      )
  )

它不起作用。

而且,我没有写第二个条件。我尝试了& setequal(. %>% pull(A), . %>% pull(B) %>% word(1)),但没有成功。

修改:
我需要单独保留所有数据帧。 B中仅df1列应被重写。 df2df3df4应该保持不变。
预期的输出是:

$df1
# A tibble: 5 x 3
   id A     B
<int> <chr> <chr>
1     1 A     A j
2     2 B     B k
3     3 C     C l
4     4 D     D m
5     5 E     E n   

$df2
# A tibble: 3 x 3
     id A     B    
  <int> <chr> <chr>
1     1 A     A j  
2     2 B     B k  
3     3 C     C l  

$df3
# A tibble: 6 x 2
     id B    
  <int> <chr>
1     1 A j  
2     2 B k  
3     3 C l  
4     4 D m  
5     5 E n  
6     6 F o  

$df4
# A tibble: 4 x 2
     id C    
  <int> <chr>
1     1 O t  
2     2 P u  
3     3 Q v  
4     4 R w  

2 个答案:

答案 0 :(得分:5)

您可以首先检查A和B是否在列中,如果是,则检查第一个元素(str_sub(B, 1, 1))是否与A不匹配,如果是,则将A和B组合在一起

使用@Moody_Mudskipper建议的map_if

df_ls %>% 
  map_if(~ all(c("A", "B") %in% colnames(.x)), 
         ~ mutate(.x, B = if_else(str_sub(B, 1, 1) != A, paste(A, B), B)))

更详细:

df_ls %>% 
  map(~ {if (all(c("A", "B") %in% colnames(.x))) {
   .x %>% 
      mutate(B = if_else(str_sub(B, 1, 1) != A, paste(A, B), B))
  } else {
    .x
  }})

# $df1
# # A tibble: 5 x 3
#      id A     B    
#   <int> <chr> <chr>
# 1     1 A     A j  
# 2     2 B     B k  
# 3     3 C     C l  
# 4     4 D     D m  
# 5     5 E     E n  
# 
# $df2
# # A tibble: 3 x 3
#      id A     B    
#   <int> <chr> <chr>
# 1     1 A     A j  
# 2     2 B     B k  
# 3     3 C     C l  
# 
# $df3
# # A tibble: 6 x 2
#      id B    
#   <int> <chr>
# 1     1 A j  
# 2     2 B k  
# 3     3 C l  
# 4     4 D m  
# 5     5 E n  
# 6     6 F o  
# 
# $df4
# # A tibble: 4 x 2
#      id C    
#   <int> <chr>
# 1     1 O t  
# 2     2 P u  
# 3     3 Q v  
# 4     4 R w

答案 1 :(得分:1)

我不确定我是否理解您的问题,但是可以尝试回答以下问题:

bind_rows(df_ls) %>% #create on tibble with all data.frames 
      select(id, A, B) %>% #select relevant columns
      filter_at(vars("A", "B"), all_vars(!is.na(.))) %>% #keep only those rows which have columns A and B (condition 1)
      mutate(B = if_else(str_extract(A, "^.") != str_extract(B, "^."), paste(A, B), B)) #if the first letter of B is the same as the first letter in A then keep B otherwise paste A and B together (condition 2)


# A tibble: 8 x 3
     id A     B    
  <int> <chr> <chr>
1     1 A     A j  
2     2 B     B k  
3     3 C     C l  
4     4 D     D m  
5     5 E     E n  
6     1 A     A j  
7     2 B     B k  
8     3 C     C l 

更新:

在发布所需的结果后,可以在此处保留列表:

myfun <- function(df){
  if ("A" %in% colnames(df) & "B" %in% colnames(df)) {
    mutate(df, B = if_else(str_extract(A, "^.") != str_extract(B, "^."), paste(A, B), B))
  } else df
}

df_ls %>% map(myfun)