Dividing a list of matrices by different sets of matrices

Viewed 127

I have a list of matrices, and I have a few different matrix that I would like to divide the elements in the list of matrices by. I would like to go down the list by sets of threes and calculate the difference between the minimum jDate for in int1 and int2, as shown below for n1, n2,and n3. I would then like to divide the matrix created from these values m1 from one of the matrix in the list. I would then move down to the next set of three elements in the list for int1 and int2 and calculate the differences between those, and repeat the same process as the first set of three. How would I go about doing this?

library(lubridate)
library(tidyverse)
date <- rep_len(seq(dmy("26-12-2010"), dmy("20-12-2013"), by = "days"), 500)
ID <- rep(seq(1, 5), 100)
df <- data.frame(date = date,
                 x = runif(length(date), min = 60000, max = 80000),
                 y = runif(length(date), min = 800000, max = 900000),
                 ID)

df$jDate <- julian(as.Date(df$date), origin = as.Date('1970-01-01'))
df$Month <- month(df$date)

t1 <- c(100,150,200)
t2 <- c(200,250,350)
t3 <- c(300,350, 400)
mat <- cbind(t1,t2, t3)

t1 <- c(150,150,200)
t2 <- c(250,250,350)
t3 <- c(350,350, 400)
mat2 <- cbind(t1,t2, t3)

l1 <- list(mat, mat2)

int1 <- df %>%
  # arrange(ID) %>%   # skipped for readability of result
  mutate(new = floor_date(date, '10 day')) %>%
  mutate(new = if_else(day(new) == 31, new - days(10), new)) %>% 
  group_by(ID, new) %>%
  filter(Month == "3") %>% 
  group_split()

int2 <- df %>%
  # arrange(ID) %>%   # skipped for readability of result
  mutate(new = floor_date(date, '10 day')) %>%
  mutate(new = if_else(day(new) == 31, new - days(10), new)) %>% 
  group_by(ID, new) %>%
  filter(Month == "2") %>% 
  group_split()

n1 <- c(((min(int1[[1]]$jDate))- min(int2[[1]]$jDate)),
        ((min(int1[[1]]$jDate))- min(int2[[2]]$jDate)),
        ((min(int1[[1]]$jDate))- min(int2[[3]]$jDate)))

n2 <- c(((min(int1[[2]]$jDate))- min(int2[[1]]$jDate)),
        ((min(int1[[2]]$jDate))- min(int2[[2]]$jDate)),
        ((min(int1[[2]]$jDate))- min(int2[[3]]$jDate)))

n3 <-  c(((min(int1[[3]]$jDate))- min(int2[[1]]$jDate)),
         ((min(int1[[3]]$jDate))- min(int2[[2]]$jDate)),
         ((min(int1[[3]]$jDate))- min(int2[[3]]$jDate)))
m1 <- cbind(n1,n2,n3)


l1[[1]]/m1

1 Answers

We can splitthe list by a grouping index, create a function ('f1') based on the OP's creation of matrix from those 3 element lists, use map2 from purrr to loop over the split lists,

library(purrr)
g1 <- as.integer(gl(length(int1), 3, length(int1)))
f1 <- function(.int1 ,.int2) {

   n1 <- c(((min(.int1[[1]]$jDate))- min(.int2[[1]]$jDate)),
        ((min(.int1[[1]]$jDate))- min(.int2[[2]]$jDate)),
        ((min(.int1[[1]]$jDate))- min(.int2[[3]]$jDate)))

n2 <- c(((min(.int1[[2]]$jDate))- min(.int2[[1]]$jDate)),
        ((min(.int1[[2]]$jDate))- min(.int2[[2]]$jDate)),
        ((min(.int1[[2]]$jDate))- min(.int2[[3]]$jDate)))

n3 <-  c(((min(.int1[[3]]$jDate))- min(.int2[[1]]$jDate)),
         ((min(.int1[[3]]$jDate))- min(.int2[[2]]$jDate)),
         ((min(.int1[[3]]$jDate))- min(.int2[[3]]$jDate)))
out <- cbind(n1,n2,n3)
out}

apply the function, then divide the 'l1' list elements with the corresponding elements of 'lstMat' (Here the reproducible example showed two list elements in 'l1', so we subset the 'lstMat' ([1:2]) to do the division

lstMat <- map2(split(int1, g1), split(int2, g1), ~  f1(.x, .y))
map2(l1, lstMat[1:2], `/`)
[[1]]
     t1        t2        t3
[1,]  4  5.714286  6.666667
[2,] 10 10.000000 10.000000
[3,] 40 23.333333 16.000000

[[2]]
       t1        t2       t3
[1,]  5.0  6.250000  7.00000
[2,]  7.5  8.333333  8.75000
[3,] 20.0 17.500000 13.33333

-checking with OP's output for first the first case

l1[[1]]/m1
     t1        t2        t3
[1,]  4  5.714286  6.666667
[2,] 10 10.000000 10.000000
[3,] 40 23.333333 16.000000

The OP's function wrapped 'f1' can be modified by simplifying with outer

f2 <- function(.int1, .int2) {
   t(outer(seq_along(.int1), seq_along(.int2), 
      FUN = Vectorize(function(i, j)  min(.int1[[i]]$jDate) - 
           min(.int2[[j]]$jDate))))
}

Now, try the same code to create 'lstMat'

lstMat <- map2(split(int1, g1), split(int2, g1), f2)
 map2(l1, lstMat[1:2], `/`)
[[1]]
     t1        t2        t3
[1,]  4  5.714286  6.666667
[2,] 10 10.000000 10.000000
[3,] 40 23.333333 16.000000

[[2]]
       t1        t2       t3
[1,]  5.0  6.250000  7.00000
[2,]  7.5  8.333333  8.75000
[3,] 20.0 17.500000 13.33333
Related