Purrr - условно изменить столбец в списке фреймов данных, если он существует - PullRequest
1 голос
/ 31 октября 2019

Рассмотрим следующий список фреймов данных:

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

Я хотел бы объединить элементы A и B в столбец B, если это не так. Обратите внимание, что не все фреймы данных имеют столбец B.

Для этого необходимо выполнить следующие условия:

  1. во фрейме данных должны быть оба столбца: A и B
  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 должен быть переписан. df2, df3 и df4 должны остаться без изменений.
Ожидаемый результат:

$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 ]

5 голосов
/ 31 октября 2019

Вы можете сначала проверить, есть ли A и B в именах столбцов, если да, то проверить, не соответствует ли первый элемент (str_sub(B, 1, 1)) A, если да, тогда объедините A и B

с map_if как предложено @ Moody_Mudskipper

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 голос
/ 31 октября 2019

Я не уверен, что понял ваш вопрос, но вот попытка ответить на него:

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)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...