【问题标题】:R how can I calculate difference between rows in a data frameR如何计算数据框中行之间的差异
【发布时间】:2013-04-25 10:15:05
【问题描述】:

这是我的问题的一个简单示例:

> df <- data.frame(ID=1:10,Score=4*10:1)
> df
       ID Score
    1   1    40
    2   2    36
    3   3    32
    4   4    28
    5   5    24
    6   6    20
    7   7    16
    8   8    12
    9   9     8
    10 10     4
    > diff(df)

Error in r[i1] - r[-length(r):-(length(r) - lag + 1L)] : 
  non-numeric argument to binary operator

谁能告诉我为什么会出现这个错误?

【问题讨论】:

  • 请注意,aPaulT 回答了您的问题,如果您通过单击勾选标记将其选为正确,它将对网站有所帮助。或任何其他出色的解决方案,如果您认为这会更有帮助。

标签: r diff dataframe


【解决方案1】:

diff 需要矩阵或向量而不是数据框。试试

data.frame(diff(as.matrix(df)))

【讨论】:

  • +1 nice - 我想这是有道理的,因为矩阵只是一个具有第二个 dim 属性的向量。
  • 作为参考,一些 benchmarks 在 1000000 行 x 5 列数据集上。
  • 请注意,与 head/tailnrow(df) 解决方案不同,这不适用于某些类型的非数字对象,例如 POSIXlt。 (dplyr 解决方案也因 POSIXlt 对象而失败)
  • 确实基准表明diff(as.matrix)data.tablefastest。但我可能会在未来几年继续使用head/tailseq_along
【解决方案2】:

也许你正在寻找这样的东西:

> tail(df, -1) - head(df, -1)
   ID Score
2   1    -4
3   1    -4
4   1    -4
5   1    -4
6   1    -4
7   1    -4
8   1    -4
9   1    -4
10  1    -4

如果两个data.frames 的维度相同,您可以将它们相减或相加。因此,我们在这里所做的是减去缺少第一行 (tail(df, -1)) 的 data.frame 和缺少最后一行 (head(df, -1)) 的 head(df, -1),然后减去它们。

【讨论】:

  • +1 富有想象力。我永远不会认为带有 -1 的 head 会返回除第一行之外的所有内容。聪明
【解决方案3】:

使用dplyr 的另一个选项是使用mutate_each 循环遍历所有列,获取列(.) 与列(lag) 的差异(.) 并删除NA顶部带有na.omit()的元素

library(dplyr)
df %>%
    mutate_each(funs(. - lag(.))) %>%
    na.omit() 

编辑:

使用 mutate_all 代替 mutate_each(已弃用 - 正如@PatrickT 所述)

df %>%
    mutate_all(funs(. - lag(.))) %>%
    na.omit() 

