【问题标题】:how to subtract rows in a data frame in R depending on difference between values?如何根据值之间的差异减去R中数据框中的行?
【发布时间】:2018-04-13 20:50:13
【问题描述】:

我有一个格式如下的数据框

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

我尝试过使用循环,但由于行数太多,效率低下。是否有一些内置功能可以让我更快地做到这一点? 提前致谢。

【问题讨论】:

  • 令我惊讶的是,您似乎不想在输出中添加任何标签。您是否不想知道与输出中的行对应的名称?
  • 首先,通过增加location 对数据帧进行排序(排序)似乎很明显。那么距离列就是diff(location)。然后,您可以使用 location 列上 +/- 1000 的滑动窗口来完成剩下的工作。
  • @smci 如果有很多 (>2) 个位置彼此靠近,则不会。这实际上发生在示例数据中。
  • @Renu:它确实使它更简单,因为我们可以使用简单的逻辑索引:在location 上创建一个 +/- 1000 的窗口,不包括给定的行。然后我们可以在该窗口上使用 sapply/dplyr/data.table 聚合。我从来没有说过窗口只会包含一个成员。
  • @smci 我怀疑您是否会得到标准的分组工具来正常工作。如果您有位置 2000、2050 和 2120,则您没有互斥的石斑鱼。也许rollapply 有这方面的东西?如果我要做一个 R 解决方案,我会在 data.table 中使用非 equi 连接来模仿 renu 的答案。

标签: r dataframe data-science


【解决方案1】:
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

【讨论】:

  • 很好的答案。对于这样的事情,SQL 比在 R 中进行一百万 x 百万连接并过滤要好得多。
  • @Gregor 同意sqldf 可能是更好的选择,这是一个很好的答案。与此同时,我研究了一个基于data.table 的解决方案,我尝试通过加入x.location &gt; i.location 来最小化笛卡尔连接。
【解决方案2】:

我同意@Gregor 的评论,他提到sqldf 在上述情况下是更好的选择,因为它避免了百万条记录的笛卡尔连接。

但我尝试通过首先加入x.location &gt; i.location 然后过滤diff &lt;=1000 来优化基于data.table 的解决方案。

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

【讨论】:

  • 如果您添加列df[, loc_max := location + 1000],您可以将location &lt; loc_max 添加到on 条件,而不是事后过滤。我不太确定内部是如何工作的,但至少很有可能它会提高内存效率。
  • 非常感谢您的 cmets。让我对这些选项进行一些基准测试。这可以给我们一些有用的结果。
  • @Arun 简要提到了 data.table 用于加入该视频约 10 分钟的方法 channel9.msdn.com/Events/useR-international-R-User-conference/…
  • @Renu 我认为non-equii join 会根据 Arun 在他的视频中的详细信息而有效。 Equii 连接可能会更快,但在上述情况下,创建列会产生开销。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-09-28
  • 1970-01-01
  • 2020-12-27
  • 2014-03-07
  • 2018-03-22
相关资源
最近更新 更多