代码之家  ›  专栏  ›  技术社区  ›  rj2700

对不同行和列进行减法运算,并按组分隔

  •  5
  • rj2700  · 技术社区  · 13 年前

    我真的不想连续问两个问题,但这是我无法理解的问题。假设我有一个数据帧,如下所示:

       df
       Row# User    Morning     Evening     Measure Date
       1    1          NA          NA          2/18/11
       2    1          50          115         2/19/11
       3    1          85          128         2/20/11
       4    1          62          NA          2/25/11
       5    1          48          100.8        3/8/11
       6    1          19          71          3/9/11
       7    1          25          98          3/10/11
       8    1          NA          105         3/11/11
       9    2          48          105         2/18/11
       10   2          28          203         2/19/11
       11   2          35          80.99        2/21/11
       12   2          91          78.25        2/22/11
    

    是否可以在R中取前一个连续日(以及 只有 前一天,而不是前一个结果)1行的晚间值和不同行的早晨值?所以我想要的结果是这样的。

       df
       Row# User    Morning     Evening     Date        Difference
       1       1      NA          NA        2/18/11        NA
       2       1      50          115       2/19/11        NA
       3       1      85          129       2/20/11        30
       4       1      62          NA        2/25/11        NA
       5       1      48          100.8     3/8/11         NA
       6       1      19          71        3/9/11         81.8
       7       1      25          98        3/10/11        46
       8       1      10          105       3/11/11        88
       9       2      48          105       2/18/11        NA
       10      2      28          203       2/19/11        77
       11      2      35          80.99     2/21/11        NA
       12      2      91          78.25     2/22/11        -10.01
    

    我想做的就是取早上的值,然后从每个用户组连续前一天的晚上的值中减去它。正如你所看到的,我的数据帧的某些部分在上午和晚上的列中包含NA值,此外,并不是每个不同用户的所有日期都是连续的,所以自然应该分配NA。

    我试过在谷歌上搜索,但没有太多关于能够将函数应用于不同列上每组行的不同行的信息(如果这有意义的话)。

    我的尝试包括许多不同的内容。

    df$Difference<-ave((df$Morning,df$Evening),
                        df$User,
                        FUN=function(x){
                            c('NA',diff(df$Evening-df$Morning)),na.rm=T
                       })
    

    再次感谢您的帮助。谢谢

    3 回复  |  直到 13 年前
        1
  •  4
  •   Arun    13 年前

    注: 您显示的输入数据和输出数据不相同。有一个 NA 被替换为 10 在输出中,最后一个日期为 2/14/11 输入和 2/22/11 在输出中。

    我假设输出是原始数据,以创建与您的结果匹配的答案。

    df$Diff <- c(NA, head(df$Evening, -1) - tail(df$Morning, -1))
    df$Diff[which(c(0, diff(as.Date(as.character(df$Measure_Date), 
                     format="%m/%d/%Y"))) != 1)] <- NA
    
    > df
    
    #    Row User Morning Evening Measure_Date   Diff
    # 1    1    1      NA      NA      2/18/11     NA
    # 2    2    1      50  115.00      2/19/11     NA
    # 3    3    1      85  128.00      2/20/11  30.00
    # 4    4    1      62      NA      2/25/11     NA
    # 5    5    1      48  100.80       3/8/11     NA
    # 6    6    1      19   71.00       3/9/11  81.80
    # 7    7    1      25   98.00      3/10/11  46.00
    # 8    8    1      10  105.00      3/11/11  88.00
    # 9    9    2      48  105.00      2/18/11     NA
    # 10  10    2      28  203.00      2/19/11  77.00
    # 11  11    2      35   80.99      2/21/11     NA
    # 12  12    2      91   78.25      2/22/11 -10.01
    

    @user1342086的编辑(被拒绝了,但确实是正确的):

    df$Diff[which(diff(df$User) != 0)] <- NA
    

    似乎负责按“用户”进行分组。

        2
  •  4
  •   Blue Magister    13 年前

    盲目的第一枪(未经测试)。依赖于已经按用户和日期排序的数据帧。

    #if necessary, transform your dates from factor to Date
    df$Date <- as.Date(levels(df$Date)[df$Date],format="%m/%d/%y")
    
    df <- within(df, 
      Difference <- ifelse(c(NA,diff(Measure_Date)) == 1 & diff(User) == 0, 
        c(NA,head(Evening,-1)) - Morning, NA
      )
    )
    
        3
  •  2
  •   Oscar de León    13 年前

    我用过 plyr ,所以一定要安装它。即使用户数据混合,此解决方案也应该有效( 即 不在连续的行中),并且日期不按时间顺序排列。

    # Your example data, as you should post it for us to use
    df <-
    structure(list(User = c(1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 
    2L, 2L), Morning = c(NA, 50L, 85L, 62L, 48L, 19L, 25L, NA, 48L, 
    28L, 35L, 91L), Evening = c(NA, 115, 128, NA, 100.8, 71, 98, 
    105, 105, 203, 80.99, 78.25), Measure_Date = structure(c(1L, 
    2L, 3L, 5L, 9L, 10L, 6L, 7L, 1L, 2L, 4L, 8L), .Label = c("2/18/11", 
    "2/19/11", "2/20/11", "2/21/11", "2/25/11", "3/10/11", "3/11/11", 
    "3/14/11", "3/8/11", "3/9/11"), class = "factor")), .Names = c("User", 
    "Morning", "Evening", "Measure_Date"), class = "data.frame", row.names = c(NA, 
    -12L))
    
    # As already stated by Arun, you need the date as class Date
    df$Measure_Date <- as.Date(df$Measure_Date, format='%m/%d/%y')
    
    
    # Use plyr to procces the dataframe by user
    library(package=plyr)
    ddply(.data=df, .variables='User',
          .fun=function(x){
            # Complete sequence of dates for each user
            tdf <- data.frame(Measure_Date=seq(from=min(x$Measure_Date),
                                               to=max(x$Measure_Date),
                                               by='1 day'))
    
            # Merge to fill in NAs for unused dates
            tdf <- merge(tdf, x, all=TRUE)
    
            # Put desired values side by side
            tdf$Evening <- c(NA, tdf$Evening[-length(tdf$Evening)])
    
            # Diference
            tdf$Difference  <- tdf$Evening - tdf$Morning
    
            # Return desired value to original data
            tdf <- tdf[,c('Measure_Date', 'Difference')]
            x <- merge(x, tdf)
            x
          })