比较分组数据帧中的组

时间:2017-08-20 06:23:11

标签: r compare dplyr grouping sequential

我正在尝试对数据框中后续组中的项目进行比较 - 我想当你知道自己在做什么时,这很容易...

我的数据集可以表示如下:

set.seed(1)
data <- data.frame(
 date = c(rep('2015-02-01',15), rep('2015-02-02',16), rep('2015-02-03',15)),
 id = as.character(c(1005 + sample.int(10,15,replace=TRUE), 1005 + sample.int(10,16,replace=TRUE), 1005 + sample.int(10,15,replace=TRUE)))
)

产生的数据框如下所示:

date    id
1/02/2015   1008
1/02/2015   1009
1/02/2015   1011
1/02/2015   1015
1/02/2015   1008
1/02/2015   1014
1/02/2015   1015
1/02/2015   1012
1/02/2015   1012
1/02/2015   1006
1/02/2015   1008
1/02/2015   1007
1/02/2015   1012
1/02/2015   1009
1/02/2015   1013
2/02/2015   1010
2/02/2015   1013
2/02/2015   1015
2/02/2015   1009
2/02/2015   1013
2/02/2015   1015
2/02/2015   1008
2/02/2015   1012
2/02/2015   1007
2/02/2015   1008
2/02/2015   1009
2/02/2015   1006
2/02/2015   1009
2/02/2015   1014
2/02/2015   1009
2/02/2015   1010
3/02/2015   1011
3/02/2015   1010
3/02/2015   1007
3/02/2015   1014
3/02/2015   1012
3/02/2015   1013
3/02/2015   1007
3/02/2015   1013
3/02/2015   1010

然后我想按日期(group_by)对数据进行分组,然后在比较组之前过滤掉重复项(不同)。我想要做的是每天确定添加哪个新ID以及哪个id离开。因此,比较第1天和第2天以确定第2天中不是第1天的id和第1天但在第2天不存在的id,然后在第2天和第3天之间进行相同的比较等。
使用anti_join(dplyr)可以非常轻松地进行比较,但我不知道如何引用数据集中的各个组。

我的尝试(或我的一次尝试)看起来像:

data %>%
  group_by(date) %>%
  distinct(id) %>%
  do(lost = anti_join(., lag(.), by="id"))

但当然这不起作用,我得到:

Error in anti_join_impl(x, y, by$x, by$y) : Can't join on 'id' x 'id' because of incompatible types (factor / logical)

我试图做什么甚至可能,或者我是否应该考虑编写一个笨重的功能来做它?

4 个答案:

答案 0 :(得分:0)

只需将输入stringsAsFactors = FALSE添加到您的数据框即可。这将使您的代码运行:虽然我不确定输出的结果是否是您正在寻找的结果。要查看整个结果,请将其传递到data.frame并查看它是否是您要查找的内容。希望这会有所帮助。

 set.seed(1)
 data <- data.frame(
    date = c(rep('2015-02-01',15), rep('2015-02-02',16), rep('2015-02-3',15)),
    id = as.character(c(1005 + sample.int(10,15,replace=TRUE), 1005 + sample.int(10,16,replace=TRUE), 1005 + sample.int(10,15,replace=TRUE))),stringsAsFactors = FALSE)


data %>%
  group_by(date) %>%
  distinct(id) %>%
  do(lost = anti_join(., lag(.), by="id"))%>%data.frame()

答案 1 :(得分:0)

对数据进行一些操作,合并可能会做你想要的。像这样的东西

df <- unique(data)
df$date <- as.Date(df$date)
df$leftdate <- df$date + 1
df$prevdate <- df$date - 1
df2 <- cbind(df[,c("date","id")],flag =  1)

# merge the dataframe so that each day would attempt to join the next day
dfleft <- merge(df,df2,by.x = c("leftdate","id"),by.y = c("date","id"),all.x= TRUE)
# if there is no common id between a day and the next day, the merge returns NA, which is the desired results for those who left
dfleft <- dfleft[is.na(dfleft$flag),c("leftdate","id")]

# Here, you reverse the logic to find those who show up today but weren't there yesterday
dfnew <- merge(df,df2,by.x = c("prevdate","id"),by.y = c("date","id"),all.x= TRUE)
dfnew <- dfnew[is.na(dfnew$flag),c("date","id")]

答案 2 :(得分:0)

我对该问题的理解是,数据显示了每个日期的ID,我们希望迭代日期中的ID与前一个日期的ID进行比较。

首先获取唯一行u并将id转换为数字。然后将id分割为date,并s,并定义一个函数diffs,该函数会使用负数生成已添加ID的数字向量,用于删除ID。 lapply表示seq_along(s)除了第一个组件,因为它没有先前的组件。没有包使用。

