1

there seem to many answered questions about how to convert a list into a double, but nothing that seems to keep the structure of the list valid.

I have a nested list like that (R output):

my_list

[[1]] 
[1]  1  3  4  8 11 13 17

[[2]] 
 [1]  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20

[[3]]  
[1]  2 14

Then I pass this list to JAGS in order to iterate over its elements. In the JAGS loop I want to iterate over the first, second and third element and then retrieve the numbers stored in there to be iterated over again in another loop. That means I want to access "1 3 4 8 11 13 17" of the first element and so on.


The code in the JAGS script that I use to iterate is like this:

    for (j in 1:subjects) {  # Subject-loop
          for (i in my_list[j]) {  # Trial loop
   .... 

     }
    }

The background for this is that not all subjects have valid trials. so I need to pass JAGS the valid trials for each subject.

I intended to do that with a nested list like above. However, it turns out JAGS cannot deal with list - so now I try to convert it into something that JAGS can iterate over without losing the information which trials belong to which subject.

I need to keep the structure functioning so everything that just converts the list into a single vector does not help as I cannot iterate over that anymore.

Any suggestions? I have tried to just pass the list, but JAGS cannot deal with that and needs a "double". I also tried to convert it into a matrix, but failed to keep structure.

Thank you!


Update: I tried this, which works somewhat as matrixes seem to work with JAGS

list_as_matrix <- do.call(rbind, my_list)

But the nested lists have different lengths, so the empty columns in the matrix are just filled with the same values over an over again.


The JAGS error code is:

4 nodes produced errors; first error: 'list' object cannot be coerced to type "double"  

Update with the answer thanks to the input from DaveArmstrong:

# a list of 6 subjects and their valid trials
subjects_and_their_valid_trials <- list(
  c(1,3,4,8, 11,13,17), 
  c(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20), 
  c(2, 14),
  c(1,3,4,8, 11,13,17), 
  c(1,2,3,4,5,6,8,9,10,11,12,13,14,15,17,18,19,20), 
  c(6, 12))

l <- subjects_and_their_valid_trials # make it easier to read

maxlen <- max(sapply(l, length))
mat <- t(sapply(l, function(x)c(x, rep(NA, maxlen-length(x)))))
minvals <- 1
maxvals <- apply(mat, 1, function(x)max(which(!is.na(x))))


# ---- How the JAGS Loop is structured
nSubj_Condition1 <- 6

# Loop for condition 
for (j in 1:nSubj_Condition1) {  #Subject-loop
  
  print( "///---------------------------------- ///")
  print(paste("Subject number", j))
  
  #-------------------------
  # This loop restricts the following loop to valid cases in the matrix
  for (k in 1:maxvals[j]) { 
    
    print(paste("Iterating through trial matrix... step", k))
    
    # This loop loops through the matrix of valid trials for each subjects 
    for (i in mat[j,k]) {
      
      print(paste("A valid trial number is:", i))
    }
  }
}
IndiPsi
  • 37
  • 5
  • 1
    Can you give us the code you have tried and the error associated to it when you run your JAGS loop? – Adam Quek May 26 '22 at 09:45
  • Hi, Thank you, I have edited the question accordingly, I hope it is clearer now. – IndiPsi May 26 '22 at 10:11
  • 1
    @IndiPsi I think you could do `print(paste("A valid trial number is:", mat[j,k]))` without the `i` for loop. Since `mat[j,k]` is a single value, the loop is over one value. – DaveArmstrong May 26 '22 at 17:06
  • Sorry, I had a typo in the code. The mat[j,k] is no single value: it is the array: for example c(1,3,4,8, 11,13,17). – IndiPsi May 26 '22 at 17:46
  • Hi Dave, I finally understand what you meant :) I need the "i" because I reference it in the formulas that will be below this code block. So something like this gamma[i,j] <- pow(prospects[j,i,],gamma.b.alt[j]) is happening, which is why I need this part to be able to use it as index. Does that clear up the confusion? – IndiPsi May 27 '22 at 17:43

2 Answers2

2

What about something like this:

l <- list(
c(1,3,4,8, 11,13,17), 
c(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20), 
c(2, 14))

Turn your list into a matrix that is padded out with NA values.

maxlen <- max(sapply(l, length))
mat <- t(sapply(l, function(x)c(x, rep(NA, maxlen-length(x)))))

# > mat
# [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11] [,12] [,13] [,14] [,15] [,16]
# [1,]    1    3    4    8   11   13   17   NA   NA    NA    NA    NA    NA    NA    NA    NA
# [2,]    1    2    3    4    5    6    7    8    9    10    11    12    13    14    15    16
# [3,]    2   14   NA   NA   NA   NA   NA   NA   NA    NA    NA    NA    NA    NA    NA    NA
# [,17] [,18] [,19] [,20]
# [1,]    NA    NA    NA    NA
# [2,]    17    18    19    20
# [3,]    NA    NA    NA    NA

