- PVSM.RU - https://www.pvsm.ru -
Несмотря на то, что задачи рядового бизнеса очень часто далеки от популярной темы больших данных и машинного обучения и часто связаны с обработкой относительно малых объёмов информации [десятки мегабайт — десятки гигабайт], размазанной в произвольных представлениях по различным видам источников, применение R в качестве основного инструмента позволяет легко и элегантно автоматизировать и ускорить эти задачи.
И, естественно, после проведения анализа необходимо все это презентовать, для чего можно с успехом использовать Shiny. Далее я приведу ряд трюков и подходов, которые могут помочь в этой задачах. Уверен, что любой практикующий аналитик сможет легко добавить свои хитрости, все зависит от решаемого класса задач.
Ситуация типичная и встречается в любой задаче по несколько раз в день:
Задача: обеспечить импорт такой желеобразной субстанции в структурированное представление, которое можно обрабатывать стандартными средствами (не только R).
Задача разбивается на несколько последовательных этапов. Сразу отмечу, что для решения задачи я стараюсь максимально использовать подходы, реализуемые Hadley Wickham в его философии «tidyverse». Сделано это по нескольким причинам:
Все бы ничего, и можно было бы использовать просто пакет readxl
, но возникают нюансы.
read_excel
надо явно указать спецификацию ВСЕХ колонок как текстовую.data.frame
, а количество, которое воспринимается входным парсером.На отдельных excel файлах конструкция типа
raw <- read_excel(fname)
ctypes <- rep("text", ncol(raw))
cnames <- str_c("grp_", seq_along(ctypes))
raw <- read_excel(fname,
col_types = ctypes,
col_names = cnames)
ломается с сообщением "Error: Need one name and type for each column". Верхеуровневое изучение объектов никакого разумного ответа не дает. Чтобы понять, как действовать, надо изучать githubstackoverflow.
Как резюме, проблематика и способ обхода следующие:
read_excel
этих данных может не быть. Все ещё сильно усугубляется различным поведением разных форматов xls* файлов. Но в 100% случаях для подобных excel файлов количество колонок из read_excel
и в исходном файле отличается.
readxl
. Способ применения вытекает из анализа содержимого пакета на github. Это не очень хорошо, но позволяет решить проблему. При этом надо подхватывать обе ветки (для .xls и для .xlsx файлов) раздельно, несмотря на то, что read_excel
все это скрывает за своим фасадом.Способ решения задачи демонстрирую на примере приведенного выше формата excel файла:
ncol(read_excel("col_test.xlsx")) # 4 колонки
length(readxl:::xlsx_col_types("col_test.xlsx")) # 5 колонок
ncol(read_excel("col_test.xls")) # 2 колонки
length(readxl:::xlsx_col_types("col_test.xls")) # 5 колонок
Получив правильное число колонок дальше без проблем импортируем согласно документации.
Рассматриваем решение задачи на примере подобного excel файла (фрагмент).
Видим, что название колонки по сути размазано по диапазону от 1 до 3-х строчек.
Стратегия достаточно простая и заключается в следующем:
Далее просто один из возможных примеров кода, который позволяет эту задачу решить. В продуктиве можно сворачивать все в цепочку обработки, но для понимания процесса в коде принудительно сделана разбивка по шагам. По ходу решения используются ряд функций, существенно упрощающих жизнь.
repair_names
— для исправления имен импортированных колонок;na.locf
— для заполнения NA строк последним не-NA встретившимся значением;complete.cases
— удаление пустых строкtribble
(transposed tibble) — для ручного формирования data.frame
по строчкам, а не по колонкам;stri_replace_all_fixed
— используем свойства векторизации для пакетного переименования строчек.raw <- read_excel(...)
# имеем проблему, колонки с NA вместо имени
df0 <- raw %>%
repair_names(prefix="repaired_", sep="")
# названия колонок размазаны по строкам 2-3. 2-ая -- группирующая, 3-я -- детализирующая
# Надо их слить и переименовать колонки, причем приоритет имеет строка 3, как уточняющая
#name_c2 <- tidyr::gather(df0[1, ], key = name, value = name_c2) # 1-ая колонка ушла в имена
#name_c3 <- tidyr::gather(df0[2, ], key = name, value = name_c3) # 1-ая колонка ушла в имена
# различные виды join не подойдут, поскольку мы хотим оставить все строки, вне зависимости от результата
# сливать по именам опасно, вдруг есть дубли
# names.df <- dplyr::full_join(name_c2, name_c3, by = "name")
names.df <- tibble(name_c2=tidyr::gather(df0[1, ], key=name, value=v)$v,
name_c3=tidyr::gather(df0[2, ], key=name, value=v)$v) %>%
mutate(name_c2 = na.locf(name_c2)) %>%
# если name_c3 = NA, то результат объединения строк также будет NA, нас это не очень устраивает
mutate(name.fix = ifelse(is.na(name_c3), name_c2, str_c(name_c2, name_c3, sep=": "))) %>%
mutate(name.fix = str_replace_all(name.fix, "r", " ")) %>% # перевод строки
mutate(name.fix = str_replace_all(name.fix, "n", " ")) %>% # перевод строки
mutate(name.fix = str_replace_all(name.fix, " ", " "))
df1 <- df0
repl.df <- tribble(
~pattern, ~replacement,
"Колонка 1: Параметр 2", "angle_in",
"Колонка 1: Параметр 3", "speed_diff_in",
"Колонка 5: Параметр 1: Уточнение а", "slot_in",
"Артикул", "mark_out"
)
names(df1) <- stri_replace_all_fixed(names.df$name.fix,
pattern = repl.df$pattern,
replacement = repl.df$replacement,
vectorize_all = FALSE)
# после всех манипуляций еще раз "починим" имена
df1 %<>% repair_names(prefix = "repaired_", sep = "")
# выбираем только интересующие колонки
df2 <- df1 %>% select(angle_in, speed_diff_in, slot_in, pressure_in, concentration_in,
performance_out, weight_out, mark_out) %>%
filter(row_number() > 6) %>% # удаляем весь верхний шлак
filter(complete.cases(.)) %>% # удаляем строки, содержащие пустые данные
distinct() %>% # уберем идентичные строчки
mutate_each(funs(as.numeric), -mark_out)
Концепция reactive programming
является ортогональной к классическому линейному исполнению кода и тем самым тяжела для полноценного понимания аналитиками. Половина Shiny кода является не кодом на исполнение, а декларацией реакции на что-либо. С учетом того, что Shiny очень активно развивается. крайне полезно периодически актуализировать свое понимание текущего состояния. Собственно говоря, материалы "2016 Shiny Developer Conference" [2], в частности, доклад Effective Shiny Programming by [3] после очередного просмотра дали основание для переработке кода Shiny приложений с одновременным сокращением кода ~ на 25% и повышением общей прозрачности.
Что интересного:
reactiveValues
в пользу раздельных функций reactive и observe.Из еще полезных трюков является способ частичной проблемы с utf-8 кодировкой под Windows. Функция sourse
в app.R
приводит к проблемам; букву Я принципиально нельзя исключить (комментарии можно переписать, но если она встречается на осях графиков или шапке таблиц...).
Эта задача легко решается путем следующей замены:
# source("common.R")
eval(parse("common.R", encoding="UTF-8"))
Иногда встречается ситуация, когда внутри функции, осуществляющей достаточно сложную обработку посредством функций из других пакетов, возникает сбой. Точнее, может быть exception, да так, что функция не завершает свою работу, а просто прерывается. В результате структура пакетного потока данных нарушается, после чего рушится все. Пристальное вглядывание в код ничего не дает, потому что проблема за его пределами. Обкладывать все tryCatch
, особенно на программах, исполняемых раз-другой, как правило никто не любит.
И в этом и во многих других случаях (в т.ч. при параллельных вычислениях) помогает логирование. Даже элементарный вывод о входевыходе в функцию может позволить быстро локализовать строку данных на которой происходит сбой, прогнать алгоритмы только на ней и понять, как устранить баг.
Пакет futile.logger
построен по принципу классических логгеров и не требует глубокого изучения. Однако его использование позволяет существенно повысить свою эффективность или получить свободное время.
Естественно, это далеко не все полезные трюки, но самые популярные в контексте рассмотренного класса задач.
Предыдущий пост: «До чего дошел прогRесс» [4]
Автор: i_shutov
Источник [5]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/data-mining/244148
Ссылки в тексте:
[1] tidy data: https://www.pvsm.ruftp://cran.r-project.org/pub/R/web/packages/tidyr/vignettes/tidy-data.html
[2] "2016 Shiny Developer Conference": https://www.rstudio.com/resources/webinars/shiny-developer-conference/
[3] Effective Shiny Programming by : https://cdn.rawgit.com/jcheng5/user2016-tutorial-shiny/master/slides.html
[4] «До чего дошел прогRесс»: https://habrahabr.ru/post/317130/
[5] Источник: https://habrahabr.ru/post/322066/?utm_source=habrahabr&utm_medium=rss&utm_campaign=best
Нажмите здесь для печати.