R:通过比较两行将长度转换为宽度

时间:2013-04-10 07:41:41

标签: r

我花了4天的时间试图解决这个问题,从其他相关问题中学到了很多,但仍然找不到有效的解决方案。这是:

我有一个包含500k乘40个变量的数据集,需要将变量从长到大转换为两个连续行之间的比较。
数据样本如下:

df <- data.frame(id=c(267, 268, 269, 269, 270, 271, 272, 272, 273, 274),              
           quant=c(2,1,4,4,1,5,2,2,3,1),
           pts=  c(3,2,7,11,2,4,5,9,6,4),
           kind=c('v','v', 'v', 'c', 'v', 'v', 'v', 'c', 'v','v'))

    id quant pts kind
1  267     2   3    v
2  268     1   2    v
3  269     4   7    v
4  269     4  11    c
5  270     1   2    v
6  271     5   4    v
7  272     2   5    v
8  272     2   9    c
9  273     3   6    v
10 274     1   4    v

请注意,每次 id 重复时, quant ,变量 kind 在一行中假定值为“v”,另一个'c'。相反,'c'的值仅出现在具有重复 id 的记录中。

我打算获得以下data.frame:

    id quant pts kind  c
1  267     2   3    v  0
2  268     1   2    v  0
3  269     4   7    v 11
5  270     1   2    v  0
6  271     5   4    v  0
7  272     2   5    v  9
9  273     3   6    v  0
10 274     1   4    v  0    

我使用以下代码弄明白了:

df$c <- 0
df$delete <- 0
for (i in 1:(dim(df)[1] - 1)) {
  if (df[i,'id'] == df[i+1, 'id'] & df[i+1, 'kind'] == 'c')
  {
    df[i, 'c'] <- df[i+1, 'pts']
    df[i+1, 'delete'] <- 1
  }
}
df <- df[df$delete == 0, ]
df$delete <- NULL    

这是令人讨厌和丑陋的,但它确实有效,虽然在原始数据集中需要大约10个小时!

正确代码的任何想法?
非常感谢!

2 个答案:

答案 0 :(得分:1)

以下是可用于此示例的基本方法:

df$c <- with(df, ave(pts, id, quant, FUN = function(x) {
  ifelse(length(x) == 1, 0, tail(x, 1))
}))
df <- df[df$kind == "v", ]
df
#     id quant pts kind  c
# 1  267     2   3    v  0
# 2  268     1   2    v  0
# 3  269     4   7    v 11
# 5  270     1   2    v  0
# 6  271     5   4    v  0
# 7  272     2   5    v  9
# 9  273     3   6    v  0
# 10 274     1   4    v  0

更新

顺便说一下,使用data.table可以让更多更有趣。

以下是数据:

library(data.table)
DT <- data.table(id = c(267, 268, 269, 269, 270, 271, 272, 272, 273, 274), 
                 quant = c(2, 1, 4, 4, 1, 5, 2, 2, 3, 1),
                 pts = c(3, 2, 7, 11, 2, 4, 5, 9, 6, 4),
                 kind = c('v','v', 'v', 'c', 'v', 'v', 'v', 'c', 'v','v'),
                 key = c("id", "quant"))
DT
#      id quant pts kind
#  1: 267     2   3    v
#  2: 268     1   2    v
#  3: 269     4   7    v
#  4: 269     4  11    c
#  5: 270     1   2    v
#  6: 271     5   4    v
#  7: 272     2   5    v
#  8: 272     2   9    c
#  9: 273     3   6    v
# 10: 274     1   4    v

以下是您正在寻找的内容:

DT[, c := ifelse(length(pts) == 1, 0, tail(pts, 1)), by = key(DT)][kind == "v"]
#     id quant pts kind  c
# 1: 267     2   3    v  0
# 2: 268     1   2    v  0
# 3: 269     4   7    v 11
# 4: 270     1   2    v  0
# 5: 271     5   4    v  0
# 6: 272     2   5    v  9
# 7: 273     3   6    v  0
# 8: 274     1   4    v  0

答案 1 :(得分:0)

这是使用包plyr的方法:

DF <- ddply(df, .(id), function(x) cbind(x[1,], c=x$pts[match("c", x$kind)]))
DF$c[is.na(DF$c)] <- 0
# or
DF <- ddply(df, .(id), function(x) cbind(x[1,], c=sum(x$pts*(x$kind=="c"))))