Menulis fungsi kustom untuk mengonversi kelas variabel dalam kerangka data berdasarkan tabel lain

Jan 03 2021

Saya mencoba untuk menulis fungsi yang dapat mengambil:

  • data frame ( df_1) yang kelas kolomnya perlu diubah
  • bingkai data lain ( df_2) yang memiliki baris untuk setiap variabeldf_1
  • kolom df_2yang menentukan kelas masing-masing variabel df_1harus dikonversi

Contoh

1 - Bingkai data ( df_1) dengan data saya (dan kelas variabel yang akan diubah)

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 - Data frame ( df_2) dengan kelas untuk mengubah df_1kolom menjadi

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 - Membangun fungsi untuk konversi kelas

Saya telah melihat solusi @ akrun di sini , yang tampaknya cukup dekat dengan apa yang saya coba capai.

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

Namun, solusi ini tidak mengatasi situasi seperti saya, di mana nama variabel df_1tidak selalu muncul df_2, dan demikian pula, df_2$var_namemenyertakan variabel yang tidak selalu muncul di df_1.

Saya akan senang mendapatkan ide untuk membuat fungsi untuk mengubah df_1kelas vars menurut info yang ditemukan di df_2. Menemukan solusi menggunakan tidyversefungsi akan ideal. Terima kasih!

Jawaban

1 IanCampbell Jan 03 2021 at 00:26

Berikut adalah pendekatan yang memanfaatkan acrossdan cur_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 

The any_ofpemilihan penolong menjamin bahwa Anda hanya mencoba untuk kolom mutasi yang hadir dalam df_2.

Argumen kedua adalah fungsi yang diterapkan ke kolom yang ada. Anda dapat menggunakan cur_column()untuk memiliki akses ke nama kolom yang sedang dimutasi. Dari sana, kami hanya mencari nama kolom itu df_2dan mengembalikan yang var_classAnda inginkan. Kemudian gunakan get()dari basis R untuk mengembalikan fungsi yang sesuai dan menerapkannya ke kolom dengan (.x).

Jika Anda ingin mendefinisikan suatu fungsi, dan meneruskan nama kolom tanpa tanda kutip seperti yang Anda lakukan dengan fungsi tidyverse lainnya, Anda dapat menggunakan 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  
# ...