Extracting coefficients while looping over variable names - r

I'm working on some time-series stuff in R (version 3.4.1), and would like to extract coefficients from regressions I ran, in order to do further analysis.
All results are so far saved as uGARCHfit objects, which are basically complicated list objects, from which I want to extract the coefficients in the following manner.
What I want is in essence this:
for(i in list){
i_GARCH_mxreg <- i_GARCH#fit$robust.matcoef[5,1]
}
"list" is a list object, where every element is the name of one observation. For now, I want my loop to create a new numeric object named as I specified in the loop.
Now this obviously doesn't work because the index, 'i', isn't replaced as I would want it to be.
How do I rewrite my loop appropriately?
Minimal working example:
list <- as.list(c("one", "two", "three"))
one_a <- 1
two_a <- 2
three_a <- 3
for (i in list){
i_b <- i_a
}
what this should give me would be:
> one_b
[1] 1
> two_b
[1] 2
> three_b
[1] 3
Clarification:
I want to extract the coefficients form multiple list objects. These are named in the manner 'string'_obj. The problem is that I don't have a function that would extract these coefficients, the list "is not subsettable", so I have to call the individual objects via obj#fit$robust.matcoef[5,1] (or is there another way?). I wanted to use the loop to take my list of strings, and in every iteration, take one string, add 'string'_obj#fit$robust.matcoef[5,1], and save this value into an object, named again with " 'string'_name "
It might well be easier to have this into a list rather than individual objects, as someone suggest lapply, but this is not my primary concern right now.
There is likely an easy way to do this, but I am unable to find it. Sorry for any confusion and thanks for any help.

The following should match your desired output:
# your list
l <- as.list(c("one", "two", "three"))
one_a <- 1
two_a <- 2
three_a <- 3
# my workspace: note that there is no one_b, two_b, three_b
ls()
[1] "l" "one_a" "three_a" "two_a"
for (i in l){
# first, let's define the names as characters, using paste:
dest <- paste0(i, "_b")
orig <- paste0(i, "_a")
# then let's assign the values. Since we are working with
# characters, the functions assign and get come in handy:
assign(dest, get(orig) )
}
# now let's check my workspace again. Note one_b, two_b, three_b
ls()
[1] "dest" "i" "l" "one_a" "one_b" "orig" "three_a"
[8] "three_b" "two_a" "two_b"
# let's check that the values are correct:
one_b
[1] 1
two_b
[1] 2
three_b
[1] 3
To comment on the functions used: assign takes a character as first argument, which is supposed to be the name of the newly created object. The second argument is the value of that object. get takes a character and looks up the value of the object in the workspace with the same name as that character. For instance, get("one_a") will yield 1.
Also, just to follow up on my comment earlier: If we already had all the coefficients in a list, we could do the following:
# hypothetical coefficients stored in list:
lcoefs <- list(1,2,3)
# let's name the coefficients:
lcoefs <- setNames(lcoefs, paste0(c("one", "two", "three"), "_c"))
# push them into the global environment:
list2env(lcoefs, env = .GlobalEnv)
# look at environment:
ls()
[1] "dest" "i" "l" "lcoefs" "one_a" "one_b" "one_c"
[8] "orig" "three_a" "three_b" "three_c" "two_a" "two_b" "two_c"
one_c
[1] 1
two_c
[1] 2
three_c
[1] 3
And to address the comments, here a slightly more realistic example, taking the list-structure into account:
l <- as.list(c("one", "two", "three"))
# let's "hide" the values in a list:
one_a <- list(val = 1)
two_a <- list(val = 2)
three_a <- list(val = 3)
for (i in l){
dest <- paste0(i, "_b")
orig <- paste0(i, "_a")
# let's get the list-object:
tmp <- get(orig)
# extract value:
val <- tmp$val
assign(dest, val )
}
one_b
[1] 1
two_b
[1] 2
three_b
[1] 3

Related

Creating a vector of graph objects in r

