根据 R 中的列表长度动态创建嵌套的 FOR 循环

Dynamically creating nested FOR loops based on the list length in R

假设我有一个这样的数据集dt

meta_cat cat sku price sales
bakery bread 796590 22.6 24
bakery bread 796595 19.8 20
bakery doughnut 796588 30.6 36
bakery sandwich 796640 45.9 42
bakery sandwich 796643 43.3 45
fruits feijoa 645342 97.2 5
fruits orange 645675 35.7 78
fruits orange 645677 43.9 65
fruits feijoa 645342 92.9 11

此外,我有一个如下所示的列表,例如:

lvl_list <- list(c("meta_cat"),   
                 c("cat"))

我事先不知道列表中会有多少层(列表长度可以是0(空列表),也可以是一、二、三等(在我们的例子中,有两个水平))。列表值对应于数据集中的列名称。

我的任务是运行基于列表长度的嵌套for循环。

如果列表为空,则不开始循环并执行main code
如果list length = 1,应该有1个这样的for循环:

for(i in unique(dt[[lvl_list[[1]]]])){  
    dt <- dt[get(lvl_list[[1]]) == I,] # make subset     
       # run main code   
       # .
       # .
       # main code
     }
   }

因此,在第一次迭代中,我们通过 meta_cat 列的第一个唯一值过滤 dt(例如,仅选择 meta_cat = "bakery" 的记录)和 运行 main code 在此 dt.

如果列表的长度 = 2,我们应该得到 2 个 for 循环:

for(i in unique(dt[[lvl_list[[1]]]])){
     dt <- dt[get(lvl_list[[1]]) == i, ] # filter dt
    
     for(j in unique(dt[[lvl_list[[2]]]])){
       dt <- dt[get(lvl_list[[2]]) == j, ] # filter dt again
       # run main code   
       #  .   
       #  .   
       # main code   
     }
   }

所以,这里我们按两列的值过滤 dt。 变量meta_cat有2个唯一值,cat变量有5个唯一值
代码执行的逻辑应该如下:在第一次迭代中,我们通过 meta_cat 的第一个值过滤 dt (留在 dt 观察中,其中 meta_cat = "bakery") ,在第二个循环的第一次迭代中,我们通过 cat 变量的第一个值过滤 dt(我们将选择 cat = "bread" 的观察值)。因此,我们得到 dt,其中 meta_cat = "bakery"cat = "bread"。此外,此过滤后的 dt 用作建模代码的输入。 在第二次迭代中,原始 dtmeta_cat = "bakery"cat = "doughnut" 过滤。然后主要代码执行到这个dt,结束等等

如果列表中有 3 个级别,我们应该有 3 个 for 循环,等等

我的问题:是否可以根据列表长度动态创建嵌套 for 循环?
对于如何实施它的任何帮助,我将不胜感激。

使用 split

可能更容易
lst1 <- lapply(split(dt, dt[[lvl_list[[1]]]]), function(x) 
         split(x, x[[lvl_list[[2]]]]))

此外,由于这是一个递归拆分,使用 collapse 中的 rsplit,默认情况下会进行递归拆分和 returns 嵌套列表`

library(collapse)
lst2 <- rsplit(dt, by = dt[, unlist(lvl_list), with = FALSE])

数据

dt <- structure(list(meta_cat = c("bakery", "bakery", "bakery", "bakery", 
"bakery", "fruits", "fruits", "fruits", "fruits"), cat = c("bread", 
"bread", "doughnut", "sandwich", "sandwich", "feijoa", "orange", 
"orange", "feijoa"), sku = c(796590L, 796595L, 796588L, 796640L, 
796643L, 645342L, 645675L, 645677L, 645342L), price = c(22.6, 
19.8, 30.6, 45.9, 43.3, 97.2, 35.7, 43.9, 92.9), sales = c(24L, 
20L, 36L, 42L, 45L, 5L, 78L, 65L, 11L)), row.names = c(NA, -9L
), class = c("data.table", "data.frame"))