Ускорьте работу с данными с помощью R

Опубликовано: 2022-03-11

Язык R часто воспринимается как язык статистиков и специалистов по данным. Довольно давно это было в основном правдой. Однако с годами гибкость, которую R обеспечивает через пакеты, превратила R в язык более общего назначения. R был открыт в 1995 году, и с тех пор репозитории пакетов R постоянно растут. Тем не менее, по сравнению с такими языками, как Python, R в значительной степени основан на данных.

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

R можно использовать для очень эффективной обработки табличных данных.

R можно использовать для очень эффективной обработки табличных данных.
Твитнуть

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

Высокопроизводительное решение в R

Base R представила класс data.frame в 1997 году, который до этого был основан на S-PLUS. В отличие от широко используемых баз данных, которые хранят данные построчно, R data.frame хранит данные в памяти в виде структуры, ориентированной на столбцы, что делает ее более эффективной для кэширования операций со столбцами, которые распространены в аналитике. Кроме того, хотя R является функциональным языком программирования, он не навязывает это разработчику. Обе возможности хорошо реализованы в пакете data.table R, который доступен в репозитории CRAN. Он работает довольно быстро при группировании операций и особенно эффективно использует память, поскольку тщательно подходит к материализации промежуточных подмножеств данных, например к материализации только тех столбцов, которые необходимы для определенной задачи. Он также позволяет избежать ненужных копий благодаря семантике ссылок при добавлении или обновлении столбцов. Первая версия пакета была опубликована в апреле 2006 года, что на тот момент значительно улучшило производительность data.frame . Первоначальное описание пакета было таким:

Этот пакет делает очень мало. Единственная причина его существования заключается в том, что в белой книге указано, что data.frame должен иметь имена строк. Этот пакет определяет новый класс data.table, который работает так же, как data.frame, но использует до 10 раз меньше памяти и может создавать (и копировать) до 10 раз быстрее. Это также позволяет использовать subset() и with() подобные выражения внутри []. Большая часть кода скопирована из базовых функций с удаленным кодом, управляющим row.names.

С тех пор реализации как data.frame , так и data.table были улучшены, но data.table по-прежнему невероятно быстрее, чем базовый R. Фактически, data.table не просто быстрее, чем базовый R, но кажется одного из самых быстрых доступных инструментов обработки данных с открытым исходным кодом, конкурирующего с такими инструментами, как Python Pandas, базами данных с колоночным хранилищем или приложениями для работы с большими данными, такими как Spark. Его производительность в распределенной общей инфраструктуре еще не тестировалась, но способность иметь до двух миллиардов строк в одном экземпляре дает многообещающие перспективы. Выдающаяся производительность идет рука об руку с функциональностью. Кроме того, с недавними усилиями по распараллеливанию трудоемких частей для постепенного повышения производительности одно направление в сторону расширения предела производительности кажется совершенно ясным.

Примеры преобразования данных

Изучение R становится немного проще из-за того, что он работает в интерактивном режиме, поэтому мы можем шаг за шагом следовать примерам и просматривать результаты каждого шага в любое время. Прежде чем мы начнем, давайте установим пакет data.table из репозитория CRAN.

 install.packages("data.table")

Полезный совет : мы можем открыть руководство любой функции, просто набрав ее имя со знаком вопроса, например, ?install.packages .

Загрузка данных в R

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

Чтобы использовать любую функцию из пакета, нам нужно загрузить ее вызовом library .

 library(data.table) DT <- fread("flights14.csv") print(DT)
 ## year month day dep_delay arr_delay carrier origin dest air_time ## 1: 2014 1 1 14 13 AA JFK LAX 359 ## 2: 2014 1 1 -3 13 AA JFK LAX 363 ## 3: 2014 1 1 2 9 AA JFK LAX 351 ## 4: 2014 1 1 -8 -26 AA LGA PBI 157 ## 5: 2014 1 1 2 1 AA JFK LAX 350 ## --- ## 253312: 2014 10 31 1 -30 UA LGA IAH 201 ## 253313: 2014 10 31 -5 -14 UA EWR IAH 189 ## 253314: 2014 10 31 -8 16 MQ LGA RDU 83 ## 253315: 2014 10 31 -4 15 MQ LGA DTW 75 ## 253316: 2014 10 31 -5 1 MQ LGA SDF 110 ## distance hour ## 1: 2475 9 ## 2: 2475 11 ## 3: 2475 19 ## 4: 1035 7 ## 5: 2475 13 ## --- ## 253312: 1416 14 ## 253313: 1400 8 ## 253314: 431 11 ## 253315: 502 11 ## 253316: 659 8

