根据线性间隔更新data.frame中的字段

时间:2014-06-24 18:57:56

标签: r dataframe

我有这个例子data.frame:

my.df = data.frame(id = rep("a",10), start = seq(100, 1000, 100), end = seq(150, 1050, 100), flag = c(1,0,0,1,0,1,0,1,0,1))
> my.df
   id start  end flag
1   a   100  150    1
2   b   200  250    0
3   c   300  350    0
4   d   400  450    1
5   e   500  550    0
6   f   600  650    1
7   g   700  750    0
8   h   800  850    1
9   i   900  950    0
10  j  1000 1050    1

行是线性间隔,按my.df$start排序,然后按升序排序my.df$end。请注意,根据定义,任何两行my.df$flag = 1的行都至少有一行,my.df$flag = 0。

我想要的是以下列方式更新my.df$id字段:

首先,使用my.df$flag = 1更新行,以便my.df$flag = 1行的任何外观都会将该行的更新my.df$id字段从ai增加到a.i +1,其中i是一个初始化为0的整数。

然后,需要以下列方式更新my.df$flag = 0的所有行: 从my.df开头到my.df$flag = 1的第一行的行将my.df$id = a.1。任何两行my.df$flag = 1的行(即任何两行my.df$flag = 1 ai和a.i + 1)my.df$start小于mid的行my.df$id = ai的结尾与my.df$id的开头之间的点= a.i + my.df$flag = 1的两行中的1将使用my.df$id = ai更新my.df$start大于或等于相同中点的行将使用my.df$id = a.i + 1进行更新。从my.df$flag = 1的最后一行到my.df的最后一行之后my.df$flag = 0的行将具有my.df$id的最后一行的my.df$flag值= 1.请注意,my.df的第一行和/或最后一行可能实际上是my.df$flag = 1的行。

因此,此示例的输出应为:

> my.updated.df
    id start  end flag
1  a.1   100  150    1
2  a.1   200  250    0
3  a.2   300  350    0
4  a.2   400  450    1
5  a.2   500  550    0
6  a.3   600  650    1
7  a.3   700  750    0
8  a.4   800  850    1
9  a.4   900  950    0
10 a.5  1000 1050    1

1 个答案:

答案 0 :(得分:1)

您可以使用cumsum上的d$flag将数据拆分为“数据块”。对于其中的每一个,您将计算“结束”和“开始”之间的中点,并使用它来通过将其与d$id进行比较来定义d$start。我稍微修改了您的示例数据框,以便包含flag = 0行出现在第一个flag=1之前和最后一个flag=1之后的边缘情况。请注意,如果d$id是您示例my.df中的一个因素,我们首先需要将其转换为字符才能使其生效(d$id <- as.character(d$id))。

d <- structure(list(id = c("a", "a", "a", "a", "a", "a", "a", "a", 
    "a", "a", "a", "a"), start = c(100, 100, 200, 300, 400, 500, 
    600, 700, 800, 900, 1000, 1000), end = c(110, 150, 250, 350, 
    450, 550, 650, 750, 850, 950, 1050, 1100), flag = c(0, 1, 0, 
    0, 1, 0, 1, 0, 1, 0, 1, 0)), .Names = c("id", "start", "end", 
    "flag"), row.names = c(NA, 12L), class = "data.frame")

# Create a 'subset index'. Rows with the same index will be
# compared to the same midpoint.
d$subset.idx <- cumsum(d$flag) 

# For each index, compute the midpoint that the 'start'
# value of each row with that index needs to be compared to
mid <- d[d$flag == 1, ]
mid$midpoint <- c((mid$start[2:nrow(mid)] - mid$end[1:(nrow(mid) - 1)]) / 2 +
    mid$end[1:(nrow(mid) - 1)], 0)
mid <- mid[c("subset.idx", "midpoint")]
mid <- rbind(c(0, 0), mid) # before merging with d, add a row for subset.idx = 0 

# Merge with d and assign id by comparing start to midpoint
d <- merge(d, mid)
d$id <- ifelse(d$start < d$midpoint,
    paste0("a.", d$subset.idx),
    paste0("a.", d$subset.idx + 1))
# Finally, handle edge cases, i.e. those with flag 0 before and after
# the first and last flag = 1 respectively
d[d$subset.idx == 0, "id"] <- "a.1"
d[d$subset.idx == max(d$subset.idx), "id"] <- paste0("a.", max(d$subset.idx))
d <- d[- which(names(d) %in% c("subset.idx", "midpoint"))]