I am currently working with the 'igraph' package on R.
I have created two functions that create a statistical table of graph object that work pretty well if used directly on a single graph object (here is an example of what they look like) :
Sfn <- function(x) # Give a table of statistics for nodes
{
Name <- deparse(substitute(x))
Nodes <- V(x)$name
Dtotal <- degree(x, mode="all")
Eigenvector <- eigen_centrality(x)
statistics_table <- data.frame(Nodes,
Dtotal,
Eigenvector)
colnames(statistics_table) <- c("Nodes","Total Degrees",
"Eigenvector centrality")
write.table(statistics_table,
file = paste0("Table_of_",Name,"_nodes.csv"),
sep=",",
row.names = F)
print("Success.")
}
As I am using several graph objects, I would like not to have to write one line per command, such as :
Sfn(g)
Sfn(g2)
Sfn(g3)
# etc...
Sfn(n)
I would thus like to create a vector of lists in which I could collect all my graph objects. I created something like that :
G <- c(
list(CC1),list(CC2),list(CC3),
list(CC4),list(CC5),list(CC6),
list(CC7),list(CC8),list(CC9),
list(CC10),list(CC11),list(CC12))
Yet, this solution is not optimal. First, it is too long to write if I have, for example, 100 graph objects. Secondly, if I write my script with an for() loop, the name of the variable sent to my function will be the name of the parameter of for(), thus, ruining the variable Name of my function Sfn. In other words, the script for(i in G) {Sfn(G)} does not work, because the variable Name will be equal to i :
# In my function Sfn, Name <- deparse(substitute(i)),
for(i in G) {print(deparse(substitute(i)))}
[1] "i"
[1] "i"
[1] "i"
[1] "i"
[1] "i"
[1] "i"
[1] "i"
[1] "i"
[1] "i"
[1] "i"
[1] "i"
[1] "i"
Also, the solution there : (Change variable name in for loop using R) does not work because I have, in my graph objects, very different randomly attributed graph names (such as "CC1","g2","CT3","CC1T3", etc).
Do you have any idea on how I could possibly:
1 - achieve a better way of creating a vector of graph objects ?
2 - make the name of the parameter sent to my variable the same as the actual name of the variable ?
Using deparse(substitute()) makes it hard to do what you want. If you really want to do this without changing your Sfn function, you'll need to construct a call to it as a string and parse that. For example:
names <- c("CC1", "g2")
Sfn <- function(x) deparse(substitute(x)) # just return the name
result <- list()
for (n in names) {
result[[n]] <- eval(parse(text = paste("Sfn(", n, ")")))
}
result
#> $CC1
#> [1] "CC1"
#>
#> $g2
#> [1] "g2"
Created on 2021-09-12 by the reprex package (v2.0.0)
This could be much simpler if you passed the name you want as a string to Sfn, instead of trying to get it using deparse(substitute()), e.g.
names <- c("CC1", "g2")
Sfn <- function(x, name) name
result <- list()
for (n in names)
result[[n]] <- Sfn(n, n)
result
#> $CC1
#> [1] "CC1"
#>
#> $g2
#> [1] "g2"
Created on 2021-09-12 by the reprex package (v2.0.0)
Edited to add: Not only is the second solution cleaner, it's safer too. If you don't have complete control of the names vector, there's a huge security risk: someone could set the "name" to some executable code (see https://xkcd.com/327/) and it would be executed.

How to see the difference in two strings

I'm trying to find the difference between two columns in a CSV file, which I named Test.
I'd like to add a new column called 'Results' that contains the difference between Events_1 & Events_2. If there is no difference the Results can be blank.
This is a basic example, for what I'm trying to accomplish, the real list contains hundreds of events in both columns.
Not tested with your data, but
vec2 <- c("hello,goodbye","hello,goodbye")
vec1 <- c("hello","hello,goodbye")
Map(setdiff, strsplit(vec2, "[,\\s]+"), strsplit(vec1, "[,\\s]+"))
# [[1]]
# [1] "goodbye"
# [[2]]
# character(0)
If you need them to be comma-delimited strings, then
mapply(function(a,b) paste(setdiff(a,b), collapse=","), strsplit(vec2, "[,\\s]+"), strsplit(vec1, "[,\\s]+"))
# [1] "goodbye" ""

R building list of lists with lapply and adding in a name

I'm using parLapply to read lots of small CSV files. Then running table() to tabulate the results and put them in a list of lists. into the parLapply function I pass in the id/csv file name.
ll <- parLapply(ids, function(id){
df<-read.csv(paste0(id,".csv"))
return(table(df$result))})
However the names of the list is lost ( names(ll) returns NULL ). How can I get the names for each id associated with the appropriate with the list.
It's because your list is not named. You can name it using names(ids) <- ids:
ids <- list(3,2,1)
names(ids) <- ids
parLapply(cl,ids,function(x) x)
$`3`
[1] 3
$`2`
[1] 2
$`1`
[1] 1

R get objects' names from the list of objects

