将k列数据重塑为2列数据,表示k个变量的连续值对。

9

I have a data frame like this:

id y1 y2 y3 y4  
--+--+--+--+--
a |12|13|14|  
b |12|18|  |
c |13|  |  |
d |13|14|15|16  

我希望将其改造成两列的形式。那么上面的例子将变为:
id from to  
--+----+--- 
a |12  |13  
a |13  |14  
a |14  |
b |12  |18
b |18  |  
c |13  |
d |13  |14  
d |14  |15  
d |15  |16  

每个id都有一对年份值的'from'和'to'。
有人知道怎样简单地做到这点吗?我尝试过使用reshape2。我还查看了将多列合并为整洁数据,但我认为我的情况不同。
3个回答

5
您可以使用lapply循环遍历列对并使用rbind将它们合并起来:
do.call(rbind,
        lapply(2:(length(df)-1), 
               function(x) setNames(df[!is.na(df[,x]),c(1,x,x+1)], 
                                    c("id", "from", "to"))))
   id from to
1   a   12 13
2   b   12 18
3   c   13 NA
4   d   13 14
11  a   13 14
21  b   18 NA
41  d   14 15
12  a   14 NA
42  d   15 16

5
一种解决方案使用 dplyrtidyrdt2 是最终输出结果。
# Create example data frame
dt <- data.frame(id = c("a", "b", "c", "d"),
                 y1 = c(12, 12, 13, 13),
                 y2 = c(13, 18, NA, 14),
                 y3 = c(14, NA, NA, 15),
                 y4 = c(NA, NA, NA, 16),
                 stringsAsFactors = FALSE)

# Load packages
library(dplyr)
library(tidyr)

# Process the data
dt2 <- dt %>%
  gather(STEP, from, -id) %>%
  drop_na(from) %>%
  arrange(id, STEP) %>%
  group_by(id) %>%
  mutate(to = lead(from)) %>%
  select(-STEP)

整洁,尽管会产生额外的一行。 - HubertL
1
@HubertL 感谢您的评论。我知道最后一行不在 OP 所需的输出中,但我认为它应该在那里。至少我没有找到一个好的逻辑来排除最后一行,因为 id 为 abc 的最后一行也在那里。 - www
它不应该,因为它的 to 值是由 lead 生成的。重排:df %>% gather(var,from,-id)%>% arrange(id,var)%>% group_by(id)%>% mutate(to = lead(from))%>% #slice(-n())%>% filter(!is.na(from),var! ='y4')%>% select(-var) - alistaire
1
如果 id=="b" 有一个 18-NA 行,我不明白为什么 id=="d" 不应该有一个 16-NA 行。 - thelatemail
@alistaire 感谢您的建议。让我们等待OP对此进行澄清。目前,我同意 @thelatemail 的观点。如果 18-NA 在id b中,则 16-NA 也应在id d中。 - www

4
在基础R中,使用stack函数将每个组的所有行向后移动一行。以@ycw的示例数据dt为例:
tmp <- na.omit(cbind(dt[1], stack(dt[-1])[-2]))
names(tmp)[2] <- "from"
tmp$to <- with(tmp, ave(from, id, FUN=function(x) c(tail(x,-1),NA) ))
tmp[order(tmp$id),]

#   id from to
#1   a   12 13
#5   a   13 14
#9   a   14 NA
#2   b   12 18
#6   b   18 NA
#3   c   13 NA
#4   d   13 14
#8   d   14 15
#12  d   15 16
#16  d   16 NA

data.table 的世界中,同样的逻辑适用。先进行 melt 操作,然后使用 shift by= id
library(data.table)
dt <- as.data.table(dt)

melt(dt, id.vars="id", value.name="from")[
  !is.na(from),-"variable"][, to := shift(from,1,type="lead"), by=id
][order(id)]

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接