2013-06-05 2 views
1

У меня есть матрица (именованные точки в этом примере) с большим количеством строк (< 90 000) и всего две колонки.Матрица с большим количеством строк

A B 
1 10.1 
2 9.2 
3 4.5 
1 8.9 
1 0.7 

Я хочу, чтобы создать еще одну матрицу только уникальных значений из столбца «А» и среднее из значений столбца «B», которые соответствуют дублированные значения (ов) .Result: -

A B 
1 6.56 
2 9.20 
3 4.50 

В настоящее время я использую это (ниже кода), который занимает много времени. Поэтому я был бы очень благодарен, если кто-то может посоветовать мне, как ускорить эти расчеты.

uniquedata<-points[which(!duplicated(points[,"A"])),] 
reps<-points[which(duplicated(points[,"A"])),] 
result<-list() 
intensity<-list() 
      for(i in c(1:length(uniquedata[,"A"]))){ 
       result[[i]]<-which(uniquedata[i,"A"]==reps[,"A"]) 
      } 
      for(j in c(1:length(result))){ 
       if(length(result[[j]])!=0){ 
        intensity[j]<-mean(c(reps[result[[j]],"B"],uniquedata[j,"B"])) 
       }else{ 
        intensity[j]<-uniquedata[j,"B"] 
       } 
      } 
      points1<-cbind(uniquedata[,1],unlist(intensity)) 

Я понимаю, что я делаю много индексирования, поэтому он медленный. Заранее благодарим за помощь!

+1

Если у вас возникает соблазн использовать циклы 'for', подобные этому в R, вы всегда должны сделать шаг назад и спросить себя, если это может быть общей задачей. Тогда вам нужно только подумать о поисковых терминах. – Roland

+1

Действительно, я буду в будущем. Спасибо за совет! – jsin

ответ

0

Обязательный data.table Ответ:

set.seed(42) 
m <- cbind(a=sample(1:3,1e4,TRUE),b=rnorm(1e4)) 

library(data.table) 
DT <- as.data.table(m) 
DT[,mean(b),by=a] 

# a   V1 
# 1: 3 -0.
# 2: 1 0.01064392 
# 3: 2 -0.02411601 
2

Если я понял ваш вопрос, вы пытаетесь агрегировать свои данные по первому столбцу и вычислять среднее значение значений во втором столбце. Вы можете использовать ряд функций в R (aggregate, by, tapply). Ниже приведен пример использования агрегата.

> my.data <- data.frame(name = sample(1:5, 1000, replace = TRUE), vals = runif(1000)) 
> head(my.data) 
    name  vals 
1 3 0.12357187 
2 2 0.50271246 
3 5 0.03868217 
4 5 0.48045079 
5 5 0.35684145 
6 5 0.36128855 
> aggregate(vals ~ name, FUN = mean, data = my.data) 
    name  vals 
1 1 0.4657559 
2 2 0.4920722 
3 3 0.5062826 
4 4 0.5169585 
5 5 0.4857688 
+0

+1 - что бы я сделал. В конкретном случае OP: 'aggregate (B ~ A, points, mean)' (возможно, с 'na.rm = TRUE' в зависимости от данных) –

+0

+1 Спасибо большое. Просто переусердствовал, насколько глупым был мой код. – jsin

0

Это многолетнее. This тесно связан и имеет более бенчмаркинг и некоторые более продвинутые методы, такие как настройка ключа. Для полноты картины, вот некоторые другие подходы:

Марка воспроизводимые:

set.seed(1) 
df1 <- data.frame(name=sample(1:5, 1000, replace = TRUE), 
         val = runif(1000)) 
head(df1) 

дает:

name  val 
1 2 0.53080879 
2 2 0.68486090 
3 3 0.38328339 
4 5 0.95498800 
5 2 0.11835658 
6 5 0.03910006 

tapply можно рассматривать как создание таблицы кросс-классификации, то применение функции к нему как в:

tapply(df1$val,df1$name,mean) 

дает:

 1   2   3   4   5 
0.4946062 0.4822890 0.5110930 0.5030683 0.4604779 

plyr полезно для более сложных вариантов 'Раскол/применить/объединить':

library(plyr) 
ddply(df1, .(name), summarize, mu=mean(val)) 

дает:

name  mu 
1 1 0.4946062 
2 2 0.4822890 
3 3 0.5110930 
4 4 0.5030683 
5 5 0.4604779 

Также есть

by(df1, df1$name, mean) 

, который дает этот (довольно нестойкий) выход:

df1$name: 1 
    name  val 
1.0000000 0.4946062 
------------------------------------------------------------ 
df1$name: 2 
    name  val 
2.000000 0.482289 
------------------------------------------------------------ 
df1$name: 3 
    name  val 
3.000000 0.511093 
------------------------------------------------------------ 
df1$name: 4 
    name  val 
4.0000000 0.5030683 
------------------------------------------------------------ 
df1$name: 5 
    name  val 
5.0000000 0.4604779 

EDIT: бенчмаркинга удален

+1

Ваш бенчмаркинг кажется неправильным. – mnel

+0

Спасибо, я тоже так думал, но я попытался повторить его несколько раз, и результаты оказались последовательными. Не знаю, где я здесь ошибся ... Я отредактирую, чтобы удалить в ближайшее время. – dardisco

3

Учитывая у вас есть матрица, существует реальная необходимость преобразовать в data.frame.Вот подход, использующий rowsum

# assuming your matrix is called M 

rowsum(M[,2],M[,1])/rowsum(rep_len(1,nrow(M)), M[,1]) 

Некоторые надлежащего бенчмаркинг

using.by <- function() x <- by(df1$val, df1$name, mean) 
using.aggregate <- function() x <- aggregate(val ~ name, FUN = mean, data = df1) 
using.ddply <- function() x <- ddply(df1, .(name), summarize, mu=mean(val)) 
using.tapply <- function() tapply(df1$val,df1$name,mean) 
using.rowsum <- function() x <- rowsum(M[,2],M[,1])/rowsum(rep_len(1,nrow(M)), M[,1]) 
using.data.table <- function() x <- DT[,mean(val),by=name] 

library(microbenchmark) 

set.seed(1) 
n <- 1e6 
df1 <- data.frame(name=sample(1:5, n, replace = TRUE), 
        val = runif(n)) 
M <- as.matrix(df1) 
DT <- as.data.table(df1) 

microbenchmark(using.by(), using.aggregate(), using.ddply(), 
       using.tapply(), using.rowsum(), using.data.table(), 
       times = 10) 

Unit: milliseconds 
#  expr    min   lq  median   uq  max neval 
# using.by()   843.46550 854.22116 862.15995 868.75859 912.49406 10 
# using.aggregate() 2416.37227 2451.60134 2482.25319 2498.54546 2501.58574 10 
# using.ddply()  208.03686 209.29981 219.74203 253.46119 258.40935 10 
# using.tapply()  819.30594 820.77757 830.07718 869.50280 987.24822 10 
# using.rowsum()  192.36873 193.48971 194.42591 198.63762 238.91224 10 
# using.data.table() 51.46841 52.37541 52.62934 53.05449 54.06227 10 

Unsurprisingly data.table является явным победителем!

Смежные вопросы