Я пытаюсь вычислить расстояние махаланобиса, не прибегая к циклам и не получая неудачи.
Вот пример набора данных:
df <- data.frame(label = c(rep("A", 5), rep("B", 5)),
date = rep(seq.Date(from = as.Date("2018-01-01"), by = "days", length.out = 5), 2),
valx = c(rnorm(5, mean = 0, sd = 1), rnorm(5, mean = 1.5, sd = 1)),
valy = c(rnorm(5, mean = 100, sd = 10), rnorm(5, mean = 115, sd = 10)),
valz = c(rnorm(5, mean = 0, sd = 10), rnorm(5, mean = 0, sd = 30)))
Я пытаюсь вычислить,по группам (label
) - расстояние махаланобисов valx
, valy
и valz
, но только с использованием строк с этой даты (date
) или предыдущей.Мое текущее решение состоит в том, чтобы циклически проходить через каждый label
, проходить через каждый date
, фильтровать набор данных до соответствующих данных, вычислять расстояние, используя stats::mahalanobis
, добавлять это расстояние в список, а затем do.call
и rbind
они вне петли *.Ясно, что это не идеально.
Я подозреваю, что есть какой-то способ написать:
cum.mdist <- function(df, cols) {...}
df %>%
group_by(label) %>%
arrange(date) %>%
mutate(mdist = xapply(., c(valx, valy, valz), cum.mdist)) %>%
ungroup()
аналогично вычислению скользящей унарной функции, например:
cumsd <- function(x) sapply(seq_along(x), function(k, z) sd(z[1:k]), z = x)
Я мог бы рассчитать расстояние от составных частей, если бы не было ковариации (дисперсию скользящей дисперсии легко вычислить с помощью функции, подобной приведенной выше), но я думаю, что мои переменные do имеют ковариацию, и яя не уверен, как построить скользящую ковариационную матрицу ...
Существует ли решение для этого вне цикла for?
* код для зацикленного решения приведен ниже:
library("tidyverse")
df <- data.frame(label = c(rep("A", 5), rep("B", 5)),
date = rep(seq.Date(from = as.Date("2018-01-01"), by = "days", length.out = 5), 2),
valx = c(rnorm(5, mean = 0, sd = 1), rnorm(5, mean = 1.5, sd = 1)),
valy = c(rnorm(5, mean = 100, sd = 10), rnorm(5, mean = 115, sd = 10)),
valz = c(rnorm(5, mean = 0, sd = 10), rnorm(5, mean = 0, sd = 30)))
mdist.list <- vector(length = nrow(df), mode = "list")
counter <- 1
for(l in seq_along(unique(df$label))){
label_data <- df %>%
filter(label == unique(df$label)[l])
for(d in seq_along(unique(label_data$date))){
label_date_data <- label_data %>%
filter(date <= unique(label_data$date)[d])
if(nrow(label_date_data) > 3){
label_date_data$mdist <- mahalanobis(label_date_data %>% select(contains("val")),
colMeans(label_date_data %>% select(contains("val"))),
cov(label_date_data %>% select(contains("val"))))
} else{
label_date_data$mdist <- NA
}
mdist.list[[counter]] <- filter(label_date_data,
date == unique(label_data$date)[d])
counter <- counter + 1
}
}
mdist.df <- do.call(rbind, mdist.list)