在函数中进行子集以计算行总数

时间:2011-01-03 09:55:31

标签: function r sum subset

我有一个包含某些工具结果的数据框,我想创建一个包含每行总数的新列。因为每次对新数据进行分析时我都有不同数量的仪器,所以我需要一个函数来动态计算带有行总数的新列。

简单地说我的问题,这就是我的数据框:

    Type    Value
1   A   10
2   A   15
3   A   20
4   A   25
5   B   30
6   B   40
7   B   50
8   B   60
9   B   70
10  B   80
11  B   90

我的目标是实现以下目标:

    A   B   Total
1   10  30  40
2   15  40  55
3   20  50  70
4   25  60  85
5       70  70
6       80  80
7       90  90

我尝试了各种方法,但这种方式最有希望:

myList <- list(a = c(10, 15, 20, 25), b = c(30, 40, 50, 60, 70, 80, 90))
tmpDF <- data.frame(sapply(myList, '[', 1:max(sapply(myList, length))))
> tmpDF
   a  b
1 10 30
2 15 40
3 20 50
4 25 60
5 NA 70
6 NA 80
7 NA 90
totalSum <- rowSums(tmpDF)
totalSum <- data.frame(totalSum)
tmpDF <- cbind(tmpDF, totalSum)
> tmpDF
   a  b totalSum
1 10 30       40
2 15 40       55
3 20 50       70
4 25 60       85
5 NA 70       NA
6 NA 80       NA
7 NA 90       NA

尽管这种方式成功地组合了两个不同长度的数据帧,但'rowSums'函数在此示例中给出了错误的值。除此之外,我的原始数据不是列表格式,因此我无法应用这样的“解决方案”。

我觉得我这个问题太复杂了,所以我想知道我怎么能......

  • 基于“类型”的数据框子集数据,
  • 将这些不同长度的单个子集插入到新的数据框中,
  • 在此数据框中添加“总计”列,该列是正确的总和 个别子集。

这个问题的另一个复杂因素是,这需要以函数或动态方式完成,这样我就不需要手动将几十个“类型”(A,B,C和所以在我的数据框中。

到目前为止,这是我所拥有的,但它不起作用,但说明了我正在思考的问题:

TotalDf <- function(x){
    tmpNumberOfTypes <- c(levels(x$Type))
    for( i in tmpNumberOfTypes){
        subSetofData <- subset(x, Type = i, select = Value)
        if( i == 1) {
        totalDf <- subSetOfData }
        else{
        totalDf <- cbind(totalDf, subSetofData)}
    }
    return(totalDf)
}

提前感谢您对此的任何想法或想法,

此致

EDIT:

感谢Joris的评论(见下文),我在正确的方向上取得了成果,但是,当我尝试将他的解决方案转换为我的数据框时,我遇到了其他问题。他提出的答案有效,并给出了A和B值的以下(正确)总和:

> tmp78 <- tapply(DF$value,DF$id,sum)
> tmp78
 1  2  3  4  5  6 
 6  8 10 12  9 10 
> data.frame(tmp78)
  tmp78
1     6
2     8
3    10
4    12
5     9
6    10

但是,当我在我的数据框架上尝试此解决方案时,它不起作用:

> subSetOfData <- copyOfTradesList[c(1:3,11:13),c(1,10)]
> subSetOfData
   Instrument AccountValue
1         JPM         6997
2         JPM         7261
3         JPM         7545
11        KFT         6992
12        KFT         6944
13        KFT         7069
> unlist(sapply(rle(subSetOfData$Instrument)$lengths,function(x) 1:x))
Error in rle(subSetOfData$Instrument) : 'x' must be an atomic vector
> subSetOfData$InstrumentNumeric <- as.numeric(subSetOfData$Instrument)
> unlist(sapply(rle(subSetOfData$InstrumentNumeric)$lengths,function(x) 1:x))
     [,1] [,2]
[1,]    1    1
[2,]    2    2
[3,]    3    3
> subSetOfData$id <- unlist(sapply(rle(subSetOfData$InstrumentNumeric)$lengths,function(x) 1:x))
Error in `$<-.data.frame`(`*tmp*`, "id", value = c(1L, 2L, 3L, 1L, 2L,  : 
  replacement has 3 rows, data has 6

我有一个令人不安的想法,就是我绕圈子走了......

2 个答案:

答案 0 :(得分:3)

两个想法:

1)你可以在rowSums中使用na.rm = T

2)你怎么知道哪一个与哪个一起去?您可以添加一些索引。

例如:

DF <- data.frame(
  type=c(rep("A",4),rep("B",6)),
  value = 1:10,
  stringsAsFactors=F
)


DF$id <- unlist(lapply(rle(DF$type)$lengths,function(x) 1:x))

现在,您可以轻松tapply原始数据框上的总和

tapply(DF$value,DF$id,sum)

更重要的是,以正确的形式获取数据框:

> DF
   type value id
1     A     1  1
2     A     2  2
3     A     3  3
4     A     4  4
5     B     5  1
6     B     6  2
7     B     7  3
8     B     8  4
9     B     9  5
10    B    10  6

> library(reshape)
> cast(DF,id~type)
  id  A  B
1  1  1  5
2  2  2  6
3  3  3  7
4  4  4  8
5  5 NA  9
6  6 NA 10

答案 1 :(得分:0)

TV <- data.frame(Type = c("A","A","A","A","B","B","B","B","B","B","B")
             , Value = c(10,15,20,25,30,40,50,60,70,80,90)
             , stringsAsFactors = FALSE)

# Added Type C for testing
# TV <- data.frame(Type = c("A","A","A","A","B","B","B","B","B","B","B", "C", "C", "C")
#                  , Value = c(10,15,20,25,30,40,50,60,70,80,90, 100, 150, 130)
#                  , stringsAsFactors = FALSE)

lnType <- with(TV, tapply(Value, Type, length))
lnType <- as.integer(lnType)
lnType

id <- unlist(mapply(FUN = rep_len, length.out = lnType, x = list(1:max(lnType))))
(TV <- cbind(id, TV))

require(reshape2)
tvWide <- dcast(TV, id ~ Type)

# Alternatively
# tvWide <- reshape(data = TV,  direction = "wide", timevar = "Type",  ids = c(id, Type))

tvWide <- subset(tvWide, select = -id)

# If you want something neat without the <NA>
# for(i in 1:ncol(tvWide)){
#
#     if (is.na(tvWide[j,i])){
#       tvWide[j,i] = 0
#     }
#     
#   }
# }

tvWide
transform(tvWide, rowSum=rowSums(tvWide, na.rm = TRUE))