Использование тидиверсии

Недавно я построил модель машинного обучения для классификации использования зданий. В процессе переключения модели на производство формат файла данных, требующих прогнозирования, внезапно изменился. Один из параметров, который модель использует для прогнозирования, - это стоимость проекта. В новом формате данных стоимость указывается не в числовом, а в строковом виде. В некоторых случаях цены указаны в пределах диапазона, а не одного значения. Моя модель не может использовать этот формат, поэтому мне нужно очистить и преобразовать входные данные.

В следующих нескольких строках я покажу вам, как я решил эту проблему, написав функцию, которая выполняет очистку и преобразование за меня. Также я покажу вам, как отобразить разработанную функцию на определенном столбце в тибле.

Решение проблемы

Во-первых, давайте посмотрим на данную структуру данных. Мы сосредоточимся на столбце ProjectValue. Примечание. Вы можете найти полный код в моем репозитории GitHub.

# Look at the given df
# A tibble: 5 x 4
 ProjectID    ProjectName    ProjectMainCatecory    ProjectValue 
   <int>         <chr>             <fct>                <chr> 
1    1         Project A            O             “2.0–3.0 Mio USD”
2    2         Project B            S             “300.0 Mio USD” 
3    3         Project C            N                    “” 
4    4         Project D            C             “0.1–0.4 Mio USD”
5    5         Project E            J                    NA

Мы хотим извлечь стоимость, чтобы использовать ее как числовое значение, а не строку символов.

Мы можем определить четыре базовых случая для всех заданных строк:

Случай 1 - строка с двумя значениями

«2,0–3,0 млн долл. США»

Случай 2 - строка с одним значением

«300,0 млн долл. США»

Случай 3 - строка без значения

“”

Случай 4 - значение NA

NA

По сути, мы хотим избавиться от всей «бесполезной» информации. В данном случае это валюта и порядок величины. Вторая важная вещь - решить проблемы, в которых у нас есть два значения. Я решил взять среднюю стоимость из двух. Это казалось наиболее разумным. Кроме того, нам нужно обрабатывать пустые строки и строки NA соответственно.

Рабочий процесс

Сначала я определяю четыре базовых случая, чтобы проверить функциональность каждого отдельного шага.

# Define base cases
case_1 <- “2.0–3.0 Mio USD”
case_2 <- “300.0 Mio USD”
case_3 <- “”
case_4 <- NA

Затем я начинаю чистить шнурок. Я могу избавиться от всех пробелов и буквенных знаков. Для этого я использую следующий код и проверяю, делает ли он то, что я хочу.

# Delete all spacing
del_space <- str_replace_all(case1, “[:space:]”, “”)
# Output
[1] "2.0-3.0MioUSD"
# Delete all alphabetic components, referring to string without
# spacing
del_char <- str_replace_all(del_space, “[:alpha:]”, “”)
# Output 
[1] "2.0-3.0"

Отлично.

Теперь мы лечим АН.

# Replace NA functionality of tidyr
no_empties <- replace_na(case_4, “”)
# Output
[1] ""

Теперь давайте подумаем, как мы могли бы определить предложение if-else для обработки заданных данных. Мы могли бы использовать длину струн. Однако мы получим крайние случаи, когда длина чистой строки с одним значением равна длине самого короткого варианта для двух значений (например, «30000,0 млн долларов США» будет иметь ту же длину, что и «1,2–1,4». Млн долл. США »). Это, в свою очередь, приведет к ошибкам.

Поэтому я решил использовать знак «-». Мы можем определить простой if-else: например, если мы находим «-» в строке, мы знаем, что это должен быть случай с двумя значениями. Если мы не найдем такого символа, мы знаем, что в данной строке есть только одно значение. Пустые строки будут обработаны в конце предложения if-else. Мы заменим пустые строки значением NA.

Пришло время определить нашу функцию. Назовем это cost_transform. Входными данными этой функции является простая строка, поэтому нам не нужно беспокоиться о квазиквотации, квотах и ​​enquos. Если вас это интересует, прочтите аккуратную виньетку здесь.

# Definiton of cost transform function
cost_transform <- function(string) {
  # Treat NAs as empty strings ("")
  no_empties <- replace_na(string, "")
  # Insert string clean functionality
  del_space <- str_replace_all(no_empties, "[:space:]", "")
  del_char <- str_replace_all(del_space, "[:alpha:]", "")
  # If-else clause definition
  # If no "-" is found return the cost as numeric type
  if (!str_detect(del_char, "-")) {
    cost <- as.numeric(del_char)
    return(cost)
    # If "-" is found return the average of the two values
  } else if (str_detect(del_char, "-")) {
    split <- str_split(del_char, pattern = "-", simplify = TRUE)
    avg_cost <- (as.numeric(split[1]) + as.numeric(split[2])) / 2
    return(avg_cost)
    # If the string is empty, return NA_real_
  } else if (string == "") {
    return(NA_real_)
  }
}

Давайте проверим это на всех наших случаях:

# Test case 1
test_p1 <- cost_transform(case_1)
# Output 
[1] 2.5
# Test case 2
test_p2 <- cost_transform(case_2)
# Output
[1] 300
# Test case 3
test_p3 <- cost_transform(case_3)
# Output
[1] NA
# Test case 4
test_p4 <- cost_transform(case_4)
# Output
[1] NA

Ясс! Оно работает.

Переходим к следующему вопросу. В конечном итоге у нас есть небольшой кусочек, но до сих пор мы тестировали нашу функцию только на отдельных входах. Но цель состоит в том, чтобы отобразить эту функцию на каждое отдельное значение в столбце произвольной длины. Мы могли бы использовать цикл for, но пакет purrr обеспечивает классную функциональность сопоставления. Об этом подробнее здесь".

В нашем случае мы используем одну специальную форму функций отображения. А именно map_dbl (). Эта опция возвращает двойное значение, которое мы и хотим использовать в нашем случае. Наконец, мы можем использовать нашу функцию в конвейере обработки данных и подготовить данные для всего, что нам нужно. В моем случае это будет кормить мою модель машинного обучения для прогнозирования использования при построении.

# See how function works within data wrangling pipeline
df_pipeline <- df %>%
  # Select some columns
  select(ProjectID, ProjectMainCatecory, ProjectValue) %>%
  mutate(
    # Apply map function
    # (only renaming to get direct comparison)
    ProjectValueNew = map_dbl(ProjectValue, cost_transform)
  )
# Output
# A tibble: 5 x 4
  ProjectID ProjectMainCatecory ProjectValue        ProjectValueNew
      <int>    <fct>             <chr>                <dbl>
1         1     O               "2.0 - 3.0 Mio USD"     2.5 
2         2     S               "300.0 Mio USD"         300   
3         3     N               ""                      NA   
4         4     C               "0.1 - 0.4 Mio USD"     0.25
5         5     J               NA                      NA

Примечание. Новый столбец ProjectValueNew создан только для прямого сравнения.

Заключение

Проблема очистки и преобразования данных вездесуща в рабочей среде, связанной с наукой о данных. Таким образом, очень важно извлечь из этого концепцию. Написание функции - обработка отдельных случаев - и последующее сопоставление этой функции со списками или атомарными векторами произвольной длины. Это просто фантастика!