u <- unique(data)
u$id <- as.numeric(as.character(u$id))
s <- split(u$id, u$date)
diffs <- function(i) c(setdiff(s[[i]], s[[i-1]]), - setdiff(s[[i-1]], s[[i]]))
diffs_list <- setNames(lapply(seq_along(s)[-1], diffs), names(s)[-1])

,并提供:

> diffs_list
$`2015-02-02`
[1]  1010 -1011

$`2015-02-03`
[1]  1011 -1015 -1009 -1008 -1006

或者您希望数据框作为输出

setNames(stack(diffs_list), c("id", "date"))

,并提供:

     id       date
1  1010 2015-02-02
2 -1011 2015-02-02
3  1011 2015-02-03
4 -1015 2015-02-03
5 -1009 2015-02-03
6 -1008 2015-02-03
7 -1006 2015-02-03

<强> magrittr

这也可以使用像这样的magrittr包表达,其中diffs定义如上。

library(magrittr)

data %>%
     unique %>%
     transform(id = as.numeric(as.character(id))) %>%
     { split(.$id, .$date) } %>%
     { setNames(lapply(seq_along(.)[-1], diffs), names(.)[-1]) }

注意:我已将data$date中的-3替换为-03。

答案 3 :(得分:0)

我确定我没有为自己的答案投票,但我必须说我最喜欢我的答案。我希望得到一个使用dplyr工具来解决问题的答案,所以我一直在研究,我想我现在有一个(半)优雅的解决方案(除了我的函数中的for循环)。

以相同的方式生成样本数据集,但使用更多数据使其更有趣:

set.seed(1)
data <- data.frame(
  date = c(rep('2015-02-01',15), rep('2015-02-02',16), rep('2015-02-03',15), rep('2015-02-04',15), rep('2015-02-05',15)),
  id = as.character(c(1005 + sample.int(10,15,replace=TRUE), 1005 + sample.int(10,16,replace=TRUE), 1005 + sample.int(10,15,replace=TRUE), 1005 + sample.int(10,15,replace=TRUE), 1005 + sample.int(10,15,replace=TRUE)))
)

通过interweb搜索我发现了dplyr函数&#39; nest()&#39;这看起来解决了我的所有分组问题。 nest()函数接受group_by()创建的组,并将它们滚动到数据框列表中,这样您最终会为每个已分组的变量输入一个条目,然后为所有适合的变量的数据框输入那个小组 - 在这里:

dataNested <- data %>%
  group_by(date) %>%
  distinct(id) %>%
  nest()

这产生了一个相当奇怪的数据框,如下所示:

     date          data
1    2015-02-01    list(id = c(3, 4, 6, 10, 9, 7, 1, 2, 8))
2    2015-02-02    list(id = c(5, 8, 10, 4, 3, 7, 2, 1, 9))
3    2015-02-03    list(id = c(6, 5, 2, 9, 7, 8))
4    2015-02-04    list(id = c(1, 5, 8, 7, 9, 3, 4, 6, 10))
5    2015-02-05    list(id = c(3, 5, 4, 7, 8, 1, 9))

因此列表中的索引引用了id的列表(奇怪但是真实)。

现在允许我们按索引编号引用组:

dataNested$data[[2]]

返回:

# A tibble: 9 × 1
      id
  <fctr>
1   1010
2   1013
3   1015
4   1009
5   1008
6   1012
7   1007
8   1006

从这里开始,这是一个简单的问题,即写一个函数会使anti_join让我们只留下每个后续组之间的差异(尽管这是我不感到骄傲的部分,并且真正开始显示我缺乏R技能 - 请随意提出改进建议):

## Function departed() - returns the id's that were dropped from each subsequent time period
departed <- function(groups) {
  tempList <- vector("list", nrow(groups))
  # Loop through the groups and do an anti_join between each
  for (i in seq(1, nrow(groups) - 1)) {
  tempList[[i + 1]] <-
  anti_join(data.frame(groups$data[[i]]),  data.frame(groups$data[[i + 1]]), by = "id")

  }
  return(tempList)
}

将此功能应用于我们的嵌套数据会产生已离开的ID列表:

> departedIDs <- dataNested %>% departed()

> departedIDs
[[1]]
NULL

[[2]]
    id
1 1011

[[3]]
    id
1 1006
2 1008
3 1009
4 1015

[[4]]
    id
1 1007

[[5]]
    id
1 1011
2 1015

我希望这个答案可以帮助那些大脑以与我相同的方式工作的人。