объединение данных на основе движущегося временного окна в R

#r #date #loops

#r #Дата #циклы

Вопрос:

У меня есть данные о погоде, которые записывались каждый час, и данные о местоположении (X, Y), которые записывались каждые 4 часа. Я хочу знать, какая температура была в местоположении X, Y. Данные о погоде поступают не совсем в одно и то же время. Итак, я написал этот цикл для каждого местоположения, чтобы сканировать данные о погоде в поисках «ближайшего» по дате / ВРЕМЕНИ и извлекать данные за это время. Проблема в том, как я это написал, для местоположения # 2 он просматривает данные о погоде, но не позволяет присваивать информацию о ближайшем времени, которая была назначена для местоположения # 1. Допустим, местоположения № 1 и 2 взяты в течение 10 минут в 18:00 и 18: 10 вечера, ближайшее погодное время — 18:00. Я не могу заставить его разрешить данные о погоде в 18:00 в качестве опции. Я вроде как настроил это так, потому что в моем наборе данных о местоположении 200 местоположений (скажем, через 3 месяца), я не хочу, чтобы это начиналось с момента 0 из данных о погоде, когда я знаю, что самые близкие данные о погоде были только что рассчитаны для последнего местоположения, и в этом наборе данных тоже 3 месяца. Ниже приведены некоторые примеры данных и мой код. Я не знаю, имеет ли это смысл.

 <h6>####Location data</h6>

<p>X   Y   DateTime <br />
1   2   4/2/2003    18:01:01
3   2   4/4/2003    17:01:33
2   3   4/6/2003    16:03:07
5   6   4/8/2003    15:03:08
3   7   4/10/2003   14:03:06
4   5   4/2/2003    13:02:00
4   5   4/4/2003    12:14:43
4   3   4/6/2003    11:00:56
3   5   4/8/2003    10:02:06</p>

<h2>2   4   4/10/2003   9:02:19</h2>

<p>Weather Data
DateTime        WndSp   WndDir  Hgt
4/2/2003 17:41:00   8.17    102.86  3462.43
4/2/2003 20:00:00   6.70    106.00  17661.00
4/2/2003 10:41:00   6.18    106.00  22000.00
4/2/2003 11:41:00   5.78    106.00  22000.00
4/2/2003 12:41:00   5.48    104.00  22000.00
4/4/2003 17:53:00   7.96    104.29  6541.00
4/4/2003 20:53:00   6.60    106.00  22000.00
4/4/2003 19:41:00   7.82    105.00  7555.00
4/4/2003 7:41:00    6.62    105.00  14767.50
4/4/2003 8:41:00    6.70    106.00  17661.00
4/4/2003 9:41:00    6.60    106.00  22000.00
4/5/2003 20:41:00   7.38    106.67  11156.67
4/6/2003 18:07:00   7.82    105.00  7555.00
4/6/2003 21:53:00   6.18    106.00  22000.00
4/6/2003 21:41:00   6.62    105.00  14767.50
4/6/2003 4:41:00    7.96    104.29  6541.00
4/6/2003 5:41:00    7.82    105.00  7555.00
4/6/2003 6:41:00    7.38    106.67  11156.67
4/8/2003 18:53:00   7.38    106.67  11156.67
4/8/2003 22:53:00   5.78    106.00  22000.00
4/8/2003 1:41:00    5.78    106.00  22000.00
4/8/2003 2:41:00    5.48    104.00  22000.00
4/8/2003 3:41:00    8.17    102.86  3462.43
4/10/2003 19:53:00  6.62    105.00  14767.50
4/10/2003 23:53:00  5.48    104.00  22000.00
4/10/2003 22:41:00  6.70    106.00  17661.00
4/10/2003 23:41:00  6.60    106.00  22000.00
4/10/2003 0:41:00   6.18    106.00  22000.00
4/11/2003 17:41:00  8.17    102.86  3462.43</p>

<h2>4/12/2003 18:41:00  7.96    104.29  6541.0</h2>
  

.

 weathrow = 1
