из цикла для применения - PullRequest
       298

из цикла для применения

1 голос
/ 28 марта 2011

Я новичок в использовании R. Поэтому я не уверен насчет того, как использовать apply.Я хотел бы ускорить мою функцию с помощью apply:

for(i in 1: ncol(exp)){
 for (j in 1: length(fe)){
  tmp =TRUE
  id = strsplit(colnames(exp)[i],"\\.")
  if(id == fe[j]){
   tmp = FALSE
  }
  if(tmp ==TRUE){
   only = cbind(only,c(names(exp)[i],exp[,i]) )
  }
 }
}

Как я могу использовать функцию apply, чтобы сделать это выше?

EDIT:

Спасибобольшое за очень хорошее объяснение и извините за мое плохое описание.Догадаешься все правильно, но когда хотел удалить спички в фе.

Exp <- data.frame(A.x=1:10,B.y=10:1,C.z=11:20,A.z=20:11)

fe<-LETTERS[1:2]

тогда результатом должны быть только имена столбцов с 'C'.Все остальное должно быть удалено.

1   C.z 
2    11 
3    12   
4    13   
5    14 
6    15  
7    16  
8    17  
9    18   
10   19  
11   20   

Ответы [ 2 ]

4 голосов
/ 28 марта 2011

РЕДАКТИРОВАТЬ: Если вы хотите удалить только столбцы, имена которых появляются в fe, вы можете просто сделать:

Exp <- data.frame(A.x=1:10,B.y=10:1,C.z=11:20,A.z=20:11)
fe<-LETTERS[1:2]

id <- sapply(strsplit(names(Exp),"\\."),
    function(i)!i[1] %in% fe)
Exp[id]

Этот код делает то же самое, что и ваш (обновленный) цикл for, тольконамного эффективнееВам не нужно перебирать fe, функция %in% векторизована.

Если имя может появиться где-то между точками, тогда

id <- sapply(strsplit(names(Exp),"\\."),
    function(i)sum(i %in% fe)==0)

Ваш кодделает некоторые очень забавные вещи, и я понятия не имею, что именно вы пытаетесь сделать.Например, strsplit дает список, поэтому id == fe[j] всегда будет возвращать false, если только fe[j] не является самим списком.И я сомневаюсь, что это ... Так что я бы исправил ваш код как

id = strsplit(colnames(Exp)[i],"\\.")[[1]][1]

, если вы хотите сравнить со всем, что находится перед точкой, или с

id = unlist(strsplit(colnames(Exp)[i],"\\.")) 

если вы хотите сравнить со всем в строке.В этом случае вы должны также использовать %in% вместо ==.

Во-вторых, вы получите матрицу символов, которая по существу умножает строки.если все элементы в fe [j] уникальны, вы также можете сделать:

only <- rbind(names(exp),exp)
only <- do.call(cbind,lapply(mat,function(x) 
       matrix(rep(x,ncol(exp)-1),nrow=nrow(exp)+1)
))

Предполагая, что логика в вашем коде имеет смысл (поскольку вы не применили некоторые примеры данных, это невозможнознаете), выполняется оптимизация:

mat <- rbind(names(Exp),Exp)

do.call(cbind,
    lapply(mat, function(x){
        n <- sum(!fe %in% strsplit(x[1],"\\.")[[1]][1])
        matrix(rep(x,n),nrow=nrow(mat))
}))

Обратите внимание, что - если вам интересно, если fe [j] появляется где-нибудь в имени - вы можете изменить код на:

do.call(cbind,
    lapply(mat, function(x){
        n <- sum(!fe %in% unlist(strsplit(x[1],"\\.")))
        matrix(rep(x,n),nrow=nrow(mat))
}))

Если это не возвращает того, что вы хотите, то ваш код тоже не делает этого.Я проверил следующие примеры данных, и все дает тот же результат:

Exp <- data.frame(A.x=1:10,B.y=10:1,C.z=11:20,A.z=20:11)
fe <- LETTERS[1:4]
2 голосов
/ 28 марта 2011

Семейство функций apply() - это вспомогательные функции.Они не обязательно будут быстрее, чем хорошо написанные для цикла или векторизованных функций.Например:

set.seed(21)
x <- matrix(rnorm(1e6),5e5,2)

system.time({
  yLoop <- x[,1]*0  # preallocate result
  for(i in 1:NROW(yLoop)) yLoop[i] <- mean(x[i,])
})
#    user  system elapsed 
#   13.39    0.00   13.39 
system.time(yApply <- apply(x, 1, mean))
#    user  system elapsed 
#   16.19    0.28   16.51
system.time(yRowMean <- rowMeans(x))
#    user  system elapsed 
#    0.02    0.00    0.02
identical(yLoop,yApply,yRowMean)
# TRUE

Причина, по которой ваш код такой медленный, заключается в том, что, как указал Гэвин, вы увеличиваете свой массив для каждой итерации цикла.Предварительно выделите весь массив перед циклом, и вы увидите значительное ускорение.

...