Как создать функцию, которая переименовывает имена столбцов на основе указанного словаря? - PullRequest
1 голос
/ 06 февраля 2020

Я нахожусь в процессе создания пакета анализа данных, который собирает данные из различных источников. Во многих случаях имена столбцов имеют непоследовательные названия для одного и того же типа данных в каждом источнике данных.

В моем случае я ищу создание функции переименования, которая вызывает словарь в формате tbl_df и переименовывает столбцы.

Этот пример работает, когда есть новое имя столбца для всех исходных столбцов, но не когда есть дополнительный столбец, который не является частью словаря в tbl_df

library(tidyverse)

df1 <- tibble::tribble(
  ~name, ~birthday, ~height,
  "John Smith", "01/20/1990", "5'10"
)

df2 <- tibble::tribble(
  ~name, ~score, ~grade,
  "John Smith", 95, 8
)


# column renaming dictionaries
people_dictionary <- tibble::tribble(
  ~namePeople, ~nameActual,
  "name", "studentName",
  "birthday", "dob",
  "height", "height"
)

test_dictionary <- tibble::tribble(
  ~nameTest, ~nameActual,
  "name", "studentName",
  "score", "examScore",
  "grade", "schoolGrade"
)

rename_function <- function(data, data_source = "people") {
  # find dictionary based on data source
  if (data_source == "people") {
    actual_names_df <- people_dictionary
  }
  if (data_source == "test") {
    actual_names_df <- test_dictionary
  }

  # get column names of data
  original_names <- colnames(data)

  # create column name filter depending on data source
  name_columns <- case_when(
    data_source == "people" ~ "namePeople",
    data_source == "people" ~ "nameTest"
  )

  # Match Original Names to Renamed Column Names
  actual_names <-
    seq_along(original_names) %>%
    purrr::map_chr(function(x) {
      actual <-
        actual_names_df %>%
        # rlang used to unquote dynamic name column
        filter((!!rlang::sym(name_columns)) == original_names[x]) %>%
        .$nameActual
    })

  # rename columns
  data <- data %>%
    purrr::set_names(actual_names)
}

renamed_df1 <- df1 %>% rename_function(data_source = "people")

# original df
df1
#> # A tibble: 1 x 3
#>   name       birthday   height
#>   <chr>      <chr>      <chr> 
#> 1 John Smith 01/20/1990 5'10

# renamed columns of df1
renamed_df1
#> # A tibble: 1 x 3
#>   studentName dob        height
#>   <chr>       <chr>      <chr> 
#> 1 John Smith  01/20/1990 5'10

# additional column not named in dictionary
df3 <- tibble::tribble(
  ~name, ~birthday, ~height, ~weight,
  "John Smith", "01/20/1990", "5'10", 165
)

df3 %>% rename_function(data_source = "people")
#> Error: Result 4 must be a single string, not a character vector of length 0

Создано в 2020-02-06 пакетом Представитель (v0.3.0)

Есть несколько частей моей функции переименования, которые я считаю, может быть улучшено:

  1. Есть ли лучший способ вызвать правильный словарь (tbl_df), который не является тонной из if операторов? Смогу ли я создать tbl_df или csv, в котором есть столбец для data_source и другой столбец, в котором указано имя tbl_df в пакете?
tibble::tribble(
  ~data_source,    ~dictionary_name,
      "people", "people_dictionary",
        "test",   "test_dictionary"
  )
Как я могу переделать свою функцию, чтобы следовать аналогичному рабочему процессу, но не получить ошибку переименования, когда у всех столбцов нет новых имен в словаре? Есть ли лучший процесс для хранения "словарей "в пакете r для моего варианта использования?

1 Ответ

0 голосов
/ 08 февраля 2020

Я думаю, что вы можете иметь один словарь для всех импортированных наборов данных. Хитрость заключается в том, чтобы использовать именованный вектор для старых и новых имен переменных с rename. Вы все еще можете иметь словарь в tibble и создавать именованный вектор с tibble::deframe

[обратите внимание, что именованный вектор должен быть c(new1 = old1, new2 = old2,..)

library(tidyverse)

# Our dictionary
dictionary <- tibble::tibble(
  new = c("studentName", "dob", "height", "examScore", "schoolGrade"),
  old = c("name", "birthday", "height", "score", "grade")
)

dictionary
#> # A tibble: 5 x 2
#>   new         old     
#>   <chr>       <chr>   
#> 1 studentName name    
#> 2 dob         birthday
#> 3 height      height  
#> 4 examScore   score   
#> 5 schoolGrade grade

# The data with a column that is not present in the dictionary
data <- tibble::tibble(
  name = rnorm(5),
  score = rnorm(5),
  grade = rnorm(5),
  new_var = rnorm(5)
)

data
#> # A tibble: 5 x 4
#>     name  score  grade new_var
#>    <dbl>  <dbl>  <dbl>   <dbl>
#> 1  0.678 -0.431  0.753  1.43  
#> 2  0.602 -0.532  1.15   0.356 
#> 3  1.68   1.40   0.410  0.0729
#> 4  0.817  1.84  -0.292  0.523 
#> 5 -0.316  0.954 -1.02   1.16


# get the data variable names where there is a new name in the dictionary
vars_found_in_dictionary <- intersect(names(data), unique(dictionary$old))

# create a temporary dictionary as a named vector
temp_dict <- dictionary %>% dplyr::filter(old %in% vars_found_in_dictionary) %>% tibble::deframe()

temp_dict
#> studentName   examScore schoolGrade 
#>      "name"     "score"     "grade"

# rename those variables only
data %>% dplyr::rename(!!temp_dict)
#> # A tibble: 5 x 4
#>   studentName examScore schoolGrade new_var
#>         <dbl>     <dbl>       <dbl>   <dbl>
#> 1       0.678    -0.431       0.753  1.43  
#> 2       0.602    -0.532       1.15   0.356 
#> 3       1.68      1.40        0.410  0.0729
#> 4       0.817     1.84       -0.292  0.523 
#> 5      -0.316     0.954      -1.02   1.16

Создан в 2020-02-08 с помощью пакета Представить (v0.3.0)

Кроме того, при создании функции лучше всего проверить, нет ли соответствующей переменной имена в словаре

РЕДАКТИРОВАТЬ

Поскольку вы хотите выбрать словарь

library(tidyverse)

people <- tibble::tibble(
  name = rnorm(5),
  birthady = rnorm(5),
  new_var = rnorm(5)
  )

test <- tibble::tibble(
  grade = rnorm(5),
  score = rnorm(5)
  )

people_dictionary <- tibble::tibble(
  new = c("studentName", "dob", "height"),
  old = c("name", "birthday", "height")
  )


test_dictionary <- tibble::tibble(
  new = c( "examScore", "schoolGrade"),
  old = c("score", "grade")
  )

dictionary_list <- 
  list( "people" = "people_dictionary",
        "test" =   "test_dictionary"
  )


  dictionary <- function(data) { 

    dictionary <- deparse(substitute(data))

    eval(parse(text= dictionary_list[dictionary][[1]]))

    }


dictionary(people)
#> # A tibble: 3 x 2
#>   new         old     
#>   <chr>       <chr>   
#> 1 studentName name    
#> 2 dob         birthday
#> 3 height      height

dictionary(test)
#> # A tibble: 2 x 2
#>   new         old  
#>   <chr>       <chr>
#> 1 examScore   score
#> 2 schoolGrade grade

Создано в 2020-02-08 представительный пакет (v0.3.0)

...