for (i in 1:nrow(SortLoc)) {
    t = 0
    while (t < 1) {
        timedif1 = difftime(SortLoc$DateTime[i], SortWeath$DateTime[weathrow], units="auto")
        timedif2 =  difftime(SortLoc$DateTime[i], SortWeath$DateTime[weathrow 1], units="auto") 
        if (timedif2 < 0) {
            if (abs(timedif1) < abs(timedif2)) {
                SortLoc$WndSp[i]=SortWeath$WndSp[weathrow]
                SortLoc$WndDir[i]=SortWeath$WndDir[weathrow]
                SortLoc$Hgt[i]=SortWeath$Hgt[weathrow]
            } else {
                SortLoc$WndSp[i]=SortWeath$WndSp[weathrow 1]
                SortLoc$WndDir[i]=SortWeath$WndDir[weathrow 1]
                SortLoc$Hgt[i]=SortWeath$Hgt[weathrow 1]
            }
            t = 1
        }
        if (abs(SortLoc$DateTime[i] - SortLoc$DateTime[i 1] < 50)) {
            weathrow=weathrow
        } else {
            weathrow = weathrow 1
            #if(weathrow = nrow(SortWeath)){t=1}
        }
    } #end while
}
  

Комментарии:

1. Если это вопрос R, не могли бы вы предоставить некоторые образцы данных в простом в использовании формате R? т.е. dput (object) небольшого объема будет достаточно.

2. Над кодом R находится небольшой набор данных. Я не уверен, что означает dput (object), поскольку я изучаю по ходу работы.

3. Если вы поместите объект (переменную) в dput (object), он выведет что-то, что другие могут использовать для копирования и вставки части ваших данных.

Ответ №1:

Вы могли бы использовать findInterval функцию для поиска ближайшего значения:

 # example data:
x <- rnorm(120000)
y <- rnorm(71000)
y <- sort(y) # second vector must be sorted
id <- findInterval(x, y, all.inside=TRUE) # finds position of last y smaller then x
id_min <- ifelse(abs(x-y[id])<abs(x-y[id 1]), id, id 1) # to find nearest
  

В вашем случае могут потребоваться некоторые as.numeric .

 # assumed that SortWeath is sorted, if not then SortWeath <- SortWeath[order(SortWeath$DateTime),]
x <- as.numeric(SortLoc$DateTime)
y <- as.numeric(SortWeath$DateTime)
id <- findInterval(x, y, all.inside=TRUE)
id_min <- ifelse(abs(x-y[id])<abs(x-y[id 1]), id, id 1)
SortLoc$WndSp  <- SortWeath$WndSp[id_min]
SortLoc$WndDir <- SortWeath$WndDir[id_min]
SortLoc$Hgt    <- SortWeath$Hgt[id_min]
  

Некоторое дополнение: вы никогда не должны, АБСОЛЮТНО НОВЫЕ, добавлять значения в data.frame в for-loop. Проверьте это сравнение:

 N=1000
x <- numeric(N)
X <- data.frame(x=x)
require(rbenchmark)
benchmark(
    vector = {for (i in 1:N) x[i]<-1},
    data.frame = {for (i in 1:N) X$x[i]<-1}
)
#         test replications elapsed relative
# 2 data.frame          100    4.32    22.74
# 1     vector          100    0.19     1.00
  

data.frame версия более чем в 20 раз медленнее, и чем больше строк она содержит, тем разница больше.

Итак, если вы измените свой скрипт и сначала инициализируете результирующие векторы:

 tmp_WndSp <- tmp_WndDir <- tmp_Hg <- rep(NA, nrow(SortLoc))
  

затем обновите значения в цикле

 tmp_WndSp[i] <- SortWeath$WndSp[weathrow 1]
# and so on...
  

и в конце (вне цикла) обновите соответствующие столбцы:

 SortLoc$WndSp <- tmp_WndSp
SortLoc$WndDir <- tmp_WndDir
SortLoc$Hgt <- tmp_Hgt
  