Если наши данные недостаточно хорошо смоделированы для дальнейшей обработки, поскольку их необходимо преобразовать из длинного в широкий или из широкого в длинный формат (также известный как разворот и разворот ), мы можем посмотреть на ?dcast и ?melt , известен из пакета reshape2. Однако data.table реализует более быстрые и эффективные методы для класса data.table/data.frame.

Запрос с использованием синтаксиса data.table

Если вы знакомы с data.frame

Запрос data.table очень похож на запрос data.frame . При фильтрации по аргументу i мы можем напрямую использовать имена столбцов без необходимости доступа к ним с помощью знака $ , например df[df$col > 1, ] . При предоставлении следующего аргумента j мы предоставляем выражение для оценки в области действия нашей data.table . Чтобы передать аргумент j , не являющийся выражением, используйте with=FALSE . Третий аргумент, отсутствующий в методе data.frame , определяет группы, заставляя выражение в j вычисляться по группам.

 # data.frame DF[DF$col1 > 1L, c("col2", "col3")] # data.table DT[col1 > 1L, .(col2, col3), ...] # by group using: `by = col4`

Если вы знакомы с базами данных

Запрос data.table во многом соответствует запросам SQL, с которыми может быть знакомо больше людей. DT ниже представляет объект data.table и соответствует предложению FROM SQL.

 DT[ i = where, j = select | update, by = group by] [ having, ... ] [ order by, ... ] [ ... ] ... [ ... ] 

Распутывание табличных данных

Сортировка строк и изменение порядка столбцов

Сортировка данных является важным преобразованием для временных рядов, а также импортом для извлечения и представления данных. Сортировку можно выполнить, указав целочисленный вектор порядка строк в аргументе i , так же, как и в data.frame . Первый аргумент в order(carrier, -dep_delay) будет выбирать данные в порядке возрастания в поле carrier и в порядке убывания меры dep_delay . Второй аргумент j , как описано в предыдущем разделе, определяет возвращаемые столбцы (или выражения) и их порядок.

 ans <- DT[order(carrier, -dep_delay), .(carrier, origin, dest, dep_delay)] head(ans)
 ## carrier origin dest dep_delay ## 1: AA EWR DFW 1498 ## 2: AA JFK BOS 1241 ## 3: AA EWR DFW 1071 ## 4: AA EWR DFW 1056 ## 5: AA EWR DFW 1022 ## 6: AA EWR DFW 989

Чтобы изменить порядок данных по ссылке, вместо запроса данных в определенном порядке мы используем функции set* .

 setorder(DT, carrier, -dep_delay) leading.cols <- c("carrier","dep_delay") setcolorder(DT, c(leading.cols, setdiff(names(DT), leading.cols))) print(DT)
 ## carrier dep_delay year month day arr_delay origin dest air_time ## 1: AA 1498 2014 10 4 1494 EWR DFW 200 ## 2: AA 1241 2014 4 15 1223 JFK BOS 39 ## 3: AA 1071 2014 6 13 1064 EWR DFW 175 ## 4: AA 1056 2014 9 12 1115 EWR DFW 198 ## 5: AA 1022 2014 6 16 1073 EWR DFW 178 ## --- ## 253312: WN -12 2014 3 9 -21 LGA BNA 115 ## 253313: WN -13 2014 3 10 -18 EWR MDW 112 ## 253314: WN -13 2014 5 17 -30 LGA HOU 202 ## 253315: WN -13 2014 6 15 10 LGA MKE 101 ## 253316: WN -13 2014 8 19 -30 LGA CAK 63 ## distance hour ## 1: 1372 7 ## 2: 187 13 ## 3: 1372 10 ## 4: 1372 6 ## 5: 1372 7 ## --- ## 253312: 764 16 ## 253313: 711 20 ## 253314: 1428 17 ## 253315: 738 20 ## 253316: 397 16

