我有一个格式如下的数据框
df <- data.frame(name=LETTERS[1:5], location=c(2000,2021,4532,1931,3457),
value=c(1,0,1,1,0))
name location value
A 2000 1
B 2021 0
C 4532 1
D 1931 1
E 3457 0
数据框中有大约一百万行。如果位置在彼此的1000之内,我如何创建一个具有每个位置之间距离的新数据帧,还会检查两个位置的值是否都是一个?
对于上述数据集,数据框中只有三行,其值为21(绝对值为2000 - 2021),69(绝对值为2000 - 1931)和90(绝对值为2021-1931) )因为这些是唯一小于1000的差异。它也会有一列0(因为A和B值不是1和1),1(因为A和C值是1和1)和0(因为B和C不是1和1)。所以它看起来像:
21 0
69 1
90 0
我尝试过使用循环,但由于行数太多,效率很低。是否有一些内置功能,我应该用它来更快地做到这一点? 提前谢谢。
答案 0 :(得分:4)
library(sqldf)
sqldf("
select a.location
, b.location
, a.location - b.location as locdiff
, a.value*b.value as value
from df a
inner join df b
on a.location - b.location between 1 and 1000
")
这给出了
a.location b.location locdiff value
1 2000 1931 69 1
2 2021 2000 21 0
3 2021 1931 90 0
或data.table
。这只是@MKR的解决方案,但添加了一列以避免大的连接结果。不确定是否可以在不创建新列的情况下实现此目的。
setDT(df)
df[, loc2 := location - 1000]
df[df
, .( locdiff = i.location - x.location
, locationA = i.location
, locationB = x.location
, value = x.value*i.value)
, on = .(location >= loc2
, location < location)
, nomatch = 0]
给出
locdiff locationA locationB value
1: 69 2000 1931 1
2: 90 2021 1931 0
3: 21 2021 2000 0
答案 1 :(得分:2)
我同意@Gregor的评论,他提到sqldf
在上面的场景中更好的选择,因为它避免了百万条记录的笛卡尔连接。
但我尝试优化基于data.table
的解决方案,首先加入x.location > i.location
,然后过滤diff <=1000
。
df <- data.frame(name=LETTERS[1:5], location=c(2000,2021,4532,1931,3457),
value=c(1,0,1,1,0))
library(data.table)
setDT(df)
df[df,.(name, diff = x.location - i.location, value = x.value*i.value),
on=.(location > location), nomatch=0][diff<=1000]
# name diff value
# 1: B 21 0
# 2: A 69 1
# 3: B 90 0