Then, for each subject, identify the first and last columns with valid data.

minvals <- 1
maxvals <- apply(mat, 1, function(x)max(which(!is.na(x))))

Then, in your JAGS model, you could pull the appropriate values out of mat to identify the trial numbers.

for (j in 1:subjects) {  # Subject-loop
  for (i in minvals[j]:maxvals[j]) {  # Trial loop
     mat[j,i] ## gives you the trial number for each subject 
    
  }
}
DaveArmstrong
  • 18,377
  • 2
  • 13
  • 25
  • That sounds promising... but in this part for (i in minvals[j]:maxvals[j]) I would already need to loop through mat[i,j] --- or do you mean adding a third loop then? – IndiPsi May 26 '22 at 11:53
  • @IndiPsi I don't think you need a third loop. In your original code `i` would be a value from each subject's list, right. So, for subject 3, the first value of `i` would be 2. In my code above, you could use `mat[j,i]` as the trial value, so for subject `mat[3,1]` would be 2 - the value of the first valid trial for subject 3. So, you should be able to use `mat[j,i]` in my code in the same way you would use `i` in your code. – DaveArmstrong May 26 '22 at 12:19
  • Ok, I think I wrote this not easy to understand, sorry: the 1:subjects is not a list as JAGS cannot handle lists, it is only a number I pass JAGS, for example: 20 (if I have a sample of 20). The loop is supposed to iterate over each of the 20 subjects and then each of the valid trials for each subject and those are store in mat[j,i] in your code and specified by the minvals abd maxvals ---- So I guess, I need to do another loop like this: for (k in mat[j,i]) {} – IndiPsi May 26 '22 at 12:33
  • @IndPsi `mat[j,i]` is a single value, so you shouldn't have to iterate over it. You should be able to plug `mat[j,i]` into other aspects of your model as a scalar value. – DaveArmstrong May 26 '22 at 15:09
  • Yes, it is a single value and I need to run on every single value some code. I will update my question with the solution that I got thanks to you! :) – IndiPsi May 26 '22 at 16:52
0

You can map lists which returns the initial list after an arbitrary function has been applied to each list member (recursively, too, if desired). In base R, you'd do this with lapply() or Map(). Package {purrr} extends this concept with a variety of convenient functions.

Example:

## base R
lapply(your_list, function(list_element) {some_JAGS_call(list_element)})

## purrr (plus pipe and formula notation)
your_list %>% map(~ some_JAGS_call(.x))

This online ressource on R and JAGS might be helpful.

Edit 1 If you, e.g., need to map my_list from a list of vectors to a list of matrices fit for JAGS, you lapply (list-apply) the function as.matrix():

my_list_of_matrices <- lapply(my_list, function(el) as.matrix(el))

(Supply further arguments to as.matrix() as required by your JAGS call.)

Edit 2 Gotcha: when you index a list with single brackets like in your loop: for (i in my_list[j]) { # Trial loop you will still obtain the list element as a list. Use double brackets: for (i in my_list[[j]]) to pick the list element (e.g. a matrix, if so) without the sticky list part.

  • Hi, Thank you but I do not see how this solves the issue. I do not need to pass things to JAGS, I have a separate function for that - I just need a format that JAGS can deal with, and I know double and matrices work. But I will check out the resources, maybe I just don't understand yet, what you mean. – IndiPsi May 26 '22 at 10:07
  • plz see edited answer –  May 26 '22 at 10:36
  • I did that before, this produces a matrix with list elements. JAGS cannot deal with that either. – IndiPsi May 26 '22 at 10:37
  • Did you double check? I get FALSE for `is.matrix(my_list_of_matrices)`, TRUE for `is.list(my_list_of_matrices)` and TRUE for `all(sapply(my_list_of_matrices, function(el) is.matrix(el)))` - meaning that my_list_of_matrices is a list (not matrix) of exlusively matrix elements. –  May 26 '22 at 10:44
  • plz see the gotcha in **edit 2** ;-) –  May 26 '22 at 10:56
  • Maybe I do not understand you correctly or apply the in a false way: This is what I do: my_list_of_subjects_with_their_trials <- lapply(my_list_of_subjects_with_their_trials, function(el) as.matrix(el)) -- -this results in a list with list items. – IndiPsi May 26 '22 at 11:18
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/245074/discussion-between-i-o-and-indipsi). –  May 26 '22 at 11:50
  • Thank you, you were right with Edit 2 - however, JAGS still cannot access the element as it needs a matrix also for the parent. – IndiPsi May 26 '22 at 11:58