1

I would like to create two lists inside another list. Normally I will have to create 50 each one for a state of USA and I'm looking for a way to make it faster.

State <- c("ALABAMA", "ALABAMA", "ALASKA", "ALASKA")
Num <- c(5, 6, 7, 8)
d <- data.frame(State, Num)

uni<-unique(d$State)

data = list(
  for(i in 1 : length(uni)){
    list[[i]](
      name = un[i],
      y = summarise(sum(d$Num[D$State==uni[i]])),
      drilldown = tolower(un[i])
    )
  }
)

More specifically I want each list to include the name of the State in Capital letters, y as the sum of the Num and drilldown as the name of the State in non capital letters. The result should be something like:

name="ALABAMA"
y= 11
drilldown="alabama"

name="ALASKA"
y= 15
drilldown="alaska"

Manually it would be like :

data = list(

      list(
        name = "ALABAMA",
        y = 11,
        drilldown = "alabama"
      ),
      list(
        name = "ALASKA",
        y = 15,
        drilldown = "alaska"
      )

    )

which gives a list of 2 lists of 3 objects.

It may be possible without for()so Im open to other suggestions

4 Answers 4

3
State <- c("ALABAMA", "ALABAMA", "ALASKA", "ALASKA")
Num <- c(5, 6, 7, 8)
d <- data.frame(State, Num, stringsAsFactors = F)

Solution 1 : Use built-in packages

df1 <- within(aggregate(Num ~ State, d, FUN = sum), drilldown <- tolower(State))
output1 <- lapply(split(df1, 1:nrow(df1)), c)
str(output1)

# List of 2
#  $ 1:List of 3
#   ..$ State    : chr "ALABAMA"
#   ..$ Num      : num 11
#   ..$ drilldown: chr "alabama"
#  $ 2:List of 3
#   ..$ State    : chr "ALASKA"
#   ..$ Num      : num 15
#   ..$ drilldown: chr "alaska"

Solution 2 : library(tidyverse)

output2 <- d %>% group_by(State) %>%
                 summarise(Num = sum(Num)) %>%
                 mutate(drilldown = tolower(State)) %>% 
                 transpose  # purrr::transpose
str(output2)

# List of 2
#  $ :List of 3
#   ..$ State    : chr "ALABAMA"
#   ..$ Num      : num 11
#   ..$ drilldown: chr "alabama"
#  $ :List of 3
#   ..$ State    : chr "ALASKA"
#   ..$ Num      : num 15
#   ..$ drilldown: chr "alaska"
Sign up to request clarification or add additional context in comments.

2 Comments

I made an edit. Yous solution gives 1 list with 2 'list of 3' while I would like 2 lists.
@firmo23 I don't understand the difference between the output you edited and mine.
3

You can run a quick rowsum() to get the group sums, then Map() to assemble the list from its parts.

xx <- with(d, rowsum(Num, State))
Map(list, name=rownames(xx), y=xx, drilldown=tolower(rownames(xx)), USE.NAMES=FALSE)
# [[1]]
# [[1]]$name
# [1] "ALABAMA"
#
# [[1]]$y
# [1] 11
#
# [[1]]$drilldown
# [1] "alabama"
#
#
# [[2]]
# [[2]]$name
# [1] "ALASKA"
#
# [[2]]$y
# [1] 15
#
# [[2]]$drilldown
# [1] "alaska"

Comments

2

Using a for-loop is indeed not the best option here, because it tends to be both slow and hard to read.

This kind of task is perfect for the dplyr package using pipes:

library(dplyr)

df_result <- d %>%
  group_by(State) %>%
  summarise(y = sum(Num),
            drilldown = tolower(first(State)))

print(df_result)

gives:

# A tibble: 2 x 3
  State       y drilldown
  <fct>   <dbl> <chr>    
1 ALABAMA    11 alabama  
2 ALASKA     15 alaska

If you want to achieve a nested list like in your example output instead of a data.frame, you can additionally use transpose() from the purrr package:

library(purrr)

transpose(df_result)

gives:

[[1]]
[[1]]$State
[1] "ALABAMA"

[[1]]$y
[1] 11

[[1]]$drilldown
[1] "alabama"


[[2]]
[[2]]$State
[1] "ALASKA"

[[2]]$y
[1] 15

[[2]]$drilldown
[1] "alaska"

3 Comments

Nice work! But the expected output should be a list, not a data frame. Maybe you can add some commands to transform it.
I made an edit. Yous solution gives 1 list with 2 'list of 3' while I would like 2 lists.
Sorry, not sure if I understand. As far as I can tell, this leads to the exact same result as what you gave as your 'manual solution'. Could you be more specific?
2

Do you want something like the following? It uses split to make a list of dataframes, one per State and then lapply an anonymous function to each of the df's.

Here are two versions, with different output formats, an object of class "list" an an object of class "data.frame".

lapply(split(d, d$State), function(DF){
  s <- as.character(DF[["State"]][1])
  list(
    State = s,
    y = sum(DF[["Num"]]),
    drilldown = tolower(s)
  )
})

lapply(split(d, d$State), function(DF){
  s <- as.character(DF[["State"]][1])
  data.frame(
    State = s,
    y = sum(DF[["Num"]]),
    drilldown = tolower(s)
  )
})

EDIT.

The first way outputs a list of two named lists. If you want those lists to be unnamed, do what G. Grothendiek comment proposes.

data <-lapply(unname(split(d, d$State)), function(DF){
  s <- as.character(DF[["State"]][1])
  list(
    State = s,
    y = sum(DF[["Num"]]),
    drilldown = tolower(s)
  )
})

3 Comments

I made an edit. Yous solution gives 1 list with 2 'list of 3' while I would like 2 lists.
This could be written: lapply(unname(split(d, d$State)), with, list(name = State[1], y = tolower(State[1]), drilldown = sum(Num)))
@firmo23 My first solution does output a list of two lists. Anyway, I have edited, see if this is it.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.