Чаще всего нам не нужен ни исходный набор данных, ни упорядоченный/отсортированный набор данных. По умолчанию язык R, как и другие языки функционального программирования, будет возвращать отсортированные данные в виде нового объекта и, таким образом, потребует в два раза больше памяти, чем сортировка по ссылке.

Подмножество запросов

Давайте создадим подмножество набора данных для пункта отправления рейса «JFK» и месяца с 6 по 9. Во втором аргументе мы подмножим результаты в перечисленные столбцы, добавив одну вычисляемую переменную sum_delay .

 ans <- DT[origin == "JFK" & month %in% 6:9, .(origin, month, arr_delay, dep_delay, sum_delay = arr_delay + dep_delay)] head(ans)
 ## origin month arr_delay dep_delay sum_delay ## 1: JFK 7 925 926 1851 ## 2: JFK 8 727 772 1499 ## 3: JFK 6 466 451 917 ## 4: JFK 7 414 450 864 ## 5: JFK 6 411 442 853 ## 6: JFK 6 333 343 676

По умолчанию при подгруппе набора данных в одном столбце data.table автоматически создает индекс для этого столбца. Это приводит к ответам в режиме реального времени на любые дальнейшие вызовы фильтрации в этом столбце.

Обновить набор данных

Добавление нового столбца по ссылке выполняется с помощью оператора := , он присваивает переменную в набор данных на месте. Это позволяет избежать копирования набора данных в памяти, поэтому нам не нужно назначать результаты каждой новой переменной.

 DT[, sum_delay := arr_delay + dep_delay] head(DT)
 ## carrier dep_delay year month day arr_delay origin dest air_time ## 1: AA 1498 2014 10 4 1494 EWR DFW 200 ## 2: AA 1241 2014 4 15 1223 JFK BOS 39 ## 3: AA 1071 2014 6 13 1064 EWR DFW 175 ## 4: AA 1056 2014 9 12 1115 EWR DFW 198 ## 5: AA 1022 2014 6 16 1073 EWR DFW 178 ## 6: AA 989 2014 6 11 991 EWR DFW 194 ## distance hour sum_delay ## 1: 1372 7 2992 ## 2: 187 13 2464 ## 3: 1372 10 2135 ## 4: 1372 6 2171 ## 5: 1372 7 2095 ## 6: 1372 11 1980

Чтобы добавить больше переменных одновременно, мы можем использовать синтаксис DT[, := (sum_delay = arr_delay + dep_delay)] , аналогичный .(sum_delay = arr_delay + dep_delay) при запросе из набора данных.

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

 DT[origin=="JFK", distance := NA] head(DT)
 ## carrier dep_delay year month day arr_delay origin dest air_time ## 1: AA 1498 2014 10 4 1494 EWR DFW 200 ## 2: AA 1241 2014 4 15 1223 JFK BOS 39 ## 3: AA 1071 2014 6 13 1064 EWR DFW 175 ## 4: AA 1056 2014 9 12 1115 EWR DFW 198 ## 5: AA 1022 2014 6 16 1073 EWR DFW 178 ## 6: AA 989 2014 6 11 991 EWR DFW 194 ## distance hour sum_delay ## 1: 1372 7 2992 ## 2: NA 13 2464 ## 3: 1372 10 2135 ## 4: 1372 6 2171 ## 5: 1372 7 2095 ## 6: 1372 11 1980

Совокупные данные

