根据条件r聚合

时间:2014-12-24 23:00:38

标签: r aggregate

我的数据集包含四列X1(id号),X2(日期时间),X3(日期时间),X4(持续时间),如下所示

test2 = structure(list(X1 = c(549395L, 678018L, 706197L, 549395L, 775731L, 789858L, 845277L, 936749L, 845277L, 954953L), X2 = c("6/16/2014", "9/16/2014", "2/12/2014", "6/16/2014", "8/29/2014", "2/26/2014", "4/7/2014", "2/14/2014", "5/18/2014", "3/5/2014"), X3 = c("6/4/2014 11:10", "9/16/2014 10:23", "2/12/2014 12:21", "6/4/2014 11:10", "8/29/2014 8:51", "2/26/2014 12:49", "4/7/2014 13:59", "2/14/2014 12:08", "4/7/2014 01:39", "3/5/2014 10:14"), X4 = c(8L, 21L, 10L, 72L, 39L, 14L, 41L, 31L, 43L, 24L)), .Names = c("X1", "X2", "X3", "X4"), class = "data.frame", row.names = c(NA, -10L))

       X1        X2              X3 X4
1  549395 6/16/2014  6/4/2014 11:10  8
2  678018 9/16/2014 9/16/2014 10:23 21
3  706197 2/12/2014 2/12/2014 12:21 10
4  549395 6/16/2014  6/4/2014 11:10 72
5  775731 8/29/2014  8/29/2014 8:51 39
6  789858 2/26/2014 2/26/2014 12:49 14
7  845277  4/7/2014  4/7/2014 13:59 41
8  936749 2/14/2014 2/14/2014 12:08 31
9  845277 5/18/2014  4/7/2014 01:39 43
10 954953  3/5/2014  3/5/2014 10:14 24

我想要完成的是

  1. 如果X1(ID号)在X3中具有相同的日期和时间,则将其X4替换为max x4 。例如,身份证号码(549395)具有相同的日期和时间(x3)6/4/2014 11:10,因此X4应替换为max(72, 8) ...72

  2. 如果X1(ID号)具有相同的日期但时间不同,则将其对应的X4替换为其各个x4的sum。例如,身份证号码(845277)具有相同的日期(2014年4月7日)不同时间(13:59,1:39),因此X4应为sum (43,41) = 84

  3. 输出应该如下所示。

           X1        X2              X3  X4
    1  549395 6/16/2014  6/4/2014 11:10  72
    2  678018 9/16/2014 9/16/2014 10:23  21
    3  706197 2/12/2014 2/12/2014 12:21  10
    4  549395 6/16/2014  6/4/2014 11:10  72
    5  775731 8/29/2014 8/29/2014  8:51  39
    6  789858 2/26/2014 2/26/2014 12:49  14
    7  845277  4/7/2014  4/7/2014 13:59  84
    8  936749 2/14/2014 2/14/2014 12:08  31
    9  845277 5/18/2014  4/7/2014 01:39  84
    10 954953  3/5/2014  3/5/2014 10:14  24
    

    我尝试使用聚合函数,但结果不在我想要完成的地方。所以需要帮助。

    新数据:

    test2=   structure(list(X1 = c(1491930L, 3162932L, 3162932L, 4092879L, 
    5374073L, 7427514L, 2377939L, 2377939L, 4081399L), X2 = structure(c(16073, 
    16073, 16073, 16073, 16074, 16073, 16081, 16081, 16077), class = "Date"), 
    X3 = structure(c(4L, 5L, 1L, 3L, 6L, 2L, 9L, 8L, 7L), .Label = c("2014-01-03 10:11", 
    "2014-01-03 11:1", "2014-01-03 11:44", "2014-01-03 8:47", 
    "2014-01-03 9:40", "2014-01-04 11:7", "2014-01-07 8:42", 
    "2014-01-11 2:58", "2014-01-11 2:6"), class = "factor"), 
    X4 = c(31, 26, 4, 32, 24, 132, 50, 16, 66)), .Names = c("X1", 
    "X2", "X3", "X4"), row.names = c(NA, -9L), class = "data.frame")
    

    期望的结果:

              X1         X2       date       time  X4
            1 1491930 2014-01-03 2014-01-03  8:47  31
            2 3162932 2014-01-03 2014-01-03  9:40  30
            3 3162932 2014-01-03 2014-01-03 10:11  30
            4 4092879 2014-01-03 2014-01-03 11:44  32
            5 5374073 2014-01-04 2014-01-04  11:7  24
            6 7427514 2014-01-03 2014-01-03  11:1 132
            7 2377939 2014-01-11 2014-01-11   2:6  66
            8 2377939 2014-01-11 2014-01-11  2:58  66
            9 4081399 2014-01-07 2014-01-07  8:42  66
    

