Choose Language Hide Translation Bar
uday_guntupalli
Community Trekker

Retrieving Nested List of Lists from R to JMP

Hello All, 

        I am pulling information from an API using R and retrieving the information into JMP. I have been successful with the data retrieval until the API returns a nested list of lists. 
       I am thinking the error is not prompted from R side because, I am able to execute the same piece of code in R Studio without any errors . Even in JSL , the error only occurs when I am trying to retrieve the object from R to JMP using R Get(). I get the error shown below when I try : 

R Get(A) ; 

R Get () supports retrieval of lists - is there something I am doing wrong ? 

 

       image.png

 

Also, since I have nested lists of lists - wondering if looping through the contents of the list is the most efficient way to extract the data out ? 

Best
Uday
0 Kudos
1 ACCEPTED SOLUTION

Accepted Solutions
ih
ih
Community Trekker

Re: Retrieving Nested List of Lists from R to JMP

That is more complex.  In R, all arguments are passed by value, so even in the first call modifying 'l' actually creates a copy of it inside the function's namespace and the list you are trying to modify is untouched.  In subsequent calls you are operating on smaller versions of 'l' which, again, do not reference back to your actual list.  To fix the list you would need to either save a reference back to the namespace where the list resides and then use the loc argument to figure out which part to cast, or build a new copy of the list and return it.  I do the latter here:

#verbose levels
# 0 nothing
# 1 Errors only
# 2 Everything
isvalid <- function(l, fix=FALSE, verbose = 2, loc = {} ) {
  if(length(loc) > 10) throw("List is too deep, maybe you have raw data?")
  for( i in seq_along(l) ) {
    if( class(l[[i]]) == "list" ) {
      #If a list, check recurse through it, fixing errors along the way if necessary
      if (verbose > 1) print(paste0("Found list at: ", paste(c(loc, i), collapse = ", ")))
      l[[i]] <- isvalid(l[[i]], fix=fix, verbose=verbose, loc=c(loc, i))
    
    } else if (!(class(l[[i]]) %in% c("numeric", "character", "NULL"))) {
      
      #If not valid data, try to cast as a list, or throw an error
      if (verbose > 0) print(paste0("Invalid data (", class(l[[i]]), ") found at: ", paste(c(loc, i), collapse = ", ")))
      
      if( fix ) {
        #Cast as a list
        l[[i]] <- as.list(l[[i]])
        
        #Make sure it worked
        if( class(l[[i]]) == "list" ) {
          
          #If it is a list, recurse through it
          if (verbose > 0) print(paste0("Successfully cast data as a list at: ", paste(c(loc, i), collapse = ", ")))
          l[[i]] <- isvalid(l[[i]], fix=fix, verbose=verbose, loc=c(loc, i))
          
        } else {
          
          #Throw a message if failed, not sure what might cause this
          errmsg <= paste0("Failed to cast '", class(l[[i]]), "' as a list at: ", paste(c(loc, i), collapse = ", "))
          throw( errmsg )
        }
      }
    } else {
      
      #Found valid data  
      if (verbose > 1) print(paste0("Found ", class(l[[i]]), " at: ", paste(c(loc, i), collapse = ", ")))
    }
  }
  
  #Return this part of the list unless not fixing anything and this is the first call
  return(if((!fix) & (length(loc) == 0)) {invisible()} else {l} )
}

#A list with a problem
lst <- list(list("a", "b",data.frame(a=c(1,2))), 2)

#Look for problems
isvalid(lst, verbose = 1)

#Fix problems
lst <- isvalid(lst, fix=TRUE, verbose = 1)

#Show the new data structure
isvalid(lst, verbose = 2)

fixing a list.PNG

 

 

Note that I tried including raw binary data inside the list and it recursed until I got an overflow error. If you run into that you might need to do something else.

View solution in original post

5 REPLIES 5
ih
ih
Community Trekker

Re: Retrieving Nested List of Lists from R to JMP

R Get will retrieve nested lists from R.  You need to get the whole list (I do not believe you can subscript in the R Get command) and it struggles with data frames containing lists, or anything other than numbers and characters.

 

Names default to here( 1 );

R Init();

R Submit( "\[
a <- list( 1, 2, list("a", list( "b", "c" ) ) )
]\");

a = R Get( "a" );
Show( a[3][2][1] );

R Term();
0 Kudos
uday_guntupalli
Community Trekker

Re: Retrieving Nested List of Lists from R to JMP

@ih,
In my case, I am not trying to subscript into the lists. I tried your example and it works perfectly, however, when I try the exact same thing on my end , I don't understand why I am failing. 

I checked if the data is stored as a list in R and it is indeed a list .

 

image.png

 

In JMP , here is what I do : 

 

DesResult = R Get("DesResult"); 

It still gives the same error , but that makes no sense because your example clearly demonstrates that it should be retrievable 

 

 

 

Best
Uday
0 Kudos
ih
ih
Community Trekker

