2015-08-06 3 views
1

У меня есть многоколоночный объект xts, который имеет вторую точность. Затем у меня есть другой объект xts, который содержит одно значение для каждого дня. Я бы хотел добавить это ежедневное значение как столбец в основном объекте xts. Вот пример:Как объединить ежедневные xts в редкие индексированные по времени xts?

Sys.setenv(TZ = "UTC") 
library(xts) 

set.seed(777) 

xt = xts(data.frame(A=1:20,B=201:220,C=round(runif(20)*10,1)), 
    order.by = as.POSIXct("2015-06-21") + (runif(20) * 86400 * 14)) 

xd = xts(round(runif(14) - 0.5,1), as.Date("2015-06-21") + (1:14)) 

Использование merge не работает: в xd записи получить дают «00:00:00» метки времени, и поэтому ни один из них не совпадают, так что я получаю объект XTS с большим количеством ВПЛ :

     A B C xd 
2015-06-21 10:04:36 5 205 7.0 NA 
2015-06-22 00:00:00 NA NA NA -0.5 
2015-06-23 00:00:00 NA NA NA -0.2 
2015-06-23 11:42:38 4 204 10.0 NA 
2015-06-24 00:00:00 NA NA NA 0.1 
... 

Ожидаемый результат:

     A B C xd 
2015-06-21 10:04:36 5 205 7.0 NA 
2015-06-23 11:42:38 4 204 10.0 -0.2 
2015-06-24 21:16:18 18 218 8.7 0.1 
2015-06-25 02:30:24 15 215 8.7 -0.2 
2015-06-25 07:48:42 16 216 1.0 -0.2 
2015-06-25 15:04:34 14 214 5.9 -0.2 
2015-06-26 07:50:09 1 201 6.9 -0.3 
2015-06-27 19:28:33 7 207 3.5 0.5 
... 

Очки:

  • Реальные данные будут намного больше, чем в этом примере, поэтому следует избегать чрезмерного использования памяти и процессора.
  • Как показано выше, НС или отсутствующие даты возможны в xd (хотя и относительно редко).
  • Есть несколько дней, не представленных в xt (как показано отсутствующим выше, чем 2015-06-22). Мне не нужна запись, созданная для таких дней. (Я предполагаю, что я мог бы использовать na.omit, чтобы удалить их, но это возможно - и не показано в моем простом примере - что у меня есть некоторые реальные Nas в данных, которые я не хочу удалить.)

ОБНОВЛЕНИЕ: В качестве примера NA s в исходных данных заблудиться, рассмотрите xt[10,'B'] <- NA. Используя решение Joshua merge(xt, xd, fill=na.locf)[index(xt)], 2015-06-28 19:41:45 заканчивается как 8 203 1.7 0.4, когда оно должно быть 8 NA 1.7 0.4. Будет ли это проблемой или нет, будет зависеть от того, что будет xt. Ответ FXQuantTrader показывает обходное решение для сохранения NA с использованием магического числа, которое в конце возвращается в NA. Одна альтернатива (которая использует больше памяти) заключается в том, чтобы взять копию любых столбцов, содержащих НС, и затем заменить весь столбец.

+0

