使用apply()对矩阵

时间:2017-01-05 06:26:36

标签: r subset apply

我觉得这一定是显而易见的事情,但我花了一整天时间试图解开它并寻找答案,所以我在这里发帖,希望有人有一些意见。

简短版本是使用apply()按照另一个数据帧中的值对数据帧进行子集并返回平均值,如果输入行数大于9,则函数返回前9次迭代的NA。如果输入行数小于9,它返回该数据就好了。对我而言,我所做的事情并不明显。

我有两个数据框。第一个是从众多样本中顺序采集的数据。 “ID”列的因子水平代表样本。在每个因子内,“长度”列对应于在样品的一条线上进行测量的位置。两个不同的测量有两个数据列。下面是一个简化的,可重复的,表示它的样子:

set.seed(10)
ID=factor(sort(rep(paste(letters[1:10]), times=10)))
Length=seq(1:10) + runif(10, 0, 0.9)
Values_1=c(1:20)
Values_2=c(21:40)
test_data=cbind.data.frame(ID, Length, Values_1, Values_2)

接下来,我有一个截断值矩阵,我想用它来“test_data”中的“长度”列的子集。每行显示我想要子集的样本,以及子集的起点和终点。

ID2=sort(rep(paste(letters[1:10]), times=2)) 
Start=c(1, 5, 1, 5)
Stop=c(5, 10, 7, 10)
Row=c(1:20)
cutoffs=cbind.data.frame(ID2, Start, Stop, Row)
colnames(cutoffs)=c("ID", "Start", "Stop", "Row")
#I'm recycling the cutoffs here. In reality the cutoffs are all pretty different

如果我手动对数据进行子集化,它适用于我选择的任何行,

r=9
subset1=test_data$Values_1[test_data[,1] == cutoffs[r,1] &
                           test_data[,2] >= cutoffs[r,2] &
                           test_data[,2] <= cutoffs[r,3] &
                           !is.na(test_data[,3])]
#[1] 1 2 3 4
mean(subset1)
#There are no NA's in this test data, but the !is.na is there to catch NA's that exist in the real data

但是当我构建一个apply函数来对所有数据进行子集时,事情变得奇怪,我不知道为什么。如果我运行该函数,它只返回cutoffs [10:20,]的值,前9个样本给出NA。但是,在第1行和第9行之间运行任何截止子集都会返回正确的值。

apply(cutoffs, 1, function(x){
  subset_1=test_data$Values_1[test_data[,1] == cutoffs[x[4],1] &
                              test_data[,2] >= cutoffs[x[4],2] &
                              test_data[,2] <= cutoffs[x[4],3] &
                              !is.na(test_data[,3])]
  subset_2=test_data$Values_2[test_data[,1] == cutoffs[x[4],1] &
                              test_data[,2] >= cutoffs[x[4],2] &
                              test_data[,2] <= cutoffs[x[4],3] &
                              !is.na(test_data[,4])]
  Mean_1=mean(subset_1)
  Mean_2=mean(subset_2)
  c(Mean_1, Mean_2)
})

    #     [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11] [,12] [,13] [,14] [,15] [,16] [,17] [,18] [,19] [,20]
    #[1,]   NA   NA   NA   NA   NA   NA   NA   NA   NA     7  13.5    17   2.5     7  13.5    17   2.5     7  13.5    17
    #[2,]   NA   NA   NA   NA   NA   NA   NA   NA   NA    27  33.5    37  22.5    27  33.5    37  22.5    27  33.5    37

#Running the same function, but subsetting below 9 rows it returns the correct values
#apply(cutoffs[1:9,], 1, function(x){...
#        1  2    3  4    5  6    7  8    9
#[1,]  2.5  7 13.5 17  2.5  7 13.5 17  2.5
#[2,] 22.5 27 33.5 37 22.5 27 33.5 37 22.5

我知道必须有一些很好的理由,但我无法弄清楚它是什么。任何帮助将不胜感激。

如果有更优雅的方式,请告诉我。实际数据集要大得多,相当于“截止”约为3K行,“test_data”为250K行。这个函数需要很长时间才能运行,所以我假设有更好的方法来执行此操作。

1 个答案:

答案 0 :(得分:1)

首先,不要在数据框上使用apply它会将df转换为矩阵,这意味着所有列都会被强制转换为单一类型。特别是,如果任何列是字符或因子,则生成的矩阵也将是字符。

但这不是问题所在。让我们看看你提出的第一个代码块:

subset1 <- test_data$Values_1[test_data[,1] == cutoffs[r,1] &
                              test_data[,2] >= cutoffs[r,2] &
                              test_data[,2] <= cutoffs[r,3] &
                              !is.na(test_data[,3])]

第二个代码块:

subset_1 <- test_data$Values_1[test_data[,1] == cutoffs[x[4],1] &
                               test_data[,2] >= cutoffs[x[4],2] &
                               test_data[,2] <= cutoffs[x[4],3] &
                               !is.na(test_data[,3])]
subset_2 <- test_data$Values_2[test_data[,1] == cutoffs[x[4],1] &
                               test_data[,2] >= cutoffs[x[4],2] &
                               test_data[,2] <= cutoffs[x[4],3] &
                               !is.na(test_data[,4])]

这些不一样(x的第4个元素的意义是什么?)。假设第一个代码块是你想要的,那么在所有行中应用它将是这样的。

sapply(seq_len(nrow(cutoffs)), function(r) {
    vals1 <- test_data$Values_1[test_data[,1] == cutoffs[r,1] &
                                test_data[,2] >= cutoffs[r,2] &
                                test_data[,2] <= cutoffs[r,3] &
                                !is.na(test_data[,3])]
    vals2 <- test_data$Values_2[test_data[,1] == cutoffs[r,1] &
                                test_data[,2] >= cutoffs[r,2] &
                                test_data[,2] <= cutoffs[r,3] &
                                !is.na(test_data[,3])]
    c(mean(vals1), mean(vals2))
})

#     [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11] [,12] [,13] [,14] [,15] [,16] [,17] [,18] [,19] [,20]
#[1,]  2.5    7 13.5   17  2.5    7 13.5   17  2.5     7  13.5    17   2.5     7  13.5    17   2.5     7  13.5    17
#[2,] 22.5   27 33.5   37 22.5   27 33.5   37 22.5    27  33.5    37  22.5    27  33.5    37  22.5    27  33.5    37