在R中,拆分数据框,使子集数据框包含先前数据帧的最后一行和后续数据帧的第一行

时间:2015-11-18 20:21:46

标签: r dataframe

如何分割数据框有很多答案,例如How to split a data frame?

但是,我想分割一个数据帧,以便较小的数据帧包含前一个数据帧的最后一行和下一个数据帧的第一行。

这是一个例子

n <- 1:9
group <- rep(c("a","b","c"), each = 3)
data.frame(n = n, group)

  n  group
1 1     a
2 2     a
3 3     a
4 4     b
5 5     b
6 6     b
7 7     c
8 8     c
9 9     c

我希望输出看起来像:

 d1 <- data.frame(n = 1:4, group = c(rep("a",3),"b"))
 d2 <- data.frame(n = 3:7, group = c("a",rep("b",3),"c"))
 d3 <- data.frame(n = 6:9, group = c("b",rep("c",3)))
 d <- list(d1, d2, d3)
 d

[[1]]
  n group
1 1     a
2 2     a
3 3     a
4 4     b

[[2]]
  n group
1 3     a
2 4     b
3 5     b
4 6     b
5 7     c

[[3]]
  n group
1 6     b
2 7     c
3 8     c
4 9     c

完成此任务的有效方法是什么?

5 个答案:

答案 0 :(得分:5)

假设DF是原始data.frame,其中包含n列和group列。设nDF中的行数。现在定义一个函数extract,它给出一系列索引ix,将它放大到包括第一个之前和之后的那个,然后返回DF的那些行。现在我们已定义extract,按组拆分向量1,...,n,并将extract应用于拆分的每个组件。

n <- nrow(DF)
extract <- function(ix) DF[seq(max(1, min(ix) - 1), min(n, max(ix) + 1)), ]
lapply(split(seq_len(n), DF$group), extract)

$a
  n group
1 1     a
2 2     a
3 3     a
4 4     b

$b
  n group
3 3     a
4 4     b
5 5     b
6 6     b
7 7     c

$c
  n group
6 6     b
7 7     c
8 8     c
9 9     c

答案 1 :(得分:4)

或者为什么不尝试使用by,{&#34; [a] ppl [功能]按功能分割数据框[INDICES]&#34; 。

by(data = df, INDICES = df$group, function(x){
   id <- c(min(x$n) - 1, x$n, max(x$n) + 1)
   na.omit(df[id, ])
   })


# df$group: a
#   n group
# 1 1     a
# 2 2     a
# 3 3     a
# 4 4     b
# -------------------------------------------------------------------------------- 
#   df$group: b
# n group
# 3 3     a
# 4 4     b
# 5 5     b
# 6 6     b
# 7 7     c
# -------------------------------------------------------------------------------- 
#   df$group: c
#   n group
# 6 6     b
# 7 7     c
# 8 8     c
# 9 9     c

尽管print by方法创造了一个“幻想”的方法。输出,(默认)结果是list,其元素由分组变量的级别命名(仅在结果对象上尝试strnames。)

答案 2 :(得分:3)

我打算在@cdetermans的回答下发表评论,但现在已经太晚了。 您可以使用data.table::shift(或dyplr::lag)来概括他的方法,以便找到组索引,然后在范围上运行简单的lapply,例如

library(data.table) # v1.9.6+ 
indx <- setDT(df)[, which(group != shift(group, fill = TRUE))]
lapply(Map(`:`, c(1L, indx - 1L), c(indx, nrow(df))), function(x) df[x,])
# [[1]]
#    n group
# 1: 1     a
# 2: 2     a
# 3: 3     a
# 4: 4     b
# 
# [[2]]
#    n group
# 1: 3     a
# 2: 4     b
# 3: 5     b
# 4: 6     b
# 5: 7     c
# 
# [[3]]
#    n group
# 1: 6     b
# 2: 7     c
# 3: 8     c
# 4: 9     c

答案 3 :(得分:1)

也可以使用data.frame,但有没有理由不使用data.table?此外,还可以选择并行执行。

library(data.table)
n <- 1:9
group <- rep(c("a","b","c"), each = 3)
df <- data.table(n = n, group)
df[, `:=` (group = factor(df$group))]
df[, `:=` (group_i = seq_len(.N), group_N = .N), by = "group"]

library(doParallel)
groups <- unique(df$group)
foreach(i = seq(groups)) %do% {
  df[group == groups[i] | (as.integer(group) == i + 1 & group_i == 1) | (as.integer(group) == i - 1 & group_i == group_N), c("n", "group"), with = FALSE]  
}
[[1]]
   n group
1: 1     a
2: 2     a
3: 3     a
4: 4     b
[[2]]
   n group
1: 3     a
2: 4     b
3: 5     b
4: 6     b
5: 7     c
[[3]]
   n group
1: 6     b
2: 7     c
3: 8     c
4: 9     c

答案 4 :(得分:0)

这是另一种dplyr方式:

library(dplyr)

data = 
  data_frame(n = n, group) %>%
  group_by(group)

firsts = 
  data %>%
  slice(1) %>%
  ungroup %>%
  mutate(new_group = lag(group)) %>%
  slice(-1)

lasts = 
  data %>%
  slice(n()) %>%
  ungroup %>%
  mutate(new_group = lead(group)) %>%
  slice(-n())

bind_rows(firsts, data, lasts) %>%
  mutate(final_group = 
           ifelse(is.na(new_group),
                  group,
                  new_group) ) %>%
  arrange(final_group, n) %>%
  group_by(final_group)