我的数据框如下:
position_time telematic_trip_no lat_dec lon_dec
1 2016-06-05 00:00:01 526132109 -26.6641 27.8733
2 2016-06-05 00:00:01 526028387 -26.6402 27.8059
3 2016-06-05 00:00:01 526081476 -26.5545 28.3263
4 2016-06-05 00:00:04 526140512 -26.5310 27.8704
5 2016-06-05 00:00:05 526140518 -26.5310 27.8704
6 2016-06-05 00:00:19 526006880 -26.5010 27.8490
is_stolen hour_of_day time_of_day day_of_week lat_min
1 0 0 0 Sunday -26.6651
2 0 0 0 Sunday -26.6412
3 0 0 0 Sunday -26.5555
4 0 0 0 Sunday -26.5320
5 0 0 0 Sunday -26.5320
6 0 0 0 Sunday -26.5020
lat_max lon_max lon_min
1 -26.6631 27.8743 27.8723
2 -26.6392 27.8069 27.8049
3 -26.5535 28.3273 28.3253
4 -26.5300 27.8714 27.8694
5 -26.5300 27.8714 27.8694
6 -26.5000 27.8500 27.8480
现在我要做的是计算 is_stolen = 1的每一行,数据框中满足以下条件的行数:
我已经使用 for 循环编写了一个脚本来执行此操作,但它慢慢地非常,这让我想到是否有一种有效的方法来执行复杂的行计数有很多条件使用像dplyr或data.table这样的东西?
ps如果你很好奇,我确实试图计算一辆偷车在一次典型行程中经过多少辆汽车:)
答案 0 :(得分:1)
鉴于您对问题的描述,以下内容应该有效
library(dplyr)
library(stats)
# df is the data.frame (see below)
df <- cbind(ID=seq_len(nrow(df)),df)
r.stolen <- which(df$is_stolen == 1)
r.not <- which(df$is_stolen != 1)
print(df[rep(r.not, times=length(r.stolen)),] %>%
setNames(.,paste0(names(.),"_not")) %>%
bind_cols(df[rep(r.stolen, each=length(r.not)),], .) %>%
mutate(in_range = as.numeric(telematic_trip_no != telematic_trip_no_not & time_of_day == time_of_day_not & day_of_week == day_of_week_not & lat_dec >= lat_min_not & lat_dec <= lat_max_not & lon_dec >= lon_min_not & lon_dec <= lon_max_not)) %>%
group_by(ID) %>%
summarise(count = sum(in_range)) %>%
arrange(desc(count)))
第一行只是将一个名为ID
的列添加到df
,该列按行号标识行,我们稍后可以dplyr::group_by
进行计数。
接下来的两行将行划分为被盗和未被盗的汽车。关键是:
N
次,其中N
是未被盗车辆的行数,M
次,其中M
是被盗汽车行的数量,(3)的结果具有从原始数据框中枚举所有被盗和未被盗行的行,以便您的条件可以以数组方式应用。作为代码第四行的dplyr
管道R工作流程(包含在print()
中)执行此操作:
times
_not
附加到列名称,以便在绑定列时将它们与被盗的汽车列区分开来。感谢this SO answer获得该宝石。each
复制被盗车辆行,并使用dplyr::bind_cols
dplyr::mutate
创建一个名为in_range
的新列,该列是应用条件的结果。布尔结果转换为{0,1}
以便于累积in_range
的计数,按ID
分组,并按计数的递减顺序排列结果。请注意,现在ID
是标识is_stolen = 1
原始数据框行的列,而ID_not
是is_stolen = 0
这假定您希望计算原始数据框中is_stolen = 1
的每一行,这就是您在问题中所说的内容。如果您真的想要被盗的每个telematic_trip_no
的计数,那么您可以使用
group_by(telematic_trip_no) %>%
而在管道中。
我已使用以下数据片段对此进行了测试
df <- structure(list(position_time = structure(c(1L, 1L, 1L, 2L, 3L,
4L, 4L, 5L, 6L, 7L, 8L, 9L, 10L), .Label = c("2016-06-05 00:00:01",
"2016-06-05 00:00:04", "2016-06-05 00:00:05", "2016-06-05 00:00:19",
"2016-06-05 00:00:20", "2016-06-05 00:00:22", "2016-06-05 00:00:23",
"2016-06-05 00:00:35", "2016-06-05 00:09:34", "2016-06-06 01:00:06"
), class = "factor"), telematic_trip_no = c(526132109L, 526028387L,
526081476L, 526140512L, 526140518L, 526006880L, 526017880L, 526027880L,
526006880L, 526006890L, 526106880L, 526005880L, 526007880L),
lat_dec = c(-26.6641, -26.6402, -26.5545, -26.531, -26.531,
-26.501, -26.5315, -26.5325, -26.501, -26.5315, -26.5007,
-26.5315, -26.5315), lon_dec = c(27.8733, 27.8059, 28.3263,
27.8704, 27.8704, 27.849, 27.88, 27.87, 27.849, 27.87, 27.8493,
27.87, 27.87), is_stolen = c(0L, 0L, 0L, 0L, 0L, 0L, 1L,
1L, 1L, 1L, 1L, 1L, 1L), hour_of_day = c(0L, 0L, 0L, 0L,
0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L), time_of_day = c(0L,
0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 9L, 0L), day_of_week = structure(c(2L,
2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 1L), .Label = c("Monday",
"Sunday"), class = "factor"), lat_min = c(-26.6651, -26.6412,
-26.5555, -26.532, -26.532, -26.502, -26.532, -26.532, -26.502,
-26.532, -26.502, -26.532, -26.532), lat_max = c(-26.6631,
-26.6392, -26.5535, -26.53, -26.53, -26.5, -26.53, -26.53,
-26.5, -26.53, -26.5, -26.53, -26.53), lon_max = c(27.8743,
27.8069, 28.3273, 27.8714, 27.8714, 27.85, 27.8714, 27.8714,
27.85, 27.8714, 27.85, 27.8714, 27.8714), lon_min = c(27.8723,
27.8049, 28.3253, 27.8694, 27.8694, 27.848, 27.8694, 27.8694,
27.848, 27.8694, 27.848, 27.8694, 27.8694)), .Names = c("position_time",
"telematic_trip_no", "lat_dec", "lon_dec", "is_stolen", "hour_of_day",
"time_of_day", "day_of_week", "lat_min", "lat_max", "lon_max",
"lon_min"), class = "data.frame", row.names = c(NA, -13L))
在此,我将7
个新行is_stolen = 1
添加到原始6
行is_stolen = 0
:
telematic_trip_no = 526005880
添加的第一行违反了所有未被盗行的经度条件,因此其计数应为0
telematic_trip_no = 526006880
添加的第二行违反了所有未被盗行的纬度条件,因此其计数应为0
telematic_trip_no = 526007880
的第三个添加的行违反了所有未被盗行的telematic_trip_no
条件,因此其计数应为0
telematic_trip_no = 526006890
的第四个添加行符合未被盗的行4
和5
的条件,因此其计数应为2
telematic_trip_no = 526106880
个第五行符合行6
未被盗的条件,因此其计数应为1
telematic_trip_no = 526017880
的第六行添加了违反所有未被盗行的time_of_day
条件,因此其计数应为0
telematic_trip_no = 526027880
的第七行添加的行违反了所有未被盗行的day_of_week
条件,因此其计数应为0
在此数据上运行代码会产生:
# A tibble: 7 x 2
ID count
<int> <dbl>
1 10 2
2 11 1
3 7 0
4 8 0
5 9 0
6 12 0
7 13 0
正如预期的那样回忆起is_stolen = 1
附加的行从7
开始ID = 7
。
如果要按telematic_trip_no
进行分组,我们会得到结果:
# A tibble: 7 x 2
telematic_trip_no count
<int> <dbl>
1 526006890 2
2 526106880 1
3 526005880 0
4 526006880 0
5 526007880 0
6 526017880 0
7 526027880 0
作为一个警告,上述方法确实会耗费内存。最坏的情况是行数增加到N^2/4
,其中N
是原始数据帧中的行数,并且用于评估条件的数据帧的列数加倍。与大多数阵列处理技术一样,速度和内存之间存在交易。
希望这有帮助。
答案 1 :(得分:1)
The current development version of data.table, v1.9.7有一个新功能非equi 连接,这使得条件连接非常简单。使用@ aichao的数据:
require(data.table) # v1.9.7+
setDT(df)[, ID := .I] # add row numbers
not_stolen = df[is_stolen == 0L]
is_stolen = df[is_stolen == 1L]
not_stolen[is_stolen,
.(ID = i.ID, N = .N - sum(telematic_trip_no == i.telematic_trip_no)),
on = .(time_of_day, day_of_week, lat_min <= lat_dec,
lat_max >= lat_dec, lon_min <= lon_dec, lon_max >= lon_dec),
by=.EACHI][, .(ID, N)]
# ID N
# 1: 7 NA
# 2: 8 NA
# 3: 9 0
# 4: 10 2
# 5: 11 1
# 6: 12 NA
# 7: 13 NA
部分not_stolen[is_stolen,
执行子集式连接操作..即,对于is_stolen
中的每一行,匹配行索引(基于在提供给on=
参数的条件下提取。
by = .EACHI
确保i
(第一个)参数中的每一行,is_stolen
,在相应的匹配行索引上,j
中提供的表达式,第二个参数,.(ID = i.ID, N = .N-sum(telematic_trip_no==i.telematic_trip_no)),
被评估。返回上面显示的结果。
HTH。