R Функция, определяемая пользователем, работает отдельно, но возвращает неверные значения при использовании с применением.

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

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

library(fields)

Функция по существу измеряет координаты XY (евклидово расстояние с помощью команды rdist()), но сначала она берет подмножество данных, сохраняя только те строки данных «TO», которые попадают в заданное сходство (евклидово расстояние между первым и вторые главные компоненты, PC1 и PC2).

Это делает выборочные данные:

# This data is the reference points to measure FROM
FROM <- data.frame(X=c(-4187500,-4183500,-4155500,-4179500,-2883500),
               Y=c(10092500,10084500,10020500,10012500,9232500),
               PC1=c(-0.525,-0.506,-1.146,-0.733,-1.160),
               PC2=c(3.606,3.609,4.114,3.681,0.882))

# This data is the destination points to measure TO
TO <- data.frame(X=c(-4207500,-4183500,-4203500,-4187500,-2827500,-4203500,-4199500,-4183500,-4195500,-4191500),
             Y=c(10100500,10100500,10096500,10092500,10092500,10088500,10084500,10084500,10072500,10064500),
             PC1=c(-0.371,0.447,-0.344,-0.026,-0.652,-0.460,-0.313,0.010,-0.293,-0.319 ),
             PC2=c(3.149,4.619,3.318,3.885,0.407,3.164,3.300,3.892,3.226,3.337))

# This is the threshold of the data similarity match (distance between PC1 and PC2 in both data sets)
threshold <- 0.5

Вот моя определяемая пользователем функция (с объяснением каждой строки):

dist.func <- function(REF){
  # Calculate the similarity (PC1 and PC2 distance) to all points in the destination
  # Select only those under the threshold
  bt <- as.matrix(TO[(rdist(REF[3:4],TO[3:4])[1,]<threshold)==T,c("X","Y")])
  # Calculate the number of points under the threshold (the "sample size")
  # If there are no points uder the threshold, the SS is set to zero (otherwise 'NA' kills the loop)
  ss <- ifelse(nrow(bt)>=50, 50 ,nrow(bt))
  # If/else to deal with SS=0
  if (nrow(bt)>0) {
    # Calculate the euclidian distance between the reference point and all points under the threshold
    # This calculates the distances, sorts them in ascending order, and trims to the sample size
    dst <- rdist(REF[1:2],bt)[1,][order(rdist(REF[1:2],bt)[1,])][1:ss]
  } else {
  dst <- c(NA)
  }
# Report (in a list or table or whatever) the summary stats for the distances 
list(
  p05=ifelse(nrow(bt)==0, NA, quantile(dst,0.05)),
  MIN=ifelse(nrow(bt)==0, NA, min(dst)),
  AVG=ifelse(nrow(bt)==0, NA, mean(dst)),
  N=ifelse(nrow(bt)==0, 0, nrow(bt)))
}

А вот тест с одной строкой данных FROM (работает) и встроен в команду apply() (не возвращает правильные значения):

# Using the function on a single line of data returns correct values for the given line
dist.func(FROM[1,])

# Embedding the function into apply() returns incorrect outputs
# I'm committed to using apply() here (or some variant) to avoid a for() loop by rows
apply(FROM, 1, dist.func)

Я новичок в пользовательских функциях, поэтому любые предложения будут оценены, если проблема в этом. Кроме того, если есть способ сделать функцию или код в целом более эффективным (пакет, с которым я не знаком), это также было бы очень кстати.


person David Roberts    schedule 08.10.2013    source источник


Ответы (2)


lapply дает правильный вывод

  my.list<-as.list(1:nrow(FROM))

k- lapply(my.list,function(i)dist.func(FROM[i,])
kk<-do.call(rbind,k) # convert to data.frame

sapply(my.list,function(i)dist.func(FROM[i,]))
    [,1]     [,2]     [,3] [,4] [,5]
p05 14939.76 16242.64 NA   NA   NA  
MIN 14422.21 16000    NA   NA   NA  
AVG 19795.44 21179.25 NA   NA   NA  
N   6        6        0    0    0  
person Metrics    schedule 08.10.2013
comment
Спасибо! Есть идеи, почему это нужно делать через lapply() или sapply(), а не через apply() по строкам? Было бы полезно понять это на будущее. - person David Roberts; 09.10.2013
comment
Кроме того, мои реальные данные содержат десятки тысяч строк, поэтому использование sapply() для вывода таблицы нецелесообразно. Есть ли удобный способ получить вывод матрицы или фрейма данных БЕЗ транспонирования вывода sapply или преобразования списка в фрейм данных (поскольку оба процесса приведут к слишком большой таблице для R)? - person David Roberts; 09.10.2013
comment
Без проблем. Хороший вопрос!, но у меня нет ответа прямо сейчас. Может быть, это можно опубликовать как новый вопрос; Мне тоже интересно это узнать. См. обновленный ответ для преобразования в data.frame; вы можете присвоить результат k, чтобы избежать получения больших таблиц в консоли, а затем использовать head(k) или head(kk) - person Metrics; 09.10.2013
comment
На самом деле, эта команда do.call() возвращает именно то, что мне нужно. Еще раз спасибо! - person David Roberts; 09.10.2013
comment
David & Metrics, см. ответ Ferdinand.kraft. Также проверьте эту ссылку для более детальный подход. Я столкнулся с той же проблемой. - person Nicolas De Jay; 29.01.2014

Проблема в том, что apply преобразует FROM в матрицу. Сравнивать:

> dist.func(FROM[1,])
$p05
[1] 14939.76
$MIN
[1] 14422.21
$AVG
[1] 19795.44
$N
[1] 6

> dist.func(as.matrix(FROM)[1,])
$p05
[1] 1400
$MIN
[1] 1e-10
$AVG
[1] 179500
$N
[1] 8

> apply(FROM, 1, dist.func)[[1]]
$p05
[1] 1400
$MIN
[1] 1e-10
$AVG
[1] 179500
$N
[1] 8
person Ferdinand.kraft    schedule 09.10.2013