У меня есть переменная, зависящая от времени, представленная в виде двух векторов: вектор времени (отсортированный) и вектор значений в это время. Я хочу передискретизировать эту переменную в разное время, указанное другим отсортированным вектором времени.
На другом языке я бы сделал одновременный обход двух отсортированных векторов времени. т.е. линейный поиск от начала старого вектора времени до тех пор, пока я не найду время, ближайшее к первому элементу в новом векторе времени, а затем продолжаю с этой точки в старом векторе, чтобы найти время, ближайшее ко второму элементу в новом векторе. и т. д. Это дает решение, равное O(n).
Ключевым моментом здесь является то, что два вектора времени имеют разную длину, а элементы не связаны друг с другом, поэтому что-то вроде map2 или walk2 — это не то, что мне нужно.
Я могу реализовать одновременную прогулку с помощью цикла for (см. код ниже), и это работает, но медленно. У меня также есть другое решение, которое больше похоже на R codey, но оно O(n^2), так что оно также оказывается медленным. Есть ли способ R сделать это, используя внутренние реализации R, чтобы получить решение O (n)?
В качестве альтернативы, есть ли функция R, которая могла бы заменить мой get_closest() бинарным поиском, чтобы, по крайней мере, это было O (nlogn)?
Из моих поисков я подозреваю, что ответ будет таким: «напишите функцию C, которую вы вызываете из R», но я довольно новичок в R, поэтому я хотел проверить, что я ничего не упустил.
Обновлено:
Я должен был четко указать, что значения в new_times могут не существовать в old_times. Я хочу найти индекс в old_times, где время ближе всего к каждой записи в new_times. В моем реальном приложении я буду делать линейную интерполяцию, но этот вопрос касается только поиска ближайшего соседа.
library(tidyverse)
# input values given
old_times <- c(2, 4, 6, 8, 10, 12, 14, 16, 18, 20)
old_values <- c(3, 7, 6, 7, 8, 9, 7, 6, 4, 6)
new_times <- c(4.1, 9.6, 12.3, 17.8)
Желаемый результат
new_values <- c(7, 8, 9, 4)
Моя попытка
new_values <- rep(NA, length(new_times))
old_index <- 1
for (new_index in 1:length(new_times)) {
while (old_index < length(old_times) &&
old_times[old_index] < new_times[new_index]) {
old_index <- old_index + 1
}
# I could now do interpolation if the value of new_times is in between
# two values in old_times. The key is I have a correspondence that
# new_times[new_index] is close in time to old_times[old_index].
new_values[new_index] <- old_values[old_index]
}
# Here's an alternative way to do it that uses more R internals,
# but winds up being O(n^2).
# Get the index in old_times closest to new_time.
# This is O(n).
get_closest <- function(new_time, old_times) {
return(which.min(abs(new_time - old_times)))
}
# Call get_closest on each element of new_times.
# This is O(n^2).
new_indices <- unlist(map(new_times, get_closest, old_times))
# Slice the list of old values to get new values.
new_values2 <- old_values[new_indices]
Мы можем использовать match
old_values[match(new_times, old_times)]
# [1] 7 8 9 4
match(new_times, old_times)
возвращает «вектор позиций (первых) совпадений его первого аргумента во втором»., т.е.
# [1] 2 5 6 9
Мы можем использовать этот результат для извлечения желаемых значений из old_values
с помощью [
.
Мы также можем использовать %in%
, который возвращает логический вектор.
old_values[old_times %in% new_times]
Благодаря @Андрей
В качестве «альтернативы» old_values[old_times %in% new_times]
делает то же самое под капотом. То есть см. ?match
. Я обнаружил, что %in%
обычно лучше масштабирует маленький (во многих случаях незаметно).
@BobSteinke Лучше всего создать минимальный пример и показать ожидаемый результат. Возможно, вы захотите задать новый вопрос.
Похоже, что лучше всего для этого использовать data.table
. Я узнал об этом в этом другом вопросе:
Найти ближайшее значение в векторе с помощью бинарного поиска
Возможна оптимизация для data.table, если он знает, что векторы поиска и поиска отсортированы, он может выполнять поиск O (n) вместо O (nlogn), но data.table уже очень быстро в мое приложение.
Хорошо, это выглядит многообещающе. Что, если значения в new_times не в old_times, а я просто хочу получить индекс ближайшей записи? Я упустил это из виду в своем примере, чтобы код не стал слишком длинным, но в моем реальном приложении он может запросить значение в момент времени 2,5, и мне придется интерполировать.