Schreiben einer benutzerdefinierten Funktion zum Konvertieren einer Variablenklasse in einen Datenrahmen basierend auf einer anderen Tabelle

Jan 03 2021

Ich versuche eine Funktion zu schreiben, die Folgendes aufnehmen könnte:

  • Datenrahmen ( df_1), dessen Spaltenklassen konvertiert werden müssen
  • ein anderer Datenrahmen ( df_2), der eine Zeile für jede Variable von hatdf_1
  • Eine Spalte, in df_2der die Klasse angegeben ist, in die jede Variable df_1konvertiert werden soll

Beispiel

1 - Datenrahmen ( df_1) mit meinen Daten (und den zu konvertierenden Variablenklassen)

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 - Datenrahmen ( df_2) mit Klassen, in die df_1Spalten konvertiert werden sollen

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 - Erstellen einer Funktion für die Klassenkonvertierung

Ich habe @ akrun Lösung gesehen hier , die ziemlich nah scheint , was ich versuche zu erreichen.

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

Diese Lösung adressiert jedoch nicht Situationen wie meine, in denen Variablennamen von df_1nicht unbedingt in df_2und in ähnlicher Weise df_2$var_nameVariablen enthalten, die nicht unbedingt in vorkommen df_1.

Ich freue mich über jede Idee, eine Funktion zum Konvertieren df_1der vars-Klassen gemäß den Informationen in zu konstruieren df_2. tidyverseIdeal wäre es, eine Lösung mit Funktionen zu finden. Vielen Dank!

Antworten

1 IanCampbell Jan 03 2021 at 00:26

Hier ist ein Ansatz leveraging acrossund 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 

Der any_ofAuswahlhelfer stellt sicher, dass Sie nur versuchen, Spalten zu mutieren, die in vorhanden sind df_2.

Das zweite Argument ist die Funktion, die auf die vorhandenen Spalten angewendet wird. Sie können verwenden cur_column(), um Zugriff auf den Namen der Spalte zu haben, die mutiert wird. Von dort aus suchen wir einfach den Spaltennamen nach df_2und geben den var_classgewünschten zurück. Verwenden Sie dann get()von Basis R, um die entsprechende Funktion zurückzugeben und diese auf die Spalte mit anzuwenden (.x).

Wenn Sie eine Funktion definieren und die Spaltennamen wie bei anderen Tidyverse-Funktionen ohne Anführungszeichen übergeben möchten, können Sie Folgendes verwenden 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  
# ...