I try to get an object's name from the list containing this object. I searched through similar questions and find some suggestions about using the deparse(substitute(object)) formula:
> my.list <- list(model.product, model.i, model.add)
> lapply(my.list, function(model) deparse(substitute(model)))
and the result is:
[[1]]
[1] "X[[1L]]"
[[2]]
[1] "X[[2L]]"
[[3]]
[1] "X[[3L]]"
whereas I want to obtain:
[1] "model.product", "model.i", "model.add"
Thank you in advance for being of some help!
You can write your own list() function so it behaves like data.frame(), i.e., uses the un-evaluated arg names as entry names:
List <- function(...) {
names <- as.list(substitute(list(...)))[-1L]
setNames(list(...), names)
}
my.list <- List(model.product, model.i, model.add)
Then you can just access the names via:
names(my.list)
names(my.list) #..............
Oh wait, you didn't actually create names did you? There is actually no "memory" for the list function. It returns a list with the values of its arguments but not from whence they came, unless you add names to the pairlist given as the argument.
You won't be able to extract the information that way once you've created my.list.
The underlying way R works is that expressions are not evaluated until they're needed; using deparse(substitute()) will only work before the expression has been evaluated. So:
deparse(substitute(list(model.product, model.i, model.add)))
should work, while yours doesn't.
To save stuffing around, you could employ mget to collect your free-floating variables into a list with the names included:
one <- two <- three <- 1
result <- mget(c("one","two","three"))
result
#$one
#[1] 1
#
#$two
#[1] 1
#
#$three
#[1] 1
Then you can follow #DWin's suggestion:
names(result)
#[1] "one" "two" "three"

R search variable in all list objects [duplicate]

Filter(is.atomic, something)
returns atomic vectors.
1. Weather -example here
> Filter(is.atomic, study)
$region
[1] "Hamburg" "Bremen"
2. mosaic-plot-as-tree-plot -example here
> Map(function(x) Filter(is.atomic, x), ls())
$g
[1] "g"
$lookup
[1] "lookup"
$req.data
[1] "req.data"
$tmp
[1] "tmp"
$tmp1
[1] "tmp1"
Look their positions can be arbitrary, I may have faintest clue of their data-structure so cannot use var$some$...$vector. I feel the need of ?Position. Use your imagination, the examples are not exclusive. How can I access their atomic vectors?
To flatten a list so you can access the atomic vectors, you can use following function:
flatten.list <- function(x){
y <- list()
while(is.list(x)){
id <- sapply(x,is.atomic)
y <- c(y,x[id])
x <- unlist(x[!id],recursive=FALSE)
}
y
}
This function maintains names of the elements. Usage, using the list x from Vincent's answer :
x <- list(
list(1:3, 4:6),
7:8,
list( list( list(9:11, 12:15), 16:20 ), 21:24 )
)
then:
> flatten.list(x)
[[1]]
[1] 7 8
[[2]]
[1] 1 2 3
[[3]]
[1] 4 5 6
[[4]]
[1] 21 22 23 24
...
To recursively do an action on all atomic elements in a list, use rapply() (which is what Vincent handcoded basically).
> rapply(x,sum)
[1] 6 15 15 30 54 90 90
> rapply(x,sum,how='list')
[[1]]
[[1]][[1]]
[1] 6
[[1]][[2]]
[1] 15
[[2]]
[1] 15
...
See also ?rapply
PS : Your code Map(function(x) Filter(is.atomic, x), ls()) doesn't make sense. ls() returns a character vector, so every element of that character vector will be returned as part of the list. This doesn't tell you anything at all.
Next to that, Filter() doesn't do what you believe it does. Taking the example list x, from the answer of Vincent, accessing only the atomic parts of it is pretty easy. Filter() only returns the second element. That's the only atomic element. Filter(is.atomic, x) is 100% equivalent to:
ind <- sapply(x, is.atomic)
x[ind]
Your question is very unclear, to say the least: an example of the input data you have and the desired output would help...
Since you suggest that we "use our imagination", I assume that you have a hierarchical data structure, i.e., a list of lists of...of lists, whose depth is unknown. For instance,
x <- list(
list(1:3, 4:6),
7:8,
list( list( list(9:11, 12:15), 16:20 ), 21:24 )
)
The leaves are vectors, and you want to do "something" with those vectors.
For instance, you may want to concatenate them into a single vector: that is what the unlist function does.
unlist(x)
You could also want all the leaves, in a list, i.e., a list of vectors.
You can easily write a (recursive) function that explores the data structure and progressively builds that list, as follows.
leaves <- function(u) {
if( is.atomic(u) ) { return( list(u) ) }
result <- list()
for(e in u) {
result <- append( result, leaves(e) )
}
return(result)
}
leaves(x)
You could also want to apply a function to all the leaves, while preserving the structure of the data.
happly <- function(u, f, ...) {
if(is.atomic(u)) { return(f(u,...)) }
result <- lapply(u, function(v) NULL) # List of NULLs, with the same names
for(i in seq_along(u)) {
result[[i]] <- happly( u[[i]], f, ... )
}
return( result )
}
happly(x, range) # Apply the "range" function to all the leaves
Filter will return a list. The functions lapply and sapply are typically used to process individual elements of a list object. If you instead want to access them by number using "[" or "[[" then you can determine the range of acceptable numbers with length(object). So object[[length(object)]] would get you the last element (as would ( tail(object, 1) ).

Resources