【问题标题】:Purrr - conditionally mutate a column in a list of data frames when it existsPurrr - 当存在时有条件地改变数据框列表中的列
【发布时间】:2019-10-31 12:03:50
【问题描述】:

考虑以下数据框列表:

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
        }
      )
  )

它不起作用。

另外,我没有设法写出我的第二个条件。我试过&amp; setequal(. %&gt;% pull(A), . %&gt;% pull(B) %&gt;% word(1)),没有成功。

编辑:
我需要单独保存所有数据框。只有df1 中的B 列应该被重写。 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  

【问题讨论】:

  • 您的预期输出是什么?当你说你想(有条件地)连接列 A 和 B 时,我不确定你想要什么。
  • 我在问题中添加了预期的输出。

标签: r list dplyr purrr


【解决方案1】:

您可以先检查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

【讨论】:

  • 我建议使用map_if 稍微简化您的解决方案
【解决方案2】:

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

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)

【讨论】:

  • 感谢您的意见,但我需要单独保存所有数据集。因此使用map
猜你喜欢
  • 2017-09-06
  • 1970-01-01
  • 1970-01-01
  • 2020-02-09
  • 2021-11-10
  • 2019-03-16
  • 2018-11-04
  • 2020-12-06
  • 2020-10-29
相关资源
最近更新 更多