2 个答案:

答案 0 :(得分:1)

这里有一个选项,你可以使用dplyr和tidyr来做到这一点。我包含了一些日期格式,这会增加" pipe"的长度,但它值得IMO。我将管道的那些部分标记为"可选"。

library(dplyr); library(tidyr)

test2 %>%
  separate(X3, into = c("date", "time"), sep = " ") %>%
  mutate_each(funs(as.Date(., format = "%Y-%m-%d")), X2, date) %>%
  group_by(X1, date, time) %>%
  mutate(X4 = max(X4)) %>%
  group_by(X1, date) %>%
  mutate(X4 = ifelse(n_distinct(time) == 1L, X4, sum(X4))) %>%
  ungroup() %>%
  unite(X3, date:time, sep = " ") %>%
  mutate(X3 = as.POSIXct(X3))

使用数据集,会产生:

Source: local data frame [9 x 4]

       X1         X2                  X3  X4
1 1491930 2014-01-03 2014-01-03 08:47:00  31
2 3162932 2014-01-03 2014-01-03 09:40:00  30
3 3162932 2014-01-03 2014-01-03 10:11:00  30
4 4092879 2014-01-03 2014-01-03 11:44:00  32
5 5374073 2014-01-04 2014-01-04 11:07:00  24
6 7427514 2014-01-03 2014-01-03 11:01:00 132
7 2377939 2014-01-11 2014-01-11 02:06:00  66
8 2377939 2014-01-11 2014-01-11 02:58:00  66
9 4081399 2014-01-07 2014-01-07 08:42:00  66

我首先将原始X3列分成两列数据和时间(使用与tidyr分开)并将列X2和日期格式化为实际日期格式。接下来,我按X1,日期和时间对数据进行分组,然后将X4替换为每个日期/时间组内的最大X4。然后我只按X1和日期对数据进行分组,并检查唯一时间值的数量是否为1.如果是,则返回X4不变,否则返回X4的总和。接下来是取消组合数据,将列日期和时间组合回单个列X3,并使用as.POSIXct将X3格式化为日期时间。


有一个假设的案例,它并不是100%清楚你期望的行为,但它可能与上面的代码有所不同。如果可能存在例如5个观察/行的id(X1)和日期组合,并且其中三个时间相同并且两个时间不同,则这将适用。对于该id-date组合的所有观察,上面的代码将导致X4完全相同。如果您不想要,可以使用下面的修改后的代码。我还将提供一个修改示例来演示行为。

