1
votes

I need access a tibble table that is in a nest() function inside of another nest() function.

x <- list( factory = c('a','b','c','d'), cost = c(21,30,44,100))
x <- as.data.frame(x)
x <-  x %>%
        melt('cost','factory')
colnames(x) <- c('cost','client','type')
x <- x %>%
  group_by(client)%>%
  nest()

for (m in 1:4) {
  if(m==1){
    x$scene <- m
    x2 <- x
  }else{
    x3 <- x
    x3$scene <- m
    x2 <- rbind(x2,x3)
  }
}
x2 <- x2 %>%
  group_by(scene) %>%
  nest()

What am I trying to do is applying a function inside of first vector, something like:

test <- function(df){
  df$data %>%
  mutate(increa = cost + 15)
}

x2$data%>%
  map(test)

dput(x2) result a:

structure(list(scene = 1:4, data = list(structure(list(client = structure(1L, .Label = "factory", class = "factor"), data = list(structure(list(cost = c(21, 30, 44, 100), type = c("a", "b", "c", "d")), row.names = c(NA, -4L), class = c("tbl_df", "tbl", "data.frame")))), row.names = c(NA, -1L), class = c("tbl_df", "tbl", "data.frame")), structure(list(client = structure(1L, .Label = "factory", class = "factor"), data = list(structure(list(cost = c(21, 30, 44, 100), type = c("a", "b", "c", "d")), row.names = c(NA, -4L), class = c("tbl_df", "tbl", "data.frame")))), row.names = c(NA, -1L), class = c("tbl_df", "tbl", "data.frame")), structure(list(client = structure(1L, .Label = "factory", class = "factor"), data = list(structure(list(cost = c(21, 30, 44, 100), type = c("a", "b", "c", "d")), row.names = c(NA, -4L), class = c("tbl_df", "tbl", "data.frame")))), row.names = c(NA, -1L), class = c("tbl_df", "tbl", "data.frame")), structure(list(client = structure(1L, .Label = "factory", class = "factor"), data = list(structure(list(cost = c(21, 30, 44, 100), type = c("a", "b", "c", "d")), row.names = c(NA, -4L), class = c("tbl_df", "tbl", "data.frame")))), row.names = c(NA, -1L), class = c("tbl_df", "tbl", "data.frame")))), row.names = c(NA, -4L), class = c("tbl_df", "tbl", "data.frame"))

The expected result:

[[1]]
[[1]]$`factory`
[1] "a" "b" "c" "d"

[[1]]$cost
[1]  21  30  44 100

[[1]]$increa
[1]  36  45  59 115


[[2]]
[[2]]$`factory`
[1] "a" "b" "c" "d"

[[2]]$cost
[1]  21  30  44 100

[[2]]$increa
[1]  36  45  59 115


[[3]]
[[3]]$`factory`
[1] "a" "b" "c" "d"

[[3]]$cost
[1]  21  30  44 100

[[3]]$increa
[1]  36  45  59 115


[[4]]
[[4]]$`factory`
[1] "a" "b" "c" "d"

[[4]]$cost
[1]  21  30  44 100

[[4]]$increa
[1]  36  45  59 115

Someone could help me to solve this issue?

ANSWER

This is the result that I was looking for:

map(x2$data, function(df) map(df$data, function(df) df <- mutate(df,increa = cost + 15)))
2
So what exactly is the desired output here? And it seems you have an extra} in your example code.MrFlick
A new column: mutate(increa = cost + 15) @MrFlickArthur Vaz
So you want to keep the column called "data" that contains tibbles each with one column also named "data" that will have three columns called "factory", "cost" and "increa"? This is a very weird structure. Are you sure you want the doubly nested data elements?MrFlick
Yes @MrFlick, that's exactly what I need. The first nest () is just a way to create many scene, changing just a few parametersArthur Vaz
I think you are looking for something like x2 %>% mutate(data = map(data, test)), but the problem is that your datasets in the data column don't have a data column themselves and thus the test function is not working--- If you remove the $data in your test function does it give you your desired output?kath

2 Answers

1
votes

To get your desired output I think it is easier first to extract the level of information you want to have and then calculate the new column. If you on the other hand want to manipulate the data in this structure and preserve this, than a nested call of map and mutate is necessary-

library(tidyverse)