或者使用来自data.tableshift。将 'data.frame' 转换为 'data.table' (setDT(df)),遍历列 (lapply(.SD, ..)) and get the difference between the column (x) and thelag(shiftby default gives thelagastype = "滞后”`)。删除第一个观察值,即 NA 元素。

library(data.table)
setDT(df)[, lapply(.SD, function(x) (x- shift(x))[-1])]

【讨论】:

    【解决方案4】:

    因为 df 适用于向量或矩阵。您可以使用 apply 跨列应用函数,如下所示:

     apply( df , 2 , diff )
       ID Score
    2   1    -4
    3   1    -4
    4   1    -4
    5   1    -4
    6   1    -4
    7   1    -4
    8   1    -4
    9   1    -4
    10  1    -4
    

    您似乎不太可能想要计算顺序 ID 的差异,因此您可以选择将其应用于所有列除了第一个列,如下所示:

    apply( df[-1] , 2 , diff )
    

    或者你可以使用data.table(不是它在这里添加任何东西我只是真的想开始使用它!),我再次假设你不想将diff应用于ID列:

    DT <- data.table(df)
    DT[ , list(ID,Score,Diff=diff(Score))  ]
        ID Score Diff
     1:  1    40   -4
     2:  2    36   -4
     3:  3    32   -4
     4:  4    28   -4
     5:  5    24   -4
     6:  6    20   -4
     7:  7    16   -4
     8:  8    12   -4
     9:  9     8   -4
    10: 10     4   -4
    

    感谢@AnandaMahto,另一种语法可以让您更灵活地选择在哪些列上运行它:

    DT[, lapply(.SD, diff), .SDcols = 1:2]
    

    这里.SDcols = 1:2 表示您要将diff 函数应用于第1 列和第2 列。如果您有20 列并且不想将其应用于ID,则可以使用.SDcols=2:20 作为示例。

    【讨论】:

    • +1 表示编辑。这将使未来的访问者对这个问题不再感到困惑。
    • 我试过了,但最后一行是 0。我如何做与昨天的区别 - 今天并将值放在今天的行中,以便最后一列不为 0? +1
    【解决方案5】:

    我想展示一种替代方法来做这种事情,即使我经常觉得这样做并不受欢迎:使用 sql。

    sqldf(paste("SELECT a.ID,a.Score"
                ,"      , a.Score - (SELECT b.Score"
                ,"                   FROM df b"
                ,"                   WHERE b.ID < a.ID"
                ,"                   ORDER BY b.ID DESC"
                ,"                   ) diff"
                ," FROM df a"
                )
          )
    

    代码看起来很复杂,但实际上并不复杂,它有一些优势,正如您在结果中看到的那样:

        ID Score diff
     1   1    40 <NA>
     2   2    36 -4.0
     3   3    32 -4.0
     4   4    28 -4.0
     5   5    24 -4.0
     6   6    20 -4.0
     7   7    16 -4.0
     8   8    12 -4.0
     9   9     8 -4.0
     10 10     4 -4.0
    

    一个优点是您使用原始数据框(无需转换为其他类)并获得一个数据框(将其放入 res

    df2 <- data.frame(ID=1:10,grp=rep(c("v","w"), each=5),Score=4*10:1)
    
    sqldf(paste("SELECT a.ID,a.grp,a.Score"
                ,"      , a.Score - (SELECT b.Score"
                ,"                   FROM df2 b"
                ,"                   WHERE b.ID < a.ID"
                ,"                         AND a.grp = b.grp"
                ,"                   ORDER BY b.ID DESC"
                ,"                   ) diff"
         ," FROM df2 a"
         )
    )
    
    
       ID grp Score diff
    1   1   v    40 <NA>
    2   2   v    36 -4.0
    3   3   v    32 -4.0
    4   4   v    28 -4.0
    5   5   v    24 -4.0
    6   6   w    20 <NA>
    7   7   w    16 -4.0
    8   8   w    12 -4.0
    9   9   w     8 -4.0
    10 10   w     4 -4.0
    

    【讨论】:

      【解决方案6】:

      为了完整性,几年后添加这个 - 您也可以使用简单的 [.data.frame 子集来实现这一点

      df[-1, ] - df[-nrow(df), ]
      #    ID Score
      # 2   1    -4
      # 3   1    -4
      # 4   1    -4
      # 5   1    -4
      # 6   1    -4
      # 7   1    -4
      # 8   1    -4
      # 9   1    -4
      # 10  1    -4
      

      【讨论】:

      • @akrun,你有一些不错的东西。从长远来看,dplyr 会给你带来很多支持——保证。请发帖
      • 好吧,虽然这违反了我的原则:-)
      • 甚至以后...我是 R 新手,并试图避免使用库。每个人都建议为一项任务使用 5 个不同的库,这些库可以用普通的 R 来解决。我没有这种添加依赖项来解决简单问题的心态。
      • @r2p2 好吧,这是不断将 R 标记为“难学”语言与 dplyr/etc 的结果。作为“非常直观和容易”。就目前而言,R 社区的很大一部分就像一只盲目追随神圣领袖 Hadley Wickham 的绵羊——你再也无法对此无所作为。
      【解决方案7】:

      看看这个答案: Compute difference between rows in R and setting in zero first difference

      我认为这是最简单的方法。

      df <- data.frame(ID=1:8, x2=8:1, x3=11:18, x4=c(2,4,10,0,1,1,9,12))
      df$vardiff <- c(0, diff(df$x4))
      df
      

      【讨论】:

        【解决方案8】:

        我个人的简单方法是使用 pivot_wider 创建另一列,然后您可以使用 summarise 减去差异:D

        全部来自 dyplr 包

        【讨论】:

          猜你喜欢
          • 2021-01-14
          • 2022-12-09
          • 1970-01-01
          • 2020-05-23
          • 1970-01-01
          • 2021-11-08
          • 2021-12-25
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多