Re: Retrieving Nested List of Lists from R to JMP

I often get strange errors when trying to import invalid data types. You might try checking every element in your list to make sure it is either a list, a number, or a string. Here is one way to do that (not thoroughly tested):

isvalid <- function(l, loc = {} ) {
  for( i in seq_along(l) ) {
    if( class(l[[i]]) == "list" ) {
        isvalid(l[[i]], c(loc, i))
    } else if (!(class(l[[i]]) %in% c("numeric", "character"))) {
        print(paste0("Invalid data found at: ", paste(c(loc, i), collapse = ", ")))
    } else {
      #print(paste0("Valid data found at: ", paste(c(loc, i), collapse = ", ")))
    }   
  }
}
lst = list(list("a", "b",data.frame(a=c(1,2))), 2)
isvalid(lst)

Which returns:

> lst = list(list("a", "b",data.frame(a=c(1,2))), 2)
> isvalid(lst)
[1] "Invalid data found at: 1, 3"
0 Kudos
uday_guntupalli
Community Trekker

Re: Retrieving Nested List of Lists from R to JMP

@ih
      That is leading us in the right track, If I am not being greedy, I would like to venture out and ask one more question . 

isvalid <- function(l, loc = {} ) 
{
  for( i in seq_along(l) ) 
    {
      if( class(l[[i]]) == "list" )
        {
          isvalid(l[[i]], c(loc, i))
        } else if (!(class(l[[i]]) %in% c("numeric", "character"))) 
          {
            l[i] <- as.list(l[i])
            #print(paste0("Invalid data found at: ", paste(c(loc, i), collapse = ", ")))
          } else 
            {
              #print(paste0("Valid data found at: ", paste(c(loc, i), collapse = ", ")))
            }   
    }
}
isvalid(DesResult)

I added one line to basically cast the invalid data found into a list - hoping that this would fix the issue. However, I don't think it worked as I expected. Any thoughts on how to fix that ? 

 

Best
Uday
0 Kudos
ih
ih
Community Trekker

Re: Retrieving Nested List of Lists from R to JMP

That is more complex.  In R, all arguments are passed by value, so even in the first call modifying 'l' actually creates a copy of it inside the function's namespace and the list you are trying to modify is untouched.  In subsequent calls you are operating on smaller versions of 'l' which, again, do not reference back to your actual list.  To fix the list you would need to either save a reference back to the namespace where the list resides and then use the loc argument to figure out which part to cast, or build a new copy of the list and return it.  I do the latter here:

#verbose levels
# 0 nothing
# 1 Errors only
# 2 Everything
isvalid <- function(l, fix=FALSE, verbose = 2, loc = {} ) {
  if(length(loc) > 10) throw("List is too deep, maybe you have raw data?")
  for( i in seq_along(l) ) {
    if( class(l[[i]]) == "list" ) {
      #If a list, check recurse through it, fixing errors along the way if necessary
      if (verbose > 1) print(paste0("Found list at: ", paste(c(loc, i), collapse = ", ")))
      l[[i]] <- isvalid(l[[i]], fix=fix, verbose=verbose, loc=c(loc, i))
    
    } else if (!(class(l[[i]]) %in% c("numeric", "character", "NULL"))) {
      
      #If not valid data, try to cast as a list, or throw an error
      if (verbose > 0) print(paste0("Invalid data (", class(l[[i]]), ") found at: ", paste(c(loc, i), collapse = ", ")))
      
      if( fix ) {
        #Cast as a list
        l[[i]] <- as.list(l[[i]])
        
        #Make sure it worked
        if( class(l[[i]]) == "list" ) {
          
          #If it is a list, recurse through it
          if (verbose > 0) print(paste0("Successfully cast data as a list at: ", paste(c(loc, i), collapse = ", ")))
          l[[i]] <- isvalid(l[[i]], fix=fix, verbose=verbose, loc=c(loc, i))
          
        } else {
          
          #Throw a message if failed, not sure what might cause this
          errmsg <= paste0("Failed to cast '", class(l[[i]]), "' as a list at: ", paste(c(loc, i), collapse = ", "))
          throw( errmsg )
        }
      }
    } else {
      
      #Found valid data  
      if (verbose > 1) print(paste0("Found ", class(l[[i]]), " at: ", paste(c(loc, i), collapse = ", ")))
    }
  }
  
  #Return this part of the list unless not fixing anything and this is the first call
  return(if((!fix) & (length(loc) == 0)) {invisible()} else {l} )
}

#A list with a problem
lst <- list(list("a", "b",data.frame(a=c(1,2))), 2)

#Look for problems
isvalid(lst, verbose = 1)

#Fix problems
lst <- isvalid(lst, fix=TRUE, verbose = 1)

#Show the new data structure
isvalid(lst, verbose = 2)

fixing a list.PNG

 

 

Note that I tried including raw binary data inside the list and it recursed until I got an overflow error. If you run into that you might need to do something else.

View solution in original post