Viết một hàm tùy chỉnh để chuyển đổi lớp biến trong khung dữ liệu dựa trên một bảng khác

Jan 03 2021

Tôi đang cố gắng viết một hàm có thể thực hiện:

  • data frame ( df_1) có các lớp cột cần được chuyển đổi
  • một khung dữ liệu khác ( df_2) có một hàng cho mỗi biếndf_1
  • một cột trong df_2đó chỉ định lớp mà mỗi biến trong đó df_1sẽ được chuyển đổi thành

Thí dụ

1 - Khung dữ liệu ( df_1) với dữ liệu của tôi (và các lớp của biến để chuyển đổi)

library(tibble)
library(dplyr)

set.seed(2021)

df_1 <-
  tibble(name = c("john", "jack", "mary", "matt", "elizabeth", "richard", "carlos", "george", "ferdinand", "william"), 
       height = sample(155:200, size = 10),
       weight = sample(50:100, size = 10),
       age = sample(20:100, size = 10),
       gender = sample(c("male", "female"), size = 10, replace = TRUE),
       preferred_pet = sample(c("dog", "cat", "frog", "rabbit"), size= 10, replace = TRUE)) %>%
  mutate(across(everything(), as.character))

## # A tibble: 10 x 6
##    name      height weight age   gender preferred_pet
##    <chr>     <chr>  <chr>  <chr> <chr>  <chr>        
##  1 john      161    100    38    female frog         
##  2 jack      192    67     87    female dog          
##  3 mary      193    52     24    male   rabbit       
##  4 matt      166    95     92    male   dog          
##  5 elizabeth 160    89     82    female cat          
##  6 richard   199    75     57    male   dog          
##  7 carlos    195    85     37    female rabbit       
##  8 george    159    86     62    male   rabbit       
##  9 ferdinand 177    71     78    female cat          
## 10 william   197    80     89    female rabbit 

2 - Khung dữ liệu ( df_2) với các lớp để chuyển đổi df_1cột thành

set.seed(2021)

df_2 <-
  tibble(var_name =  c("name", "height", "weight", "gender", "preferred_pet", "record_creation"),
         var_class = c("character", "numeric", "numeric", "factor", "factor", "datetime")) %>%
  slice(sample(1:n()))

## # A tibble: 6 x 2
##   var_name        var_class
##   <chr>           <chr>    
## 1 weight          numeric  
## 2 record_creation datetime 
## 3 height          numeric  
## 4 name            character
## 5 gender          factor   
## 6 preferred_pet   factor 

3 - Xây dựng một chức năng để chuyển đổi lớp

Tôi đã thấy giải pháp của @ akrun ở đây , có vẻ khá gần với những gì tôi đang cố gắng đạt được.

library(purrr)
library(stringr)

my_df <- iris
my_types <- c("factor", "character", "double", "logical", "character")
my_df[] <- map2(my_df, str_c("as.", my_types), ~ get(.y)(.x))

Tuy nhiên, giải pháp này không giải quyết các tình huống như của tôi, trong đó các tên biến df_1không nhất thiết phải xuất hiện df_2và tương tự, df_2$var_namebao gồm các biến không nhất thiết phải xuất hiện df_1.

Tôi rất vui vì bất kỳ ý tưởng nào về việc xây dựng một hàm để chuyển đổi df_1các lớp vars của theo thông tin tìm thấy trong df_2. Tìm một giải pháp bằng cách sử dụng các tidyversehàm sẽ là lý tưởng. Cảm ơn!

Trả lời

1 IanCampbell Jan 03 2021 at 00:26

Đây là một cách tiếp cận tận dụng acrosscur_column:

library(dplyr) #version >= 1.0.0
df_1 %>% 
  mutate(across(any_of(df_2$var_name), ~get(paste0("as.",df_2[df_2$var_name == cur_column(),"var_class"]))(.x)))
# A tibble: 10 x 6
   name      height weight age   gender preferred_pet
   <chr>      <dbl>  <dbl> <chr> <fct>  <fct>        
 1 john         161    100 38    female frog         
 2 jack         192     67 87    female dog          
 3 mary         193     52 24    male   rabbit       
 4 matt         166     95 92    male   dog          
 5 elizabeth    160     89 82    female cat          
 6 richard      199     75 57    male   dog          
 7 carlos       195     85 37    female rabbit       
 8 george       159     86 62    male   rabbit       
 9 ferdinand    177     71 78    female cat          
10 william      197     80 89    female rabbit 

Trình any_oftrợ giúp lựa chọn đảm bảo rằng bạn chỉ cố gắng thay đổi các cột có trong đó df_2.

Đối số thứ hai là hàm được áp dụng cho các cột hiện diện. Bạn có thể sử dụng cur_column()để có quyền truy cập vào tên của cột đang bị thay đổi. Từ đó, chúng tôi chỉ cần tra cứu tên cột đó df_2và trả về tên var_classbạn muốn. Sau đó, sử dụng get()từ cơ sở R để trả về hàm thích hợp và áp dụng hàm đó cho cột với (.x).

Nếu bạn muốn xác định một hàm và chuyển các tên cột không được trích dẫn như bạn làm với các hàm ngăn nắp khác, bạn có thể sử dụng rlang::enquo:

library(rlang)
change_class_by_table <- function(data,data_ref,column_name,column_class){
data %>% 
  mutate(across(any_of(pull(data_ref,!!enquo(column_name))), 
                ~get(paste0("as.",filter(data_ref, !!enquo(column_name) == cur_column()) %>%
                                    pull(!!enquo(column_class))))(.x)))
}
change_class_by_table(df_1,df_2,var_name,var_class)
## A tibble: 10 x 6
#   name      height weight age   gender preferred_pet
#   <chr>      <dbl>  <dbl> <chr> <fct>  <fct>        
# 1 john         161    100 38    female frog         
# 2 jack         192     67 87    female dog          
# 3 mary         193     52 24    male   rabbit  
# ...