R - 用于 data.table[lapply(.SD,Mode),by=.()] 的快速模式函数
R - Fast Mode Function for use in data.table[,lapply(.SD,Mode),by=.()]
我在 data.table 分组依据中汇总数据,我需要在组中获取变量的单个值。我希望这个值成为组的模式。我认为它需要模式,因为通常一个组是 8 行,它会有 2 行一个值,其他 6 行左右将是另一个值。
这是一个简化的例子,来自:
key1 2
key1 2
key1 2
key1 8
key1 2
key1 2
key1 2
key1 8
我想要这个:
key1 2
我在使用base R提供的标准模式功能时遇到了问题,所以我在这里使用了这个解决方案:
Mode <- function(x) {
ux <- unique(x)
ux[which.max(tabulate(match(x, ux)))]
}
它在我的小测试数据集上工作得很好,但是当我在我的实际数据集(2200 万行)上 运行 它只是 运行s 和 运行s 和运行秒。我所有其他 data.table 与 相似 的操作工作得很好而且非常快,但我没有使用 UDF。这是我的 data.table 查询的结构:
ModeCharacterColumns <- ExposureHistory[,lapply(.SD,Mode), .(Key1=Key1, Key2=Key2, ..., key7=key7, key8=key8), .SDcols=('col1','col2','col3', ..., 'col53')]
所以我猜我的问题是我的 UDF 确实在减慢速度,有没有人有任何建议可以让我完成相同的目标但更快地完成它?
谢谢大家!
编辑:
更好地表示数据:
DT <- fread("key1A key2A key3A key4A 2 2 4 s
key1A key2A key3A key4A 2 2 4 s
key1A key2A key3A key4A 8 8 8 t
key1A key2A key3A key4A 2 2 4 s
key1B key2B key3B key4B 6 6 6 v
key1B key2B key3B key4B 2 2 5 t
key1B key2B key3B key4B 2 2 5 v
key1B key2B key3B key4B 2 2 5 v")
期望的结果:
result <- fread("key1A key2A key3A key4A 2 2 4 s
key1B key2B key3B key4B 2 2 5 v")
尝试使用 data.table 对数据进行制表:
DT <- fread("key1 8
key1 2
key1 2
key1 8
key1 2
key1 2
key1 2
key1 8")
setkeyv(
DT[, .N, by = .(V1, V2)], #tabulate
c("V1", "N") #sort by N
)[, .(Mode = V2[.N]), by = V1] #most frequent value by V1
# V1 Mode
#1: key1 2
你需要仔细考虑打破平局。我实际上可能会使用 for
循环将其应用于更多值列,但如果您想让我尝试这样做,您需要提供一个具有代表性的可重现示例。
编辑:
Frank 提供了一种对评论中的多个值列执行此操作的选项:
DT[, lapply(.SD, function(x) setDT(list(x = x))[, .N, by=x][order(-N)][1L, x]), by=V1]
但是,我相信这会复制每个值列,这可能会减慢速度。
最快的解决方案是 collapse 包中的函数 fmode
,现在可在 CRAN 上使用。它在 C++ 中计算一个分组(和可选的加权)模式,速度非常令人满意。语法:
fmode(x, g = NULL, w = NULL, ...)
其中x
可以是向量、矩阵、data.frame或dplyr分组tibble,g
是分组向量或分组向量列表,w
是权重向量。对于混合类型的聚合,函数 collap
提供了一个简洁的解决方案。来电
collap(data, ~ id1 + id2, FUN = fmean, catFUN = fmode, ...)
按 id1 和 id2 聚合数据,将均值应用于 data
中的所有数字列,并将众数应用于 data
中的所有非数字(分类)列。默认情况下,返回的数据是按原始顺序排序的行和列。此解决方案比 data.table 更快,并且 collap
调用可以进一步定制。
我在 data.table 分组依据中汇总数据,我需要在组中获取变量的单个值。我希望这个值成为组的模式。我认为它需要模式,因为通常一个组是 8 行,它会有 2 行一个值,其他 6 行左右将是另一个值。
这是一个简化的例子,来自:
key1 2
key1 2
key1 2
key1 8
key1 2
key1 2
key1 2
key1 8
我想要这个:
key1 2
我在使用base R提供的标准模式功能时遇到了问题,所以我在这里使用了这个解决方案:
Mode <- function(x) {
ux <- unique(x)
ux[which.max(tabulate(match(x, ux)))]
}
它在我的小测试数据集上工作得很好,但是当我在我的实际数据集(2200 万行)上 运行 它只是 运行s 和 运行s 和运行秒。我所有其他 data.table 与 相似 的操作工作得很好而且非常快,但我没有使用 UDF。这是我的 data.table 查询的结构:
ModeCharacterColumns <- ExposureHistory[,lapply(.SD,Mode), .(Key1=Key1, Key2=Key2, ..., key7=key7, key8=key8), .SDcols=('col1','col2','col3', ..., 'col53')]
所以我猜我的问题是我的 UDF 确实在减慢速度,有没有人有任何建议可以让我完成相同的目标但更快地完成它?
谢谢大家!
编辑: 更好地表示数据:
DT <- fread("key1A key2A key3A key4A 2 2 4 s
key1A key2A key3A key4A 2 2 4 s
key1A key2A key3A key4A 8 8 8 t
key1A key2A key3A key4A 2 2 4 s
key1B key2B key3B key4B 6 6 6 v
key1B key2B key3B key4B 2 2 5 t
key1B key2B key3B key4B 2 2 5 v
key1B key2B key3B key4B 2 2 5 v")
期望的结果:
result <- fread("key1A key2A key3A key4A 2 2 4 s
key1B key2B key3B key4B 2 2 5 v")
尝试使用 data.table 对数据进行制表:
DT <- fread("key1 8
key1 2
key1 2
key1 8
key1 2
key1 2
key1 2
key1 8")
setkeyv(
DT[, .N, by = .(V1, V2)], #tabulate
c("V1", "N") #sort by N
)[, .(Mode = V2[.N]), by = V1] #most frequent value by V1
# V1 Mode
#1: key1 2
你需要仔细考虑打破平局。我实际上可能会使用 for
循环将其应用于更多值列,但如果您想让我尝试这样做,您需要提供一个具有代表性的可重现示例。
编辑:
Frank 提供了一种对评论中的多个值列执行此操作的选项:
DT[, lapply(.SD, function(x) setDT(list(x = x))[, .N, by=x][order(-N)][1L, x]), by=V1]
但是,我相信这会复制每个值列,这可能会减慢速度。
最快的解决方案是 collapse 包中的函数 fmode
,现在可在 CRAN 上使用。它在 C++ 中计算一个分组(和可选的加权)模式,速度非常令人满意。语法:
fmode(x, g = NULL, w = NULL, ...)
其中x
可以是向量、矩阵、data.frame或dplyr分组tibble,g
是分组向量或分组向量列表,w
是权重向量。对于混合类型的聚合,函数 collap
提供了一个简洁的解决方案。来电
collap(data, ~ id1 + id2, FUN = fmean, catFUN = fmode, ...)
按 id1 和 id2 聚合数据,将均值应用于 data
中的所有数字列,并将众数应用于 data
中的所有非数字(分类)列。默认情况下,返回的数据是按原始顺序排序的行和列。此解决方案比 data.table 更快,并且 collap
调用可以进一步定制。