I have a list of ggplot objects that I am using the print() function to display. When I do this in r markdown it adds ## $`item name` where "item name" is the name of the object in the list. It adds this before every ggplot object. How do I get rid of this?
you can use unname function to make the list printed as usual
a <- list(`one` = 2 , `two` = 3 , `three` = 4)
print(unname(a))
#> [[1]]
#> [1] 2
#>
#> [[2]]
#> [1] 3
#>
#> [[3]]
#> [1] 4
Related
I have multiple CSV files that are stored in a specific order, and I want to read them in this exact same order, from the bottom to the top. They are stored like this:
tFile20.RAW
tFile17.RAW
tFile16.RAW
tFile12.RAW
tFile11.RAW
tFile10.RAW
.
.
and so on until tFile1.RAW. I've seen multiple questions about this issue but all of them were regarding python.
I'm using this code, but it is reading the files in a random order (the CSVs are stored in smallfolder):
temp = list.files(path = '/bigfolder/myname/smallfolder', pattern="RAW", full.names = TRUE)
final_list = lapply(temp, read.csv)
it's reading tFile1.RAW and then jumps to tFile10.RAW and tFile11.RAW and so on..
How can I make it read starting from tFile1.RAW and go to the top? so the first CSV file it reads would be tFile1.RAW, hence final_list[[1]] = tFile1.RAW, and then final_list[[2]] = tFile2.RAW, final_list[[3]] = tFile3.RAW and so on.
library(stringr)
Preparing the folder structure and writing files
# Creating folder
folder_path <- "bigfolder/myname/smallfolder"
dir.create(folder_path, recursive = TRUE)
# Files
files <- c("file1.csv", "file10.csv", "file11.csv", "file12.csv", "file13.csv",
"file14.csv", "file15.csv", "file16.csv", "file17.csv", "file18.csv",
"file19.csv", "file2.csv", "file20.csv", "file3.csv", "file4.csv",
"file5.csv", "file6.csv", "file7.csv", "file8.csv", "file9.csv"
)
# writing files
lapply(files, \(x) write.csv(x, file.path(folder_path, x)))
With that I have a folder structure as you described in your code, now I will
list all files I’m going to read. The only difference here is that I will use
full.names = FALSE because I think that in you local machine the path has numbers in it
temp <- list.files(folder_path)
You have to sort the files afther you use the list.file function, I would do it as follow:
Extract the integer in the name of the file
file_number <- stringr::str_extract(temp, "[0-9]+") |> as.numeric()
Get the position where each file should be, comparing the ordered file_number with
the position they actually have
correct_index_order <- sapply(sort(file_number), \(x) which(file_number == x))
Rearrange you temp vector with that new vector
temp <- temp[correct_index_order]
temp
#> [1] "file1.csv" "file2.csv" "file3.csv" "file4.csv" "file5.csv"
#> [6] "file6.csv" "file7.csv" "file8.csv" "file9.csv" "file10.csv"
#> [11] "file11.csv" "file12.csv" "file13.csv" "file14.csv" "file15.csv"
#> [16] "file16.csv" "file17.csv" "file18.csv" "file19.csv" "file20.csv"
Now we can read the files
lapply(file.path(folder_path, temp), read.csv)
#> [[1]]
#> X x
#> 1 1 file1.csv
#>
#> [[2]]
#> X x
#> 1 1 file2.csv
#>
#> [[3]]
#> X x
#> 1 1 file3.csv
#>
#> [[4]]
#> X x
#> 1 1 file4.csv
#>
#> [[5]]
#> X x
#> 1 1 file5.csv
#>
#> [[6]]
#> X x
#> 1 1 file6.csv
#>
Created on 2023-01-14 with reprex v2.0.2
I have a multiple lists in my environment(all start with "CDS_"). Each list is conducted of multiple sub lists.I want to call the lists one by one to apply a function for each of these objects.
This is what I am trying:
lists<-grep("CDS_",names(.GlobalEnv),value=TRUE) #Lists all objectrs staring with "CDS_"
for (i in seq_along(lists)){
data<-do.call("list",mget(lists[i])) #this line blends all sub lists into one list
assign(paste("Df_", lists[i], sep = "_"), my_function(data) # my_function requires a list with multiple sub lists
}
but the issue is the do.call("list",mget(lists[i])) blends all sub lists into one. For example if there is a list with one sub list it returns the list but all sub lists go into one!
Any solutions how to make this work?
here is a sample to test:
#Defining my_function pulling out the sub list which contains "sample1"
my_function<-function(.data){
# pull out the undergraduate data
grep("sample1", .data, value = TRUE)
}
# 1st list
list_1 <- list(1:54,
c("This","is","sample1","for","list1"),
c("This","is","sample2","for","list1"),
"Hi")
# 2nd list
list_2 <- list(51:120,
c("This","is","sample1","for","list1"),
c("This","is","sample2","for","list1"),
"Bus")
# 3rd list
list_3 <- list(90:120,
letters[16:11],
2025)
lists<-grep("list_",names(.GlobalEnv),value=TRUE)
for (i in seq_along(lists)){
data<-do.call("list",mget(lists[i]))
assign(paste("sample1_", lists[i], sep = ""), my_function(data))
}
As mentioned by #MrFlick, R has a ton of list functionality. It is usually the case that you are better off storing your lists in a list than trying to directly edit them in the environment. Here is one possible solution using base R:
l <- mget(ls(pattern = "^list_\\d$")) # store lists in a list
lapply(l, \(x) lapply(x, my_function))
$list_1
$list_1[[1]]
character(0)
$list_1[[2]]
[1] "sample1"
$list_1[[3]]
character(0)
$list_1[[4]]
character(0)
$list_2
$list_2[[1]]
character(0)
$list_2[[2]]
[1] "sample1"
$list_2[[3]]
character(0)
$list_2[[4]]
character(0)
$list_3
$list_3[[1]]
character(0)
$list_3[[2]]
character(0)
$list_3[[3]]
character(0)
Update
Sticking with base R to remove non-matches you could do:
lapply(l, \(x) Filter(length, lapply(x, my_function)))
$list_1
$list_1[[1]]
[1] "sample1"
$list_2
$list_2[[1]]
[1] "sample1"
$list_3
list()
A purrr solution would be:
library(purrr)
map(map_depth(l, 2, my_function), compact)
When you have lists of lists, and option is rapply, the recursive version of lapply.
my_function<-function(.data){
# pull out the undergraduate data
grep("sample1", .data, value = TRUE)
}
lists <- mget(ls(pattern = "^list_"))
rapply(lists, my_function, how = "list")
#> $list_1
#> $list_1[[1]]
#> character(0)
#>
#> $list_1[[2]]
#> [1] "sample1"
#>
#> $list_1[[3]]
#> character(0)
#>
#> $list_1[[4]]
#> character(0)
#>
#>
#> $list_2
#> $list_2[[1]]
#> character(0)
#>
#> $list_2[[2]]
#> [1] "sample1"
#>
#> $list_2[[3]]
#> character(0)
#>
#> $list_2[[4]]
#> character(0)
#>
#>
#> $list_3
#> $list_3[[1]]
#> character(0)
#>
#> $list_3[[2]]
#> character(0)
#>
#> $list_3[[3]]
#> character(0)
Created on 2022-05-13 by the reprex package (v2.0.1)
Edit
To answer to the OP's comment to another answer, to keep only the matches, save the rapply result and a lapply loop calling lengths, the list version of vector length is used to extract the matches.
r <- rapply(lists, my_function, how = "list")
lapply(r, \(x) x[lengths(x) > 0])
#> $list_1
#> $list_1[[1]]
#> [1] "sample1"
#>
#>
#> $list_2
#> $list_2[[1]]
#> [1] "sample1"
#>
#>
#> $list_3
#> list()
Created on 2022-05-13 by the reprex package (v2.0.1)
I have a simulation model that takes parameters.
Instead of passing all parameters to a main function (which is complicated for the user since the dimensions of some of the parameters depend on themselves, e.g. if n=2, vec_n is length 2), I wanted an internal PARAMETERS object within the package, which all functions could access, and the users can change.
I made a package Test with two functions and an internal list INTERNAL=list(a=2) which is saved in sysdata.rda.
test_function<-function(b){
INTERNAL$a = b
print(INTERNAL)
second_function()
}
second_function<-function(){
print(INTERNAL$a)
}
However on loading the package, and running it I get the following output:
> test_function(5)
$a
[1] 5
[1] 2
Clearly, the object itself doesn't change outside the function.
I'd appreciate any help / advice in getting this to work.
INTERNAL$a = b creates a local copy of INTERNAL in your function, and modifies that. Since you want to modify the global copy, you could use
INTERNAL$a <<- b
but this is a bad idea, and probably wouldn't work in a package: you can't modify most values in a package after it is installed.
Alternatives to this are to make INTERNAL into an environment (which you can modify), or create a function that returns the values you want, e.g.
INTERNAL <- function(a = "default", b = "default") {
list(a = a, b = b)
}
INTERNAL(a = 2)
#> $a
#> [1] 2
#>
#> $b
#> [1] "default"
Created on 2021-04-19 by the reprex package (v1.0.0)
You can combine these two ideas:
INTERNAL <- local({
saved <- list(a = "default", b = "default")
function(...) {
saved <<- modifyList(saved, list(...))
saved
}
})
INTERNAL(a = 1)
#> $a
#> [1] 1
#>
#> $b
#> [1] "default"
INTERNAL(b = 2)
#> $a
#> [1] 1
#>
#> $b
#> [1] 2
INTERNAL(c = 3)
#> $a
#> [1] 1
#>
#> $b
#> [1] 2
#>
#> $c
#> [1] 3
Created on 2021-04-19 by the reprex package (v1.0.0)
Take a simple nested list L:
L <- list(lev1 = list(lev2 = c("bit1","bit2")), other=list(yep=1))
L
#$lev1
#$lev1$lev2
#[1] "bit1" "bit2"
#
#
#$other
#$other$yep
#[1] 1
And a vector giving a series of depths for each part I want to select from L:
sel <- c("lev1","lev2")
The result I want when indexing is:
L[["lev1"]][["lev2"]]
#[1] "bit1" "bit2"
Which I can generalise using Reduce like so:
Reduce(`[[`, sel, init=L)
#[1] "bit1" "bit2"
Now, I want to extend this logic to do a replacement, like so:
L[["lev1"]][["lev2"]] <- "new val"
, but I am genuinely stumped as to how to generate the recursive [[ selection in a way that will allow me to then assign to it as well.
Why cant you just do
L[[sel]] <- "new val"
well if you want to do the long way then
You could still use Reduce with modifyList or you could use [[<-. Here is an example with modifyList:
modifyList(L,Reduce(function(x,y)setNames(list(x),y),rev(sel),init = "new val"))
$lev1
$lev1$lev2
[1] "new val"
$other
$other$yep
[1] 1
You could eval() and parse() by concatenating everything. I am not sure how generalized you could make it:
``` r
L <- list(lev1 = list(lev2 = c("bit1","bit2")), other=list(yep=1))
L
#> $lev1
#> $lev1$lev2
#> [1] "bit1" "bit2"
#>
#>
#> $other
#> $other$yep
#> [1] 1
sel <- c("lev1","lev2")
eval(parse(text = paste0('L', paste0('[["', sel, '"]]', collapse = ''), '<- "new val"')))
L
#> $lev1
#> $lev1$lev2
#> [1] "new val"
#>
#>
#> $other
#> $other$yep
#> [1] 1
Created on 2019-11-25 by the reprex package (v0.3.0)
I have a list like the following in R:
data1<-list("A" = 1, "B" = 2, "C" = 3,"D" = 4)
and when I print data1 I have:
$A
[1] 1
$B
[1] 2
$C
[1] 3
$D
[1] 4
I have a csv file with the values:
alt1,alt2,alt3,alt4
appear,certain,dance,example
apply,danger,chance,excellent
where alt1,alt2,... are the headers of the csv.file
I would like to extract the second row from my csv file so that I could get something like data1, I have done the following:
getData=read.csv("test.csv",header=TRUE)
q<-getData[2,]
print(q)
anylist<-list()
anylist[[q[1]]]<-1
anylist[[q[2]]]<-2
anylist[[q[3]]]<-3
anylist[[q[4]]]<-4
print(anylist)
because I need that anylist to have the same structure like data1, I mean if I will have to write directly it would be:
anylist<-list("apply" = 1, "danger" = 2, "chance" = 3,"excellent" = 4)
so when I print anylist I want to print:
$apply
[1] 1
$danger
[1] 2
$chance
[1] 3
$excellent
[1] 4
but I got the error:
Error in anylist[[q[1]]] <- 1 : invalid subscript type 'list'
The following quick function will take a row of data, determine the order, and associate order and name, outputting a list. I believe this is what you wanted, right? If you wanted to do this for many rows, simply use an apply statement, with MARGIN=1, and you will get a list of lists. Is this what you were looking for?
getNames=function(row){
retList=as.list(order(row))
names(retList) = as.character(sort(row))
return(retList)
}
...here's a quick validation.
test=c("apply", "danger", "chance", "excellent")
getNames(test)
$apply
[1] 1
$chance
[1] 3
$danger
[1] 2
$excellent
[1] 4
test2=c('alt1','alt2','alt3','alt4')
getNames(test2)
$alt1
[1] 1
$alt2
[1] 2
$alt3
[1] 3
$alt4
[1] 4
Gotcha!
getData=read.csv("test.csv",header=TRUE,stringsAsFactors=FALSE)
q<-getData[2,]
n<-as.list(c(1:4))
names(n)<-q