Do a loop with letters in R - r

I want to do a loop with letter..i have a matrix(named 'a') and i want to have all the column names..
k<-arrayInd(2,dim(a))
colnames(a)[k[,1]]
colnames(a)[k[,2]]
colnames(a)[k[,3]]
.
.
.
colnames(a)[k[,n]]
i guess the loop will be something like that
aa<-list()
for (i in 1:n) {
aa[[i]]<-colnames(a)[k[,i]]
}
But i don't get any results. I think that the loop is ok but i have to change with something else the
aa<-list()
and replace the "list" with something else..

Suppose you have a matrix mat, which looks like this:
mat <- matrix(1:4, ncol = 2, dimnames = list(letters[1:2], LETTERS[1:2]))
You can inspect its structure like this:
str(mat)
# int [1:2, 1:2] 1 2 3 4
# - attr(*, "dimnames")=List of 2
# ..$ : chr [1:2] "a" "b"
# ..$ : chr [1:2] "A" "B"
And you get the column names by using this:
colnames(mat)
# [1] "A" "B"

Related

List of named vector from list of tibbles in R

I have an appearingly very simple task but can't figure out what I'm doing wrong. I have a list of 3 Xx2 tibbles (in the example 2x2) having a character vector and an integer vector. I want to convert it to a list of 3 named vectors where the letters are the vector elements and the numbers are the names. Here is my approach:
tbl <- tibble(numbers=c(1:2), letters=letters[1:2])
vec_names <- c("name1", "name2", "name3")
lst <- list(tbl, tbl, tbl)
names(lst) <- vec_names
lst_n <- lapply(lst, function(x) x[["letters"]])
lst_n <- sapply(vec_names,
function(x) names(lst_n[[x]]) <- lst[[x]]$numbers)
I get this result
lst_n
name1 name2 name3
[1,] 1 1 1
[2,] 2 2 2
and I can't see my mistake.
Doing
names(lst_n[["name1"]]) <- lst[["name1"]]$numbers
gives me exactly what I want for "name1" but why doesn't it work with sapply?
I had [] before and changed it to [[]] to access the tibbles inside the list instead of the list elements but it still doesn't work. Can anyone help? It seems like a very basic task.
Here's one way to do it, all in one anonymous function:
z = lapply(lst, function(x) {
result = x$letters
names(result) = x$numbers
return(result)
})
str(z)
# List of 3
# $ name1: Named chr [1:2] "a" "b"
# ..- attr(*, "names")= chr [1:2] "1" "2"
# $ name2: Named chr [1:2] "a" "b"
# ..- attr(*, "names")= chr [1:2] "1" "2"
# $ name3: Named chr [1:2] "a" "b"
# ..- attr(*, "names")= chr [1:2] "1" "2"
Your approach got stuck because after you extracted all the letters, you need to iterate over both the letters and the numbers to set the names, but lapply only lets you iterate over one thing. (And assigning inside the lapply doesn't work well, the only thing that matters is the returned object.)
If you couldn't use the approach above, doing things in one pass through lst, you can use Map instead which iterates over multiple lists. We'll use the setNames function instead of names<-(), which is what is called when you try to do names(x) <-.
Map(
f = setNames,
object = lapply(lst, "[[", "letters"),
nm = lapply(lst, "[[", "numbers")
)
# $`name1`
# 1 2
# "a" "b"
#
# $name2
# 1 2
# "a" "b"
#
# $name3
# 1 2
# "a" "b"

How to display a list of lists in a nice way

I have a list of lists, such as below.
Each list (e.g. list1, list2, list3) has two attributes: Variable and Time
list1 <- list(c("Color", "Price"), "Quarter")
list2 <- list(c("Price"), "Month")
list3 <- list(c("Color"), "Month")
total <- list(list1, list2, list3)
when we print total, we'll see:
[[1]]
[[1]][[1]]
[1] "Color" "Price"
[[1]][[2]]
[1] "Quarter"
[[2]]
[[2]][[1]]
[1] "Price"
[[2]][[2]]
[1] "Month"
[[3]]
[[3]][[1]]
[1] "Color"
[[3]][[2]]
[1] "Month"
How can I turn it into a data frame such as this one?
EDIT: I am able to accomplish it using this code. Any better suggestion is appreciated!
num <- length(total)
max <- 0
for(i in 1:num) {
if(length(total[[i]][1]) > max) {
max <- length(total[[i]])
}
}
for(i in 1:num) {
length(total[[i]][[1]]) <- max
for(j in 1:max) {
if(is.null(total[[i]][[1]][[j]])) {
total[[i]][[1]][[j]] <- " "
}
}
}
df <- data.frame(matrix(unlist(total), nrow=num, byrow=T))
This isn't just a nested-list problem, it's a nested problem. If I'm interpretting things correctly, the fact that Color and Price are in one list and Quarter is in another is meaningful. So really, you should be looking at how to turn the first element of each list into a data.frame, repeat for all other elements, then join the results. (This is where #divibisan's and #camille's suggestions come into play ... reduce the problem, use the duplicates' code, then combine.)
(The fact that I believe you will never have more than two elems in each list is not strictly a factor. Below is a general way of handling 1-or-more, not just "always 2".)
Your data:
str(total)
# List of 3
# $ :List of 2
# ..$ : chr [1:2] "Color" "Price"
# ..$ : chr "Quarter"
# $ :List of 2
# ..$ : chr "Price"
# ..$ : chr "Month"
# $ :List of 2
# ..$ : chr "Color"
# ..$ : chr "Month"
What we need to do is break this down by element-of-each-list. (I'm assuming that there will be symmetry here.) Let's start by just working on the first elem of each:
total1 <- lapply(total, `[[`, 1)
str(total1)
# List of 3
# $ : chr [1:2] "Color" "Price"
# $ : chr "Price"
# $ : chr "Color"
In order to use the suggestions from the dupes, we need to know how much to pad them. That is, they need to be the same length.
( maxlen <- max(sapply(total1, function(l) length(unlist(l)))) )
# [1] 2
Now we pad them:
total1 <- lapply(total1, function(l) { length(l) <- maxlen; l; })
str(total1)
# List of 3
# $ : chr [1:2] "Color" "Price"
# $ : chr [1:2] "Price" NA
# $ : chr [1:2] "Color" NA
(You can start to see the structure break out here.) The dupes suggested cbinding them, but you want to rbind them:
do.call(rbind, total1)
# [,1] [,2]
# [1,] "Color" "Price"
# [2,] "Price" NA
# [3,] "Color" NA
Now this is a matrix, not a data.frame, but it's a start. Let's work with naming at the end. Let's write a function to do what we just did, and then we'll use it on each level of total.
In order to do this, though, we need to modify total, so that the new first element has all first elements, new second has all seconds, etc.
newtotal <- lapply(seq_len(max(sapply(total, length))), function(i) lapply(total, `[[`, i))
str(newtotal)
# List of 2
# $ :List of 3
# ..$ : chr [1:2] "Color" "Price"
# ..$ : chr "Price"
# ..$ : chr "Color"
# $ :List of 3
# ..$ : chr "Quarter"
# ..$ : chr "Month"
# ..$ : chr "Month"
m <- do.call(cbind, lapply(newtotal, func))
m
# [,1] [,2] [,3]
# [1,] "Color" "Price" "Quarter"
# [2,] "Price" NA "Month"
# [3,] "Color" NA "Month"
So this last point is pretty much what you need, though as a matrix. From here, it's easy enough to name things:
m <- do.call(cbind, lapply(newtotal, func))
colnames(m) <- c(paste0("Var", seq_len(ncol(m)-1L)), "Time")
df <- as.data.frame(m)
df$List <- paste0('List', seq_len(nrow(df)))
df
# Var1 Var2 Time List
# 1 Color Price Quarter List1
# 2 Price <NA> Month List2
# 3 Color <NA> Month List3

write a list as element in data frame in r

I would like to make an empty data frame with 2 columns, named "ngrams" and "pred".
df <- data.frame(nGrams=character(), pred = character(), stringsAsFactors=FALSE)
I need each element in column "pred" to be a vector of words, but if I initialize 'pred = list()' the data frame won't add that column in.
I tried
> pred
[1] "a" "the" "not" "that" "to" "an"
df[nrow(df)+1, ] <- c("is", pred)
Error in matrix(value, n, p) :
(converted from warning) data length [7] is not a sub-multiple or multiple of the number of columns [2]
df[nrow(df)+1, ] <- c("the", list(pred))
Error in `[<-.data.frame`(`*tmp*`, nrow(df) + 1, , value = list("the", :
(converted from warning) replacement element 2 has 6 rows to replace 1 rows
Can anyone show me what is the right way of doing it? Thanks in advance.
EDIT
I got the solution using data.table
dt <- data.table(nGrams = my_ngrams, pred = list_pred)
where list_pred is a list of lists. But it's still good to know the right way for data frame.
You could try
d1 <- data.frame(nGrams='the', pred=I(list(pred)))
str(d1)
#'data.frame': 1 obs. of 2 variables:
#$ nGrams: Factor w/ 1 level "the": 1
#$ pred :List of 1
# ..$ : chr "a" "the" "not" "that" ...
#..- attr(*, "class")= chr "AsIs"
Or using the empty dataframe df
df[nrow(df)+1,] <- list('is', list(pred))
str(df)
#'data.frame': 1 obs. of 2 variables:
#$ nGrams: chr "is"
#$ pred :List of 1
# ..$ : chr "a" "the" "not" "that" ...
where,
pred <- c('a', 'the', 'not', 'that', 'to', 'an')

R: Sorting list by values in nested list

Data is generated by similar process:
x <- rnorm(10)
y <- c("a", "b", "c")
# chr vectors might have varying length and contents, simplified for sake of example
data_list <- list()
for(i in 1:length(x)) {
data_list <- append(data_list, list(list(numeric = x[i], char = y)))
}
Basically, structure of generated list looks like:
$ :List of 2
..$ numeric: num 0.928
..$ char : chr [1:3] "a" "b" "c"
$ :List of 2
..$ numeric: num 1.4
..$ char : chr [1:3] "a" "b" "c"
...
I would like to sort this list by numeric in ascending order, retaining initial structure.
I have tried solution explained here but it disrupts the structure of chr vectors.
data_list = data_list[order(sapply(data_list, `[[`, i=1))]
For example:
data_list[order(rapply(data_list,"numeric",f=c))]
rapply intsruction will extract all numeric values, then we order them.
Also:
data_list[order(unlist(do.call(`c`, data_list)[c(T,F)]))]

How can I make a list of lists in R?

I don't know how to make a list of lists in R.
I have several lists, I want to store them in one data structure to make accessing them easier. However, it looks like you cannot use a list of lists in R, so if I get list l1 from another list, say, l2 then I cannot access elements l1. How can I implement it?
EDIT- I will show an example of what does not work for me:
list1 <- list()
list1[1] = 1
list1[2] = 2
list2 <- list()
list2[1] = 'a'
list2[2] = 'b'
list_all <- list(list1, list2)
a = list_all[1]
a[2]
#[[1]]
#NULL
but a should be a list!
You can easily make lists of lists
list1 <- list(a = 2, b = 3)
list2 <- list(c = "a", d = "b")
mylist <- list(list1, list2)
mylist is now a list that contains two lists. To access list1 you can use mylist[[1]]. If you want to be able to something like mylist$list1 then you need to do somethingl like
mylist <- list(list1 = list1, list2 = list2)
# Now you can do the following
mylist$list1
Edit: To reply to your edit. Just use double bracket indexing
a <- list_all[[1]]
a[[1]]
#[1] 1
a[[2]]
#[1] 2
Using your example::
list1 <- list()
list1[1] = 1
list1[2] = 2
list2 <- list()
list2[1] = 'a'
list2[2] = 'b'
list_all <- list(list1, list2)
Use '[[' to retrieve an element of a list:
b = list_all[[1]]
b
[[1]]
[1] 1
[[2]]
[1] 2
class(b)
[1] "list"
If you are trying to keep a list of lists (similar to python's list.append()) then this might work:
a <- list(1,2,3)
b <- list(4,5,6)
c <- append(list(a), list(b))
> c
[[1]]
[[1]][[1]]
[1] 1
[[1]][[2]]
[1] 2
[[1]][[3]]
[1] 3
[[2]]
[[2]][[1]]
[1] 4
[[2]][[2]]
[1] 5
[[2]][[3]]
[1] 6
The example creates a list of named lists in a loop.
MyList <- list()
for (aName in c("name1", "name2")){
MyList[[aName]] <- list(aName)
}
MyList[["name1"]]
MyList[["name2"]]
To add another list named "name3" do write:
MyList$name3 <- list(1, 2, 3)
As other answers pointed out in a more complicated way already, you did already create a list of lists! It's just the odd output of R that confuses (everybody?). Try this:
> str(list_all)
List of 2
$ :List of 2
..$ : num 1
..$ : num 2
$ :List of 2
..$ : chr "a"
..$ : chr "b"
And the most simple construction would be this:
> str(list(list(1, 2), list("a", "b")))
List of 2
$ :List of 2
..$ : num 1
..$ : num 2
$ :List of 2
..$ : chr "a"
..$ : chr "b"

Resources