Кстати, мой «избежать чрезмерной памяти» точка была по отношению к идее я (кратко) пришлось использовать '' na.locf' и seq' создать одну запись для каждой второй в 'xd', так что слияние будет работать (например, http://stackoverflow.com/a/8981517/841830). Это потребует 86 400 столько строк (и становится еще глуже, если 'xt' использует миллисекунды)! –

ответ

2

Чтобы получить то, что вы хотите, вы хотите точно объединить временные метки xd на метки времени xd (до уровня подсекции). Таким образом, один из подходов состоит в том, чтобы найти первый штамп времени в xt в каждый календарный день в вашем часовом поясе и использовать эту метку времени как значение индекса для дня в xd.

В предположении, что вы знаете значение xd на начальном этапе торгового дня (в 00:00:00), вы можете сделать что-то вроде этого (нижеприведенный код требует незначительной настройки, если вы знаете только значение из XD в конце торгового дня):

Sys.setenv(TZ = "UTC") 
library(xts) 

set.seed(777) 

library(lubridate) 
xt = xts(data.frame(A=1:20,B=201:220,C=round(runif(20)*10,1)), 
      order.by = as.POSIXct("2015-06-21") + (runif(20) * 86400 * 14)) 

# Use consistent time index ordering (both POSIXct): 
xd = xts(round(runif(14) - 0.5,1), as.POSIXct("2015-06-21") + days(1:14)) 

# since xd elements are randomly created each time: 
xd2 <- xd 

# get first timestamp of each day in xt: 
first_each_day <- .indexday(xt) 
first_each_day_ndup <- !duplicated(first_each_day) 
first_each_day_ndup.i <- which(first_each_day_ndup) # this row is the first for each day 

xt_sub <- xt[first_each_day_ndup.i] 

xt_sub_floor_dates <- floor_date(index(xt_sub), "day") 
xd_date_eq_xt_date.i <- which(index(xd2) %in% xt_sub_floor_dates) 
switch2.i <- which(xt_sub_floor_dates %in% index(xd2)) 


# Set xd time to the first timestamp in xt for the day, if it exists in xt: 
xdtmp <- xd2[xd_date_eq_xt_date.i,] 
index(xdtmp) <- index(xt_sub[switch2.i,]) 

# xts merge trick -- name new column at the same time as merging all in one statement: 
res <- merge(xt, dailyvalue = drop(xdtmp)) 
res[, "dailyvalue"] <- na.locf(res[, "dailyvalue"]) 

что касается ваших пунктов:

Если нет строки в ХТ, что соответствует дате в XD, строка не добавляется в ХТ с этим подход (т. е. ваша точка 3 адресована).

Вы не упомянули, как вы хотели бы, чтобы справиться с Nas, которые могут пре-существующие в XD до того слияния XD и ХТ, но один способ быть в курсе ВПЛ в ХТ после слияния будет устанавливать значения в XD, который являются NA для общего смысла неиспользуемого числового значения, такого как -Inf, поэтому все еще можно использовать na.LOCF заполнить dailyvalue колонки соответственно в хх

# suppose NA exists in xd at row 7: 

xd2[7,] <- NA 

# Set a replacement dummy numeric value for recognising NAs in the dailyvalue column. e.g. Inf 

xd2[is.na(xd2),] <- -Inf 

# Now repeat the above code for merging: 
xdtmp <- xd2[xd_date_eq_xt_date.i,] 
index(xdtmp) <- index(xt_sub[switch2.i,]) 

# merge and name new column at the same time all in one statement: 
res <- merge(xt, dailyvalue = drop(xdtmp)) 
res[, "dailyvalue"] <- na.locf(res[, "dailyvalue"]) 

# backfill NAs in dailyvale if necessary: 
res[!is.finite(res[, "dailyvalue"]), "dailyvalue"] <- NA 
print(res) 

Наконец, если вам не хватает даты в XD, но знает, как вы хотели бы, чтобы заполнить их обратно значение, вы можете просто добавить эти неизвестные даты, чтобы Xd, прежде чем делать приведенный выше код для слияния.

+1

Спасибо за усилия в этом ответе! Но неужели это не должно быть так сложно? Обработка индексов xts настолько сложна, что ей есть что помочь. Кстати, использование -9999 для временного значения NA является довольно рискованным - вы никогда не знаете, когда код будет использоваться для данных с другим диапазоном, до того, что вы видели до сих пор. (Я хочу сказать, что '-Inf' или' NaN' более безопасны, чем -9999, но они так же уязвимы, я полагаю.) –

+1

Что касается замены временного НС, вы правы, Inf - более чистый выбор, и работает с na.locf. Хотя NaN, похоже, не работает с na.locf. – FXQuantTrader

3

«канонической», как я бы сделать что-то вроде этого:

  1. Слияние двух объектов.
  2. Звоните na.locf на результат.
  3. Подмножество, которое приводит к тому, что оно содержит только нужные значения индекса.

Вы можете сделать na.locf вызов через fill аргумент merge.xts функции. Например:

xtd <- merge(xt, xd, fill=na.locf)[index(xt)] 

Это самое легкое решение, о котором я могу думать, что может быть не самым результативным. Дайте мне знать, подходит ли это для вашего случая использования. Если нет, мне придется потратить некоторое время на размышления о более эффективном решении.


Мы можем использовать ту же парадигму, если xt имеет NA, что нужно сохранить, но нам нужно запустить na.locf на колоннах только xd.

xtd <- merge(xt, xd) 
xtd[,"xd"] <- na.locf(xtd[,"xd"]) 
xtd <- xtd[index(xt)] 
+0

Спасибо - это очень просто. Хотя это происходит неправильно, когда 'xt' уже содержит значения NA, которые должны быть сохранены (я только что редактировал свой вопрос, чтобы показать пример). –

+2

@ DarrenCook: Это легко разрешить. Просто сделайте 'na.locf' за пределами вызова' merge'. Я отредактирую свой ответ. –

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