-1

I am trying to create a nested dictionary starting from a data.frame.

dta <- data.frame(x = c('A','B','A','B'),
                  y = c('P','Q','Q','P'),
                  z = c(89,12,34,56)
                  ) |> print()
    #>   x y  z
    #> 1 A P 89
    #> 2 B Q 12
    #> 3 A Q 34
    #> 4 B P 56
    

The following is not what I am looking for.

  as.list(dta)
    #> $x
    #> [1] "A" "B" "A" "B"
    #> $y
    #> [1] "P" "Q" "Q" "P"
    #> $z
    #> [1] 89 12 34 56

What I am trying to do is to create a lookup table using make_nested_list such that nested_dta = make_nested_list(dta) and nested_dta['A']['P'] will output 89. This answer is close but not useful for my case. I am trying to do this in base R so that it is more readable. dplyr can do this but seems wordy library(tidyvserse);dta %>% filter(x=='A',y=='P') %>% pull(z). There are other use cases that may benefit from the base R formulation. Is there is a better way to create the nested dictionary? Thank you.

jay.sf
  • 60,139
  • 8
  • 53
  • 110
Stat-R
  • 5,040
  • 8
  • 42
  • 68

4 Answers4

2

You may define a custom function


dta <- data.frame(x = c('A','B','A','B'), y = c('P','Q','Q','P'), z = c(89,12,34,56) )

lt <- function(x, y, df = dta){
  return(df[df[1]==x & df[2] == y, 3])
}

lt('A', 'P')

[1] 89

Still if you want creating a nested list, a tidyverse option can be

dta <- data.frame(x = c('A','B','A','B'), y = c('P','Q','Q','P'), z = c(89,12,34,56) )

library(tidyverse)
dta %>%
  split(.$x, drop = TRUE) %>%
  map(~ split(.x, .x$y, drop = T)) %>%
  map_depth(2, ~.x %>% pull(z)) -> nested_list

nested_list
#> $A
#> $A$P
#> [1] 89
#> 
#> $A$Q
#> [1] 34
#> 
#> 
#> $B
#> $B$P
#> [1] 56
#> 
#> $B$Q
#> [1] 12

Created on 2021-06-27 by the reprex package (v2.0.0)

AnilGoyal
  • 25,297
  • 4
  • 27
  • 45
2

You can achieve the desired nested list using setNames and Map.

nested_list <- Map(setNames, setNames(as.list(dta$z), dta$x), dta$y)
nested_list

#$A
# P 
#89 

#$B
# Q 
#12 

#$A
# Q 
#34 

#$B
# P 
#56 

Check output -

nested_list[['A']][['P']]
#[1] 89

nested_list[['B']][['Q']]
#[1] 12
Ronak Shah
  • 377,200
  • 20
  • 156
  • 213
2

Using split in by.

make_nested_list <- function(d) by(d, dta$x, \(b) split(b$z, b$y))
nested_dta <- make_nested_list(dta)
nested_dta[['A']][['P']]
# [1] 89
nested_dta$A$P
# [1] 89
jay.sf
  • 60,139
  • 8
  • 53
  • 110
  • Thanks. The generated list supports both `$` and `[[`. How would one define a general function that would take any number of columns and make a (multi)nested list – Stat-R Jun 29 '21 at 04:53
  • @Stat-R Good question, perhaps you might want to make examples of such possible lists, function usage, and output. – jay.sf Jun 29 '21 at 09:27
1

For the sake of completeness, a less wordy approach to query a lookup table can be achieved using a keyed data.table:

library(data.table)
setDT(dta, key = c("x", "y"))

dta[.("A", "P"), z]

[1] 89

Uwe
  • 41,420
  • 11
  • 90
  • 134