Это должно выполняться намного быстрее.

Комментарии:

1. большое вам спасибо! это отлично работает. и нет необходимости в цикле! теперь, чтобы протестировать это полностью!

Ответ №2:

Вот пример одной стратегии, которую вы могли бы использовать. При этом время погоды просматривается одно за другим, затем принимается абсолютное значение разницы между этим временем и временем каждого местоположения, затем фиксируется наименьшая разница во времени. Это решает вашу проблему «смотреть вперед / оглядываться назад». Ваш набор данных кажется достаточно маленьким, чтобы переход к полувекторизованному решению обеспечил вам необходимый прирост скорости, но если нет, то его должно быть относительно просто добавить в движущееся окно, которое передает только некоторые / — N наблюдений вокруг последнего loc.match.index.

 w <- as.POSIXct(strptime( c("4/2/2003 17:41:00","4/2/2003 20:00:00","4/2/2003 10:41:00","4/2/2003 11:41:00","4/2/2003 12:41:00"),format="%m/%d/%Y %H:%M:%S"))
l <- as.POSIXct(strptime( c("4/2/2003 18:01:01","4/2/2003 17:01:33","4/2/2003 16:03:07","4/2/2003 15:03:08","4/2/2003 14:03:06","4/2/2003 13:02:00"),format="%m/%d/%Y %H:%M:%S"))

window.size <- 5

findClosest <- function(w.i,l) {
    which.min(abs(w.i-l))
}
makeWindow <- function(loc.match.index,i,window.size,n) {
    win.max <- loc.match.index[i-1]   window.size
    if(win.max > n) {
        win.max <- n
    }
    win.min <- loc.match.index[i-1] - window.size
    if(win.min < 1) {
        win.min <- 1
    }
    return(seq(win.min,win.max))
}

loc.match.index <- integer()
n <- length(w)
# Initialize on whole vector
i <- 1
loc.match.index[i] <- findClosest(w[i],l)
# Continue on window
for(i in seq(2,n)) {
    wndw <- makeWindow(loc.match.index,i,window.size,n)
    loc.match.index[i] <- findClosest(w[i],l[wndw]) 
    # Add the start of the window back to the index that was returned
    loc.match.index[i] <- loc.match.index[i]   min(wndw)-1
}
> loc.match.index
[1] 1 1 5 5 5
  

Здесь все еще есть области, которые можно было бы сделать более эффективными, но это должно быть достаточно быстрым, учитывая, что сравнения векторизованы и окно можно настроить.

Комментарии:

1. Итак, мой набор данных состоит из 120 000 местоположений, и моя погода была собрана за 5 лет, поэтому 71 000 данных о погоде. Ранее я написал скрипт для выполнения того, что, по-моему, вы описали, но для обработки потребовалось 8 дней и подключение 1 компьютера для этого. Для пункта 1 я вычислил разницу в дате и времени для всех погодных данных (71 000 различий), а затем нашел минимальное значение. Теперь проделайте это 120 000 раз. Таким образом, идея окна. Мне не нужно выполнять обратное вычисление, и мне не нужно выполнять поиск до конца. Итак, ваш совет по поводу окна … не могли бы вы объяснить это немного подробнее?

2. Я попытался запустить предоставленный вами небольшой скрипт с дополнительными данными о погоде и выдал ошибку, в которой указывалось, что data.frames должны быть одинакового размера. В моей текущей попытке было больше погодных точек, чем местоположений, но в окончательной версии будет больше точек местоположения, чем погоды. В любом случае…

3. @Kerry: Добавлено окно для вас.

4. @Kerry: Я подтвердил, что это работает с разными длинами векторов на моем компьютере. Работает ли это, когда вы копируете и вставляете их? Я подозреваю, что при переходе от моего упрощенного решения к вашему решению на основе data.frame вы допустили ошибку. Возможно, опубликовать свой код? Я бы поработал с data.frames, если бы вы предоставили dput() выходные данные … для дальнейшего использования, dput действительно упрощает работу для всех.