Чтобы агрегировать данные, мы предоставляем третий by квадратной скобкой. Затем в j нам нужно обеспечить вызовы агрегатных функций, чтобы данные можно было фактически агрегировать. Символ .N , используемый в аргументе j , соответствует количеству всех наблюдений в каждой группе. Как упоминалось ранее, агрегаты можно комбинировать с подмножествами строк и столбцов выбора.

 ans <- DT[, .(m_arr_delay = mean(arr_delay), m_dep_delay = mean(dep_delay), count = .N), .(carrier, month)] head(ans)
 ## carrier month m_arr_delay m_dep_delay count ## 1: AA 10 5.541959 7.591497 2705 ## 2: AA 4 1.903324 3.987008 2617 ## 3: AA 6 8.690067 11.476475 2678 ## 4: AA 9 -1.235160 3.307078 2628 ## 5: AA 8 4.027474 8.914054 2839 ## 6: AA 7 9.159886 11.665953 2802

Часто нам может понадобиться сравнить значение строки с ее агрегатом по группе. В SQL мы применяем агрегаты по разбиению на : AVG(arr_delay) OVER (PARTITION BY carrier, month) .

 ans <- DT[, .(arr_delay, carrierm_mean_arr = mean(arr_delay), dep_delay, carrierm_mean_dep = mean(dep_delay)), .(carrier, month)] head(ans)
 ## carrier month arr_delay carrierm_mean_arr dep_delay carrierm_mean_dep ## 1: AA 10 1494 5.541959 1498 7.591497 ## 2: AA 10 840 5.541959 848 7.591497 ## 3: AA 10 317 5.541959 338 7.591497 ## 4: AA 10 292 5.541959 331 7.591497 ## 5: AA 10 322 5.541959 304 7.591497 ## 6: AA 10 306 5.541959 299 7.591497

Если мы не хотим запрашивать данные с помощью этих агрегатов, а вместо этого просто помещаем их в фактическое обновление таблицы по ссылке, мы можем сделать это с помощью оператора := . Это позволяет избежать копии набора данных в памяти, поэтому нам не нужно назначать результаты новой переменной.

 DT[, `:=`(carrierm_mean_arr = mean(arr_delay), carrierm_mean_dep = mean(dep_delay)), .(carrier, month)] head(DT)
 ## carrier dep_delay year month day arr_delay origin dest air_time ## 1: AA 1498 2014 10 4 1494 EWR DFW 200 ## 2: AA 1241 2014 4 15 1223 JFK BOS 39 ## 3: AA 1071 2014 6 13 1064 EWR DFW 175 ## 4: AA 1056 2014 9 12 1115 EWR DFW 198 ## 5: AA 1022 2014 6 16 1073 EWR DFW 178 ## 6: AA 989 2014 6 11 991 EWR DFW 194 ## distance hour sum_delay carrierm_mean_arr carrierm_mean_dep ## 1: 1372 7 2992 5.541959 7.591497 ## 2: NA 13 2464 1.903324 3.987008 ## 3: 1372 10 2135 8.690067 11.476475 ## 4: 1372 6 2171 -1.235160 3.307078 ## 5: 1372 7 2095 8.690067 11.476475 ## 6: 1372 11 1980 8.690067 11.476475

Присоединиться к наборам данных

