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
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_1
sẽ đượ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_1
cộ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_1
không nhất thiết phải xuất hiện df_2
và tương tự, df_2$var_name
bao 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_1
cá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 tidyverse
hàm sẽ là lý tưởng. Cảm ơn!
Trả lời
Đây là một cách tiếp cận tận dụng across
và 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
Trình any_of
trợ 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_2
và trả về tên var_class
bạ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
# ...