本文概述
- R中的高性能解决方案
- 数据转换示例
- 资源资源
- 总结
谈到数据, 表格数据是最常用的数据类型之一, 因此值得特别注意。它是一种数据类型, 对应于数据库中已知的表结构, 其中每一列可以具有不同的类型, 并且该特定数据类型的处理性能是许多应用程序的关键因素。
文章图片
【使用R提升数据处理能力】R可用于表格数据的非常有效的数据处理
鸣叫
在本文中, 我们将介绍如何以有效的方式实现表格数据转换。许多已经使用R进行机器学习的人并不知道在R中可以更快地进行数据处理, 因此他们不需要使用其他工具。
R中的高性能解决方案 Base R在1997年引入了data.frame类, 该类基于之前的S-PLUS。与常用的按行存储数据的数据库不同, R data.frame将数据作为面向列的结构存储在内存中, 因此对于分析中常见的列操作, 它的缓存效率更高。另外, 即使R是一种功能编程语言, 它也不会在开发人员上强制执行。这两个机会已经通过data.table R包得到了很好的解决, 该包可在CRAN存储库中找到。在分组操作时, 它执行得非常快, 并且通过小心实现中间数据子集(例如仅实现特定任务所需的那些列)而特别有效地提高了内存效率。它还在添加或更新列时通过其参考语义避免了不必要的复制。该软件包的第一个版本已于2006年4月发布, 当时显着提高了data.frame的性能。最初的软件包描述是:
该软件包几乎没有。其存在的唯一原因是白皮书指定data.frame必须具有行名。这个包定义了一个新的类data.table, 它的工作方式与data.frame一样, 但是使用的内存减少了10倍, 并且创建(和复制)的速度提高了10倍。它还借此机会允许[]中的subset()和with()之类的表达式。大多数代码是从基本函数复制而来的, 其中删除了操作row.names的代码。从那时起, data.frame和data.table的实现都得到了改进, 但是data.table仍然比base R快得令人难以置信。事实上, data.table不仅比base R快, 而且似乎比base R快。是目前最快的开源数据整理工具, 可与Python Pandas等工具, 列存储数据库或Spark等大数据应用程序竞争。其在分布式共享基础架构上的性能尚未经过基准测试, 但一次最多可以拥有20亿行的行列, 前景可观。出色的性能与功能齐头并进。另外, 随着最近努力使耗时??的零件并行化以提高性能, 似乎很明确地朝着提高性能极限的方向发展。
数据转换示例 由于R可以交互工作, 因此学习R变得容易一些, 因此我们可以逐步遵循示例, 并随时查看每个步骤的结果。在开始之前, 让我们从CRAN信息库安装data.table软件包。
install.packages("data.table")
有用的提示:我们只要打开带有任何问号的名称, 即?install.packages, 就可以打开任何功能的手册。
将数据加载到R中
有很多软件包可以从各种格式和数据库中提取数据, 其中通常包括本机驱动程序。我们将从CSV文件加载数据, 这是原始表格数据的最常见格式。在以下示例中使用的文件可以在这里找到。我们无需担心CSV的读取性能, 因为fread功能在此基础上进行了高度优化。
为了使用包中的任何函数, 我们需要通过库调用来加载它。
library(data.table)
DT <
- fread("flights14.csv")
print(DT)
##year month day dep_delay arr_delay carrier origin dest air_time
##1: 2014111413AAJFKLAX359
##2: 201411-313AAJFKLAX363
##3: 20141129AAJFKLAX351
##4: 201411-8-26AALGAPBI157
##5: 20141121AAJFKLAX350
##---
## 253312: 201410311-30UALGAIAH201
## 253313: 20141031-5-14UAEWRIAH189
## 253314: 20141031-816MQLGARDU83
## 253315: 20141031-415MQLGADTW75
## 253316: 20141031-51MQLGASDF110
##distance hour
##1:24759
##2:247511
##3:247519
##4:10357
##5:247513
##---
## 253312:141614
## 253313:14008
## 253314:43111
## 253315:50211
## 253316:6598
如果我们的数据没有很好地建模以进行进一步处理, 因为它们需要从长到宽或从宽到长(也称为透视和非透视)格式进行重塑, 我们可以看一下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参数, 请使用= 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对象, 并且对应于SQLs FROM子句。
DT[ i = where, j = select | update, by = group by]
[ having, ... ]
[ order by, ... ]
[ ... ] ... [ ... ]
文章图片
排序行和重新排序列
数据排序是时间序列的重要转换, 也是导入数据提取和表示的基础。可以通过向i参数提供行顺序的整数矢量来实现排序, 方法与data.frame相同。查询顺序中的第一个参数(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:AAEWRDFW1498
## 2:AAJFKBOS1241
## 3:AAEWRDFW1071
## 4:AAEWRDFW1056
## 5:AAEWRDFW1022
## 6:AAEWRDFW989
为了通过引用对数据重新排序, 而不是按特定的顺序查询数据, 我们使用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:AA1498 20141041494EWRDFW200
##2:AA1241 20144151223JFKBOS39
##3:AA1071 20146131064EWRDFW175
##4:AA1056 20149121115EWRDFW198
##5:AA1022 20146161073EWRDFW178
##---
## 253312:WN-12 201439-21LGABNA115
## 253313:WN-13 2014310-18EWRMDW112
## 253314:WN-13 2014517-30LGAHOU202
## 253315:WN-13 201461510LGAMKE101
## 253316:WN-13 2014819-30LGACAK63
##distance hour
##1:13727
##2:18713
##3:137210
##4:13726
##5:13727
##---
## 253312:76416
## 253313:71120
## 253314:142817
## 253315:73820
## 253316:39716
通常, 我们既不需要原始数据集, 也不需要排序/排序的数据集。默认情况下, 与其他函数式编程语言类似, 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:JFK79259261851
## 2:JFK87277721499
## 3:JFK6466451917
## 4:JFK7414450864
## 5:JFK6411442853
## 6:JFK6333343676
默认情况下, 在单列data.table上设置数据集时, 将自动为该列创建索引。这将对该列上的所有进一步过滤调用产生实时答案。
更新数据集
使用:=运算符通过引用添加新列, 它会将变量分配到适当的数据集中。这样可以避免在内存中复制数据集, 因此我们无需将结果分配给每个新变量。
DT[, sum_delay := arr_delay + dep_delay]
head(DT)
##carrier dep_delay year month day arr_delay origin dest air_time
## 1:AA1498 20141041494EWRDFW200
## 2:AA1241 20144151223JFKBOS39
## 3:AA1071 20146131064EWRDFW175
## 4:AA1056 20149121115EWRDFW198
## 5:AA1022 20146161073EWRDFW178
## 6:AA989 2014611991EWRDFW194
##distance hour sum_delay
## 1:137272992
## 2:187132464
## 3:1372102135
## 4:137262171
## 5:137272095
## 6:1372111980
要一次添加更多变量, 可以从数据集中查询时使用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:AA1498 20141041494EWRDFW200
## 2:AA1241 20144151223JFKBOS39
## 3:AA1071 20146131064EWRDFW175
## 4:AA1056 20149121115EWRDFW198
## 5:AA1022 20146161073EWRDFW178
## 6:AA989 2014611991EWRDFW194
##distance hour sum_delay
## 1:137272992
## 2:NA132464
## 3:1372102135
## 4:137262171
## 5:137272095
## 6:1372111980
汇总数据
为了汇总数据, 我们在方括号中提供第三个参数。然后, 在j中, 我们需要提供聚合函数调用, 以便可以实际聚合数据。 j参数中使用的.N符号对应于每个组中所有观测值的数量。如前所述, 聚合可以与行和选择列上的子集结合。
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:AA105.5419597.5914972705
## 2:AA41.9033243.9870082617
## 3:AA68.69006711.4764752678
## 4:AA9-1.2351603.3070782628
## 5:AA84.0274748.9140542839
## 6:AA79.15988611.6659532802
通常, 我们可能需要将行的值与其在组中的汇总进行比较。在SQL中, 我们通过以下方法将聚合应用于分区:AVG(arr_delay)OVER(按载体分类, 月份)。
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:AA1014945.54195914987.591497
## 2:AA108405.5419598487.591497
## 3:AA103175.5419593387.591497
## 4:AA102925.5419593317.591497
## 5:AA103225.5419593047.591497
## 6:AA103065.5419592997.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:AA1498 20141041494EWRDFW200
## 2:AA1241 20144151223JFKBOS39
## 3:AA1071 20146131064EWRDFW175
## 4:AA1056 20149121115EWRDFW198
## 5:AA1022 20146161073EWRDFW178
## 6:AA989 2014611991EWRDFW194
##distance hour sum_delay carrierm_mean_arr carrierm_mean_dep
## 1:1372729925.5419597.591497
## 2:NA1324641.9033243.987008
## 3:13721021358.69006711.476475
## 4:137262171-1.2351603.307078
## 5:1372720958.69006711.476475
## 6:13721119808.69006711.476475
联接数据集
数据集的基本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:AADFW5877
##2:AABOS1173
##3:AAORD4798
##4:AASEA298
##5:AAEGE85
##6:AALAX3449
##7:AAMIA6058
##8:AASFO1312
##9:AAAUS297
## 10:AADCA172
# outer join
ans <
- carrierdest[DT, on = c("carrier", "dest")]
print(ans)
##carrier dest count dep_delay year month day arr_delay origin
##1:AADFW58771498 20141041494EWR
##2:AABOS11731241 20144151223JFK
##3:AADFW58771071 20146131064EWR
##4:AADFW58771056 20149121115EWR
##5:AADFW58771022 20146161073EWR
##---
## 253312:WNBNANA-12 201439-21LGA
## 253313:WNMDWNA-13 2014310-18EWR
## 253314:WNHOUNA-13 2014517-30LGA
## 253315:WNMKENA-13 201461510LGA
## 253316:WNCAKNA-13 2014819-30LGA
##air_time distance hour sum_delay carrierm_mean_arr
##1:2001372729925.541959
##2:39NA1324641.903324
##3:17513721021358.690067
##4:198137262171-1.235160
##5:1781372720958.690067
##---
## 253312:11576416-336.921642
## 253313:11271120-316.921642
## 253314:202142817-4322.875845
## 253315:10173820-314.888889
## 253316:6339716-437.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:AA1498 20141041494EWRDFW200
##2:AA1071 20146131064EWRDFW175
##3:AA1056 20149121115EWRDFW198
##4:AA1022 20146161073EWRDFW178
##5:AA989 2014611991EWRDFW194
##---
## 23515:AA-8 20141011-13JFKDCA53
## 23516:AA-9 2014521-12JFKDCA52
## 23517:AA-9 201465-6JFKDCA53
## 23518:AA-9 2014102-21JFKDCA51
## 23519:AA-11 201452710JFKDCA55
##distance hour sum_delay carrierm_mean_arr carrierm_mean_dep count
##1:1372729925.5419597.5914975877
##2:13721021358.69006711.4764755877
##3:137262171-1.2351603.3070785877
##4:1372720958.69006711.4764755877
##5:13721119808.69006711.4764755877
##---
## 23515:NA15-215.5419597.591497172
## 23516:NA15-214.1501728.733665172
## 23517:NA15-158.69006711.476475172
## 23518:NA15-305.5419597.591497172
## 23519:NA15-14.1501728.733665172
请注意, 由于与基本R子集的一致性, 默认情况下, 外部联接为RIGHT OUTER。如果要寻找LEFT OUTER, 则需要交换表, 如上例所示。确切的行为也可以在merge data.table方法中轻松控制, 使用与R基合并data.frame相同的API。
如果我们只想在数据集中查找列, 则可以在加入时使用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:AA1498 20141041494EWRDFW200
## 2:AA1241 20144151223JFKBOS39
## 3:AA1071 20146131064EWRDFW175
## 4:AA1056 20149121115EWRDFW198
## 5:AA1022 20146161073EWRDFW178
## 6:AA989 2014611991EWRDFW194
##distance hour sum_delay carrierm_mean_arr carrierm_mean_dep lkp.count
## 1:1372729925.5419597.5914975877
## 2:NA1324641.9033243.9870081173
## 3:13721021358.69006711.4764755877
## 4:137262171-1.2351603.3070785877
## 5:1372720958.69006711.4764755877
## 6:13721119808.69006711.4764755877
对于加入时的汇总, 请使用= .EACHI。它执行的联接不会实现中间联接结果, 并且会动态应用聚合, 从而提高存储效率。
滚动连接是一项罕见功能, 旨在处理有序数据。它非常适合处理时间数据以及一般的时间序列。它基本上将匹配条件下的匹配滚动到下一个匹配值。加入时通过提供roll参数来使用它。
快速重叠联接通过使用各种重叠运算符(包括在, 在, 开始, 结束中的任何一个), 根据周期及其重叠处理来连接数据集。
当前正在开发使用非等式条件连接数据集的非等式连接功能。
分析数据
在浏览数据集时, 有时我们可能希望收集有关该主题的技术信息, 以更好地理解数据的质量。
描述性统计
summary(DT)
##carrierdep_delayyearmonth
##Length:253316Min.:-112.00Min.:2014Min.: 1.000
##Class :character1st Qu.:-5.001st Qu.:20141st Qu.: 3.000
##Mode:characterMedian :-1.00Median :2014Median : 6.000
##Mean:12.47Mean:2014Mean: 5.639
##3rd Qu.:11.003rd Qu.:20143rd Qu.: 8.000
##Max.:1498.00Max.:2014Max.:10.000
##
##dayarr_delayorigindest
##Min.: 1.00Min.:-112.000Length:253316Length:253316
##1st Qu.: 8.001st Qu.: -15.000Class :characterClass :character
##Median :16.00Median :-4.000Mode:characterMode:character
##Mean:15.89Mean:8.147
##3rd Qu.:23.003rd Qu.:15.000
##Max.:31.00Max.:1494.000
##
##air_timedistancehoursum_delay
##Min.: 20.0Min.:80.0Min.: 0.00Min.:-224.00
##1st Qu.: 86.01st Qu.: 529.01st Qu.: 9.001st Qu.: -19.00
##Median :134.0Median : 762.0Median :13.00Median :-5.00
##Mean:156.7Mean: 950.4Mean:13.06Mean:20.61
##3rd Qu.:199.03rd Qu.:1096.03rd Qu.:17.003rd Qu.:23.00
##Max.:706.0Max.:4963.0Max.:24.00Max.:2992.00
##NA's:81483
##carrierm_mean_arr carrierm_mean_deplkp.count
##Min.:-22.403Min.:-4.500Min.:85
##1st Qu.:2.6761st Qu.: 7.8151st Qu.:3449
##Median :6.404Median :11.354Median :5877
##Mean:8.147Mean:12.465Mean:4654
##3rd Qu.: 11.5543rd Qu.:17.5643rd Qu.:6058
##Max.: 86.182Max.:52.864Max.: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:14570110316163109509
##distance hour sum_delay carrierm_mean_arr carrierm_mean_dep lkp.count
## 1:15225102113413411
NA比率 为了计算每一列的未知值(R中为NA, SQL中为NULL)的比率, 我们提供了所需的函数以应用于每一列。
DT[, lapply(.SD, function(x) sum(is.na(x))/.N)]
##carrier dep_delay year month day arr_delay origin dest air_time
## 1:000000000
##distance hour sum_delay carrierm_mean_arr carrierm_mean_dep lkp.count
## 1: 0.32166540000 0.9071555
汇出资料
data.table包还提供了将表格数据快速导出为CSV格式的功能。
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开发版本, 否则我们可以使用base R write.csv函数, 但不要指望它会很快。
资源资源 有大量可用资源。除了每个功能可用的手册之外, 还有软件包小插图, 它们是针对特定主题的教程。这些可以在” 入门” 页面上找到。此外, “ 演示文稿” 页面列出了来自全球data.table演示文稿的30多种材料(幻灯片, 视频等)。同样, 社区支持也在不断增长, 最近在Stack Overflow data.table标签上达到了第4000个问题, 仍然有很高的回答率(91.9%)。下图显示了随时间推移堆栈溢出的data.table标签问题的数量。
文章图片
SO每月针对data.table提问-仅标记data.table的问题, 不回答带有data.table的问题(已接受)
总结 本文提供了一些示例, 可以使用data.table包在R中进行有效的表格数据转换。可以通过寻找可重复的基准来检查性能的实际数字。我发表了一篇有关data.table解决方案的总结博客文章, 涉及关于R语言的前50个最受好评的StackOverflow问题, 称为” 使用data.table有效解决常见R问题” , 你可以在其中找到大量图形和可复制的代码。包data.table使用本机实现的快速基数排序进行分组操作, 并使用二进制搜索快速子集/联接。此基数排序已从3.3.0版合并到base R中。此外, 该算法最近已在H2O机器学习平台中实现, 并在H2O集群上并行化, 从而可以在10B x 10B行上进行有效的大联接。
相关:使用Supergroup.js进行最终的内存中数据收集操作
推荐阅读
- 聚类算法(从开始到最新)
- 使用SciPy Stack全面介绍你的基因组
- JSON中的双向关系支持
- 如何修复Chrome和Edge上的RESULT_CODE_HUNG错误(解决办法)
- 如何修复DX11 Feature Level 10.0需要运行引擎错误(解决办法)
- 如何修复Firefox右键单击不起作用(解决办法教程)
- 如何修复Windows 10亮度不工作(解决办法分步教程)
- 如何修复Windows 10 0xC00D36D5未连接摄像头(解决办法)
- 如何修复Windows 10 Firefox没有声音的问题(解决办法)