rterr

Is there a way to repeatedly solve matrices created from a data frame without using for loops?


Update

I've been trying the apply() functions but the output is stumping me. I need a data.frame or data.table. apply(smallExample, 1, SectionDev) gives the right output but in a list. I've read sapply() can output a data.frame or matrix but I can't get the entire row to feed into the function.

New Function

  SectionDev <- function(x){
    parameters <- data.table("Name" = c("Head", "Foot", "Pillow", "Mattress", "Sleeping", "Restless"),
                             "Space" = c(8, 78, 17, 17, 3, 8), 
                             "Portion" = c(2, 4, 2, 2, 3, 3))

    RG <- as.numeric(x[which(names(x) == "Gas")])
    RS <- as.numeric(x[which(names(x) == "Solid")])

    A <- data.frame(MG=c(1, 0, 1, 0),
                    MS=c(1, 0, 0, 1),
                    VG=c(0, 1, -1*RG, 0),
                    VS=c(0, 1, 0, -1*RS))

    for (j in 1:nrow(parameters)) {
      SectName <- as.vector(as.matrix(parameters[j, `Name`]))
      Ac <- parameters[j, `Portion`]
      Vt <- parameters[j, `Space`]

      Pushing <- as.numeric(x[which(names(x) == SectName)])
      Driving <- as.numeric(Pushing * 249.1)
      Pressing <- Driving*Ac / 9.8
      b <- c(Pressing, Vt, 0, 0)

      MatrixConstants <- data.table("Timestamp" = as.vector(as.matrix(x[which(names(x) == "Timestamp")])),
                                    "Section" = SectName,
                                    RG,
                                    RS) 

      MatrixSolution <- solve(A,b)

      MatrixPtResult <- cbind(MatrixConstants,t(
        as.data.frame(MatrixSolution)))

      if (exists("MatrixTest")){
        rlist <- list(MatrixTest, MatrixPtResult)
      } else {
        rlist <- list(MatrixPtResult)
      }

      MatrixTest <- rbindlist(rlist)
      }

    return(MatrixTest)
  }

Desired Output

> MatrixOutput[1:12,]
              Timestamp  Portion Gas Solid        MG         MS         VG
 1: 2019-04-24 06:00:00     Head  18   234  149.2218  -67.88299   8.290098
 2: 2019-04-24 06:00:00     Foot  18   234 1427.7993 -309.39116  79.322184
 3: 2019-04-24 06:00:00   Pillow  18   234  318.7908 -166.28061  17.710601
 4: 2019-04-24 06:00:00 Mattress  18   234  183.2262 1596.05952  10.179233
 5: 2019-04-24 06:00:00 Sleeping  18   234 -316.4209 4815.47194 -17.578940
 6: 2019-04-24 06:00:00 Restless  18   234 -549.3597 9013.67602 -30.519983
 7: 2019-04-24 07:00:00     Head  17   239  141.7429  -80.73885   8.337819
 8: 2019-04-24 07:00:00     Foot  17   239 1365.2541 -551.86634  80.309064
 9: 2019-04-24 07:00:00   Pillow  17   239  303.3448 -201.67136  17.843813
10: 2019-04-24 07:00:00 Mattress  17   239  178.7719 1549.67705  10.515996
11: 2019-04-24 07:00:00 Sleeping  17   239 -277.9378 4624.47863 -16.349283
12: 2019-04-24 07:00:00 Restless  17   239 -501.7540 8966.07028 -29.514938
            VS
 1: -0.2900983
 2: -1.3221844
 3: -0.7106009
 4:  6.8207672
 5: 20.5789399
 6: 38.5199830
 7: -0.3378195
 8: -2.3090642
 9: -0.8438132
10:  6.4840044
11: 19.3492830
12: 37.5149384

Original

I have to solve the same matrix for each time point and location in my data. The for loop works but isn't fast at 12,000+ time points and 6 locations.

Is there a way to do this without using for? It just doesn't feel like R the way I'm doing it currently.

I did try using foreach with doParallel but it was actually slower than the for as written in TERR.

There are two nested loops. The first pulls the measurements for the time point and the second executes solve for each location.

  for (i in 1:nrow(smallExample)) {
      RG <- as.matrix(smallExample[i, "Gas"])
      RS <- as.matrix(smallExample[i, "Solid"])

      A <- data.frame(MG=c(1, 0, 1, 0),
                      MS=c(1, 0, 0, 1),
                      VG=c(0, 1, -1*RG,0),
                      VS=c(0, 1, 0, -1*RS))

      for (j in 1:nrow(parameters)) {
        SectName <- as.vector(as.matrix(parameters[j, "Name"]))
        Ac <- parameters[j, "Portion"]
        Vt <- parameters[j, "Space"]

        Pushing <- smallExample[i, get(SectName)]
        Driving <- as.matrix(Pushing*249.1) 
        Pressing <- Driving * as.matrix(Ac) / 9.8
        b <- c(Pressing,Vt,0,0)

        MatrixConstants <- data.table("Timestamp" = as.vector(as.matrix(smallExample[i, "Timestamp"])),
                                      "Portion" = SectName,
                                      RG,
                                      RS)

        MatrixSolution <- solve(A,b)

        MatrixPtResult <- cbind(MatrixConstants,t(
          as.data.frame(MatrixSolution)))

        if (exists("MatrixOutput")){
          rlist <- list(MatrixOutput, MatrixPtResult)
        } else {
          rlist <- list(MatrixPtResult)
        }

        MatrixOutput <- rbindlist(rlist)
        cat(paste("\r",i, "Observations of ", nrow(smallExample),"\t",nrow(MatrixOutput),"Matrix results",sep = " "))
      }
  }

The result is an unpivoted table of times, locations, measurements, and the four parts of the solutions.

Data:

library(data.table)

smallExample <- structure(list(Gas = c(18,  17, 15,  14,  12,  11), 
                                 Solid = c(234, 239,  237, 238,  233, 239),
                                 Timestamp = structure(c(1556085600,
                                                         1556089200, 
                                                         1556092800, 
                                                         1556096400, 
                                                         1556100000, 
                                                         1556103600),
                                                       class = c("POSIXct", "POSIXt"), tzone = "UTC"), 
                                 Head = c(1.6, 1.2, 1.1, 1.0, 0.8, 0.7), 
                                 Foot = c(11, 8, 7, 6, 5, 4), 
                                 Pillow = c(3, 2, 2, 2, 1, 1), 
                                 Mattress = c(35, 34, 31, 30, 28, 26), 
                                 Sleeping = c(59, 57, 59, 58, 59, 59), 
                                 Restless = c(111, 111, 110, 111, 112, 113)),
                            class = c("data.table", "data.frame"),
                            row.names = c(NA_integer_, -6L))

parameters <- data.table("Name" = c("Head", "Foot", "Pillow", "Mattress", "Sleeping", "Restless"),
                         "Space" = c(8, 78, 17, 17, 3, 8), 
                         "Portion" = c(2, 4, 2, 2, 3, 3))

Solution

  • The solution was to use apply and do.call with rbind (thanks to fotNelton). Benchmarking with microbenchmark using apply came in 60% faster. Just left wishing I could get do.call(rbindlist,)) to work.

    MatrixAnswer <- as.data.table(do.call(rbind, apply(MatrixSubTest, 1, SectionMasses)))