Соединение и слияние наборов данных Base R считается особым типом операции с подмножеством . Мы предоставляем набор данных, к которому мы хотим присоединиться, в первом аргументе квадратной скобки i . Для каждой строки в наборе данных, предоставленном i , мы сопоставляем строки из набора данных, в котором мы используем [ . Если мы хотим сохранить только совпадающие строки ( внутреннее соединение ), то мы передаем дополнительный аргумент nomatch = 0L . Мы используем аргумент on , чтобы указать столбцы, в которых мы хотим объединить оба набора данных.

 # create reference subset carrierdest <- DT[, .(count=.N), .(carrier, dest) # count by carrier and dest ][1:10 # just 10 first groups ] # chaining `[...][...]` as subqueries print(carrierdest)
 ## carrier dest count ## 1: AA DFW 5877 ## 2: AA BOS 1173 ## 3: AA ORD 4798 ## 4: AA SEA 298 ## 5: AA EGE 85 ## 6: AA LAX 3449 ## 7: AA MIA 6058 ## 8: AA SFO 1312 ## 9: AA AUS 297 ## 10: AA DCA 172
 # outer join ans <- carrierdest[DT, on = c("carrier","dest")] print(ans)
 ## carrier dest count dep_delay year month day arr_delay origin ## 1: AA DFW 5877 1498 2014 10 4 1494 EWR ## 2: AA BOS 1173 1241 2014 4 15 1223 JFK ## 3: AA DFW 5877 1071 2014 6 13 1064 EWR ## 4: AA DFW 5877 1056 2014 9 12 1115 EWR ## 5: AA DFW 5877 1022 2014 6 16 1073 EWR ## --- ## 253312: WN BNA NA -12 2014 3 9 -21 LGA ## 253313: WN MDW NA -13 2014 3 10 -18 EWR ## 253314: WN HOU NA -13 2014 5 17 -30 LGA ## 253315: WN MKE NA -13 2014 6 15 10 LGA ## 253316: WN CAK NA -13 2014 8 19 -30 LGA ## air_time distance hour sum_delay carrierm_mean_arr ## 1: 200 1372 7 2992 5.541959 ## 2: 39 NA 13 2464 1.903324 ## 3: 175 1372 10 2135 8.690067 ## 4: 198 1372 6 2171 -1.235160 ## 5: 178 1372 7 2095 8.690067 ## --- ## 253312: 115 764 16 -33 6.921642 ## 253313: 112 711 20 -31 6.921642 ## 253314: 202 1428 17 -43 22.875845 ## 253315: 101 738 20 -3 14.888889 ## 253316: 63 397 16 -43 7.219670 ## carrierm_mean_dep ## 1: 7.591497 ## 2: 3.987008 ## 3: 11.476475 ## 4: 3.307078 ## 5: 11.476475 ## --- ## 253312: 11.295709 ## 253313: 11.295709 ## 253314: 30.546453 ## 253315: 24.217560 ## 253316: 17.038047
 # inner join ans <- DT[carrierdest, # for each row in carrierdest nomatch = 0L, # return only matching rows from both tables on = c("carrier","dest")] # joining on columns carrier and dest print(ans)
 ## carrier dep_delay year month day arr_delay origin dest air_time ## 1: AA 1498 2014 10 4 1494 EWR DFW 200 ## 2: AA 1071 2014 6 13 1064 EWR DFW 175 ## 3: AA 1056 2014 9 12 1115 EWR DFW 198 ## 4: AA 1022 2014 6 16 1073 EWR DFW 178 ## 5: AA 989 2014 6 11 991 EWR DFW 194 ## --- ## 23515: AA -8 2014 10 11 -13 JFK DCA 53 ## 23516: AA -9 2014 5 21 -12 JFK DCA 52 ## 23517: AA -9 2014 6 5 -6 JFK DCA 53 ## 23518: AA -9 2014 10 2 -21 JFK DCA 51 ## 23519: AA -11 2014 5 27 10 JFK DCA 55 ## distance hour sum_delay carrierm_mean_arr carrierm_mean_dep count ## 1: 1372 7 2992 5.541959 7.591497 5877 ## 2: 1372 10 2135 8.690067 11.476475 5877 ## 3: 1372 6 2171 -1.235160 3.307078 5877 ## 4: 1372 7 2095 8.690067 11.476475 5877 ## 5: 1372 11 1980 8.690067 11.476475 5877 ## --- ## 23515: NA 15 -21 5.541959 7.591497 172 ## 23516: NA 15 -21 4.150172 8.733665 172 ## 23517: NA 15 -15 8.690067 11.476475 172 ## 23518: NA 15 -30 5.541959 7.591497 172 ## 23519: NA 15 -1 4.150172 8.733665 172

Имейте в виду, что из-за согласованности с базовым подмножеством R внешнее соединение по умолчанию RIGHT OUTER . Если мы ищем LEFT OUTER , нам нужно поменять местами таблицы, как в примере выше. Точное поведение также можно легко контролировать в методе merge data.table , используя тот же API, что и базовый R merge data.frame .

Если мы хотим просто найти столбец (столбцы) в нашем наборе данных, мы можем эффективно сделать это с помощью оператора := в аргументе j при присоединении. Точно так же, как мы назначаем по ссылке, как описано в разделе « Обновление набора данных », мы просто добавляем столбец по ссылке из набора данных, к которому мы присоединяемся. Это позволяет избежать копирования данных в памяти, поэтому нам не нужно присваивать результаты новым переменным.

 DT[carrierdest, # data.table to join with lkp.count := count, # lookup `count` column from `carrierdest` on = c("carrier","dest")] # join by columns head(DT)
 ## carrier dep_delay year month day arr_delay origin dest air_time ## 1: AA 1498 2014 10 4 1494 EWR DFW 200 ## 2: AA 1241 2014 4 15 1223 JFK BOS 39 ## 3: AA 1071 2014 6 13 1064 EWR DFW 175 ## 4: AA 1056 2014 9 12 1115 EWR DFW 198 ## 5: AA 1022 2014 6 16 1073 EWR DFW 178 ## 6: AA 989 2014 6 11 991 EWR DFW 194 ## distance hour sum_delay carrierm_mean_arr carrierm_mean_dep lkp.count ## 1: 1372 7 2992 5.541959 7.591497 5877 ## 2: NA 13 2464 1.903324 3.987008 1173 ## 3: 1372 10 2135 8.690067 11.476475 5877 ## 4: 1372 6 2171 -1.235160 3.307078 5877 ## 5: 1372 7 2095 8.690067 11.476475 5877 ## 6: 1372 11 1980 8.690067 11.476475 5877

Для объединения во время соединения используйте by = .EACHI . Он выполняет соединение, которое не материализует промежуточные результаты соединения и применяет агрегаты на лету, что делает его эффективным с точки зрения использования памяти.

Прокручивающееся соединение — это необычная функция, предназначенная для работы с упорядоченными данными. Он идеально подходит для обработки временных данных и временных рядов в целом. В основном он переворачивает совпадения в условии соединения со следующим совпадающим значением. Используйте его, указав аргумент roll при присоединении.

Быстрое соединение с перекрытием объединяет наборы данных на основе периодов и их обработки с перекрытием с использованием различных операторов перекрытия: any , within , start , end .

В настоящее время разрабатывается функция неэквивалентного соединения для объединения наборов данных с использованием неравного условия.

Профилирование данных

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

Описательная статистика

 summary(DT)
 ## carrier dep_delay year month ## Length:253316 Min. :-112.00 Min. :2014 Min. : 1.000 ## Class :character 1st Qu.: -5.00 1st Qu.:2014 1st Qu.: 3.000 ## Mode :character Median : -1.00 Median :2014 Median : 6.000 ## Mean : 12.47 Mean :2014 Mean : 5.639 ## 3rd Qu.: 11.00 3rd Qu.:2014 3rd Qu.: 8.000 ## Max. :1498.00 Max. :2014 Max. :10.000 ## ## day arr_delay origin dest ## Min. : 1.00 Min. :-112.000 Length:253316 Length:253316 ## 1st Qu.: 8.00 1st Qu.: -15.000 Class :character Class :character ## Median :16.00 Median : -4.000 Mode :character Mode :character ## Mean :15.89 Mean : 8.147 ## 3rd Qu.:23.00 3rd Qu.: 15.000 ## Max. :31.00 Max. :1494.000 ## ## air_time distance hour sum_delay ## Min. : 20.0 Min. : 80.0 Min. : 0.00 Min. :-224.00 ## 1st Qu.: 86.0 1st Qu.: 529.0 1st Qu.: 9.00 1st Qu.: -19.00 ## Median :134.0 Median : 762.0 Median :13.00 Median : -5.00 ## Mean :156.7 Mean : 950.4 Mean :13.06 Mean : 20.61 ## 3rd Qu.:199.0 3rd Qu.:1096.0 3rd Qu.:17.00 3rd Qu.: 23.00 ## Max. :706.0 Max. :4963.0 Max. :24.00 Max. :2992.00 ## NA's :81483 ## carrierm_mean_arr carrierm_mean_dep lkp.count ## Min. :-22.403 Min. :-4.500 Min. : 85 ## 1st Qu.: 2.676 1st Qu.: 7.815 1st Qu.:3449 ## Median : 6.404 Median :11.354 Median :5877 ## Mean : 8.147 Mean :12.465 Mean :4654 ## 3rd Qu.: 11.554 3rd Qu.:17.564 3rd Qu.:6058 ## Max. : 86.182 Max. :52.864 Max. :6058 ## NA's :229797

мощность

Мы можем проверить уникальность данных с помощью функции uniqueN и применить ее к каждому столбцу. Объект .SD в приведенном ниже запросе соответствует подмножеству таблицы Data.table :

 DT[, lapply(.SD, uniqueN)]
 ## carrier dep_delay year month day arr_delay origin dest air_time ## 1: 14 570 1 10 31 616 3 109 509 ## distance hour sum_delay carrierm_mean_arr carrierm_mean_dep lkp.count ## 1: 152 25 1021 134 134 11

Отношение числовой апертуры

Чтобы вычислить соотношение неизвестных значений ( NA в R и NULL в SQL) для каждого столбца, мы предоставляем желаемую функцию для применения к каждому столбцу.

 DT[, lapply(.SD, function(x) sum(is.na(x))/.N)]
 ## carrier dep_delay year month day arr_delay origin dest air_time ## 1: 0 0 0 0 0 0 0 0 0 ## distance hour sum_delay carrierm_mean_arr carrierm_mean_dep lkp.count ## 1: 0.3216654 0 0 0 0 0.9071555

Экспорт данных

Быстрый экспорт табличных данных в формат CSV также обеспечивается пакетом data.table .

 tmp.csv <- tempfile(fileext=".csv") fwrite(DT, tmp.csv) # preview exported data cat(system(paste("head -3",tmp.csv), intern=TRUE), sep="\n")
 ## carrier,dep_delay,year,month,day,arr_delay,origin,dest,air_time,distance,hour,sum_delay,carrierm_mean_arr,carrierm_mean_dep,lkp.count ## AA,1498,2014,10,4,1494,EWR,DFW,200,1372,7,2992,5.54195933456561,7.59149722735674,5877 ## AA,1241,2014,4,15,1223,JFK,BOS,39,,13,2464,1.90332441727168,3.98700802445548,1173

На момент написания этой статьи функция fwrite еще не была опубликована в репозитории CRAN. Чтобы использовать его, нам нужно установить версию для разработки data.table , в противном случае мы можем использовать базовую функцию R write.csv , но не ожидайте, что она будет быстрой.

Ресурсы

Есть много доступных ресурсов. Помимо руководств, доступных для каждой функции, есть также пакетные виньетки, которые представляют собой учебные пособия, ориентированные на конкретную тему. Их можно найти на странице «Начало работы». Кроме того, на странице «Презентации» представлено более 30 материалов (слайды, видео и т. д.) из презентаций data.table со всего мира. Кроме того, поддержка сообщества с годами выросла, недавно достигнув 4000-го вопроса в теге data.table Stack Overflow, по-прежнему имея высокий коэффициент (91,9%) отвеченных вопросов. На приведенном ниже графике показано количество вопросов с тегами data.table в Stack Overflow с течением времени.

SO вопросы ежемесячно для data.table - только вопросы с тегами data.table, а не вопросы с ответами data.table (принятые)

SO вопросы ежемесячно для data.table - только вопросы с тегами data.table, а не вопросы с ответами data.table (принятые)

Резюме

В этой статье представлены избранные примеры эффективного преобразования табличных данных в R с использованием пакета data.table . Фактические показатели производительности можно проверить, выполнив воспроизводимые тесты. Я опубликовал сводную запись в блоге о решениях data.table для 50 самых популярных вопросов StackOverflow для языка R под названием Эффективное решение общих проблем R с помощью data.table, где вы можете найти множество рисунков и воспроизводимый код. Пакет data.table использует встроенную реализацию быстрого упорядочения по основанию для операций группировки и двоичного поиска для быстрых подмножеств/соединений. Этот порядок счисления был включен в базу R начиная с версии 3.3.0. Кроме того, алгоритм был недавно реализован в платформе машинного обучения H2O и распараллелен в кластере H2O, что позволяет эффективно выполнять большие соединения в строках размером 10 x 10 B.

Связанный: Максимальное управление сбором данных в памяти с помощью Supergroup.js