首先,让我们创建一个新的数据集test3并对其进行修改,以便X1 == 3162932date == 2014-01-03有4个时间条目:2个相同(因此对于那些X4应该是被其最大值替换,2是不同的(因此对于那些我们应该用X4的总和替换X4)。

test3 <- test2
test3$X1[1:4] <- "3162932"
test3$X3[2] <- "2014-01-03 8:47"

> test3
       X1         X2               X3  X4  # all 4 first rows have the same id
1 3162932 2014-01-03  2014-01-03 8:47  31   #   the first two are 
2 3162932 2014-01-03  2014-01-03 8:47  26   #     same id, same date, same time
3 3162932 2014-01-03 2014-01-03 10:11   4   #   this and the next are
4 3162932 2014-01-03 2014-01-03 11:44  32   #     same id, same date, different times
5 5374073 2014-01-04  2014-01-04 11:7  24
6 7427514 2014-01-03  2014-01-03 11:1 132
7 2377939 2014-01-11   2014-01-11 2:6  50
8 2377939 2014-01-11  2014-01-11 2:58  16
9 4081399 2014-01-07  2014-01-07 8:42  66

以下是您如何处理它,因为我认为您喜欢(但同样,您还没有真正明确指出):

test3 %>%
  separate(X3, into = c("date", "time"), sep = " ") %>%
  mutate_each(funs(as.Date(., format = "%Y-%m-%d")), X2, date) %>%
  group_by(X1, date, time) %>%
  mutate(X4 = max(X4), check = n() == 1L) %>%
  group_by(X1, date) %>%
  mutate(X4 = ifelse(n_distinct(time) > 1L & check, sum(X4[check]), X4)) %>%
  ungroup() %>%
  unite(X3, date:time, sep = " ") %>%
  mutate(X3 = as.POSIXct(X3)) %>%
  select(ID = X1, Date = X2, DateTime = X3, Value = X4)

Source: local data frame [9 x 4]

       ID       Date            DateTime Value
1 3162932 2014-01-03 2014-01-03 08:47:00    31    # replaced by max
2 3162932 2014-01-03 2014-01-03 08:47:00    31    # replaced by max
3 3162932 2014-01-03 2014-01-03 10:11:00    36    # replaced by sum of 4 + 32
4 3162932 2014-01-03 2014-01-03 11:44:00    36    # replaced by sum of 4 + 32
5 5374073 2014-01-04 2014-01-04 11:07:00    24
6 7427514 2014-01-03 2014-01-03 11:01:00   132
7 2377939 2014-01-11 2014-01-11 02:06:00    66
8 2377939 2014-01-11 2014-01-11 02:58:00    66
9 4081399 2014-01-07 2014-01-07 08:42:00    66

答案 1 :(得分:1)

这是一个稍微简单的逻辑,使用data.table实现:

require(data.table)
setDT(test2)[, tmp := NA]

test2[, c("X4", "tmp") := if (.N > 1) list(max(X4), TRUE), by=.(X1, X3)] ## (1)
test2[is.na(tmp), X4 := sum(X4), by=.(X1, gsub(" .*$", "", X3))]         ## (2)
#         X1         X2               X3  X4 tmp
# 1: 1491930 2014-01-03  2014-01-03 8:47  31  NA
# 2: 3162932 2014-01-03  2014-01-03 9:40  30  NA
# 3: 3162932 2014-01-03 2014-01-03 10:11  30  NA
# 4: 4092879 2014-01-03 2014-01-03 11:44  32  NA
# 5: 5374073 2014-01-04  2014-01-04 11:7  24  NA
# 6: 7427514 2014-01-03  2014-01-03 11:1 132  NA
# 7: 2377939 2014-01-11   2014-01-11 2:6  66  NA
# 8: 2377939 2014-01-11  2014-01-11 2:58  66  NA
# 9: 4081399 2014-01-07  2014-01-07 8:42  66  NA
  1. 通过引用将test2从data.frame转换为data.table(无需任何其他副本)。现在test2是一个data.table,我们可以使用data.table语法。然后我们使用NA添加一个新的(虚拟)列(作为标记)。

  2. X1X3列进行汇总。如果有多行,则这些行属于您的第一个条件。我们已根据X4的最大值单独替换这些行。我们在(1)中执行此操作,但此外,我们还将这些行的虚拟列更新为TRUE。

  3. 然后,我们按X1分组,只分组X3的日期部分,但仅限于上一步中未修改的行。这些满足你的第二个条件。我们将X4替换为该组中所有值的总和。

  4. 这也应该考虑到你的行满足给定X1和日期的两个条件的情况。

    如有必要,您可以将tmp列设置为NULL

    test2[, tmp := NULL]