First solution - extract information and then calculate new column:
We can get to desired level of information with

map(x2$data, ~ .x$data) 

# [[1]]
# [[1]][[1]]
# # A tibble: 4 x 2
#    cost type 
#   <dbl> <chr>
# 1    21 a    
# 2    30 b    
# 3    44 c    
# 4   100 d    
# 
# 
# [[2]]
# [[2]][[1]]
# # A tibble: 4 x 2
#    cost type 
#   <dbl> <chr>
# 1    21 a    
# 2    30 b    
# 3    44 c    
# 4   100 d    
#
# ...

As this is a nested list structure a second map is needed to calculate the new column. Here the mutate-function is applied to each of the nested data entries with the additional specification to create a new column inc.

map(x2$data, ~ map(.x$data, mutate, inc = cost + 15)) 

# [[1]]
# [[1]][[1]]
# # A tibble: 4 x 3
#    cost type    inc
#   <dbl> <chr> <dbl>
# 1    21 a        36
# 2    30 b        45
# 3    44 c        59
# 4   100 d       115
# 
# 
# [[2]]
# [[2]][[1]]
# # A tibble: 4 x 3
#    cost type    inc
#   <dbl> <chr> <dbl>
# 1    21 a        36
# 2    30 b        45
# 3    44 c        59
# 4   100 d       115
#
# ...

The same result would be obtained with an extra function test which takes a data.frame as input parameter and calculates the new column:

test <- function(df){
    mutate(df, increa = cost + 15)
}

map(x2$data, ~ map(.x$data, test)) 

Second solution - Manipulate in place
If you however want to keep this nested structure, then we use mutate on the first data-column with map and again mutate and map:

x2_new <- x2 %>% 
  mutate(data = map(data, function(df1) mutate(df1, data = map(data, test))))

To verify that this worked we again extract the needed information as above:

map(x2_new$data, ~ .x$data) 


# [[1]]
# [[1]][[1]]
# # A tibble: 4 x 3
#    cost type  increa
#   <dbl> <chr>  <dbl>
# 1    21 a         36
# 2    30 b         45
# 3    44 c         59
# 4   100 d        115
# 
# 
# [[2]]
# [[2]][[1]]
# # A tibble: 4 x 3
#    cost type  increa
#   <dbl> <chr>  <dbl>
# 1    21 a         36
# 2    30 b         45
# 3    44 c         59
# 4   100 d        115
#
# ...

Third solution - breaks structure but keep information
This is my favourite solution as it turns the data into a tidy format and keeps all information:

x2 %>% 
  unnest(data) %>% 
  unnest(data) %>% 
  mutate(inc = cost + 15)

# A tibble: 16 x 5
#    scene client   cost type    inc
#    <int> <fct>   <dbl> <chr> <dbl>
#  1     1 factory    21 a        36
#  2     1 factory    30 b        45
#  3     1 factory    44 c        59
#  4     1 factory   100 d       115
#  5     2 factory    21 a        36
#  6     2 factory    30 b        45
#  7     2 factory    44 c        59
#  8     2 factory   100 d       115
#  9     3 factory    21 a        36
# 10     3 factory    30 b        45
# 11     3 factory    44 c        59
# 12     3 factory   100 d       115
# 13     4 factory    21 a        36
# 14     4 factory    30 b        45
# 15     4 factory    44 c        59
# 16     4 factory   100 d       115

Data

generic_data <- structure(
  list(client = structure(1L, .Label = "factory", class = "factor"), 
       data = list(structure(list(cost = c(21, 30, 44, 100), 
                                  type = c("a", "b", "c", "d")), 
                             row.names = c(NA, -4L), 
                             class = c("tbl_df", "tbl", "data.frame")))), 
  row.names = c(NA, -1L), class = c("tbl_df", "tbl", "data.frame"))

x2 <- structure(
  list(scene = 1:4, 
       data = list(generic_data, generic_data, generic_data, generic_data)), 
  row.names = c(NA, -4L), class = c("tbl_df", "tbl", "data.frame"))
0
votes

Based on your description, I think you're looking for map_depth

From the documentation https://purrr.tidyverse.org/reference/map_if.html:

map_depth(x, 2, fun) is equivalent to x <- map(x, ~ map(., fun))

which looks like the answer/solution you settled on.