【问题标题】:Applying a loop to a list of lists in r将循环应用于 r 中的列表列表
【发布时间】:2020-11-20 15:24:51
【问题描述】:

我有数据框(Jdef),例如:

FocalID   Sex    Date    Time    Year_record Month_record Age Date_record
Adelaide   F 2014-06-22 07:41:04        2014            6 103      6 2014
Adelaide   F 2015-08-22 11:53:17        2015            8 117      8 2015
Adelaide   F 2014-06-16 12:30:18        2014            6 103      6 2014
Adelaide   F 2014-06-11 11:58:28        2014            6 103      6 2014
Adelaide   F 2014-07-05 14:10:23        2014            7 104      7 2014
Adelaide   F 2014-06-20 11:41:34        2014            6 103      6 2014
Adelaide   F 2014-06-04 09:39:34        2014            6 103      6 2014
Adelaide   F 2015-09-24 14:21:51        2015            9 119      9 2015
Adelaide   F 2015-08-15 13:09:07        2015            8 117      8 2015
Adelaide   F 2015-09-16 09:06:16        2015            9 118      9 2015

我使用以下方法创建了一个列表列表:

Jdef$Date_record = paste(Jdef$Month_record, Jdef$Year_record)

listJ = split(Jdef, Jdef$Date_record)  
listJi = lapply(listJ, function(x) split(x, x$Age))
listJii = lapply(listJi, function(x) sapply(x, function(x) split(x, x$FocalID)))

现在我想向listJii 的每个元素添加从Jdef 中获取的同一月份和年份(与listJii 的每个元素中列出的记录相同的月份和年份)的所有记录。我使用了以下内容:

data_to_add = list()
for(i in 1:length(listJii)){
data_to_add[[i]] <- unique(listJii[[i]]$Date_record)
}

listJ_subs = list()
for(i in 1:length(data_to_add)){
listJ_subs[[i]] <- subset(Jdef, Jdef$Date_record %in% data_to_add[[i]])
}

但是,循环不起作用,表示索引超出范围(data_to_add 列表结果为空)。我不明白为什么。这是否与 listJii 是列表列表这一事实有关??

【问题讨论】:

  • 请务必使用dput() 提供可重现且易于管理的示例。
  • 您好,感谢您的提示。正如您所建议的,我已经编辑了帖子,添加了指向使用 dput() 提取的原始 df 的链接。
  • 我看不到。最好用head( , n = 10) 左右发布一小部分样本。
  • data_to_add 为空,因为 listJii[[i]]$Date_record 不存在。您的数据框位于列表结构的三个级别内,因此您需要更深入地了解列表结构。例如,要获得唯一的数据记录,您需要更多的 for 循环来处理 listJii[[i]][[j]][[k]]$Data_record 之类的内容
  • 是的,我一直以为问题是列表的多级结构,但我不知道如何解决它......

标签: r list loops


【解决方案1】:

这里的问题是data_to_add 是空的。它是空的,因为listJii[[i]]$Date_record 不存在。因为列表列表(即嵌套列表)可能比简单列表或数据框更令人困惑,所以您需要遍历列表结构的层级(从 outside in)找到您寻找的向量的位置。在这种情况下,Date_recordlistJii[[i]][[j]] 中。

如何构建嵌套循环来计算嵌套列表中的向量?

让嵌套循环正确的一个技巧是由内而外

第 1 步。 确保核心功能正常工作,而不使用循环。
使用嵌套列表中的样本向量测试命令行。

unique(listJii[[1]][[1]]$Date_record) # this is what you want to store in `data_to_add`

# you have two layers of lists in `listJii`, so you need two list layers in `data_to_add`
data_to_add <- list()
data_to_add[[1]] <- list()
data_to_add[[1]][[1]] <- unique(listJii[[1]][[1]]$Date_record) 

# test
data_to_add # you needed all those commands above to make this work, so those will be necessary in your function

第 2 步。 构建您的循环,一次一个,首先替换需要更改的最里面的变量,然后解决问题。一路测试它的每一步。

## innermost loop test
data_to_add <- list()
data_to_add[[1]] <- list()
for(j in seq_along(listJii[[1]])) {
    data_to_add[[1]][[j]] <- unique(listJii[[1]][[j]]$Date_record)
}

# test
data_to_add # the innermost loop works


## outer loop test
data_to_add <- list()
for(i in seq_along(listJii)) {
    data_to_add[[i]] <- list()
    for(j in seq_along(listJii[[i]])) {
        data_to_add[[i]][[j]] <- unique(listJii[[i]][[j]]$Date_record)
    }
}

# test
data_to_add # the final loop works--this function is now ready

您的listJsubs 函数与此处构建的data_to_add 一起使用。不过,由于在您的评估中使用了嵌套列表,您可能还需要考虑使用嵌套循环来跟进 listJsubs


在嵌套列表上使用嵌套循环的替代方法是使用嵌套 lapply()

lapply() 是另一个函数,您可以使用它来代替列表上的 for 循环。
它的输出是一个列表,因此您不需要像 for 循环那样事先将空列表结构分配给 data_to_adddata_to_add[[i]]

data_to_add <- lapply(listJii, function(i){
    lapply(i, function(j){
        return(unique(j$Date_record))
    })
})

注意:这个嵌套 lapply() 中的核心函数与嵌套 for 循环中使用的函数有点不同。这是一种不同的方法,我可以使用lapply() 并获得相同的结果。我实际上是从 outside in 构建的——因此,您在核心函数中看不到任何 i

【讨论】:

  • 就像 OP 已经在日期拆分 data.frame 一样,我们现在的事实是,每个拆分都会有一个唯一的值,即第一行的值
  • 谢谢@LC-datascientist!这是一个非常清楚的解释!您认为在新列表“listJ_subs”中可以保持与原始“listJii”中相同的元素名称吗?也就是说,第一个元素级别将具有日期记录名称,第二个元素级别将具有年龄编号,第三个元素级别将具有 FocalID 名称。
  • @VittoriaRoatti 将 lapply 替换为 sapply
  • @Abdessabour Mtk 谢谢!也许我正在使自己的生活复杂化,但我试图用 for 循环而不是 lapply 来做到这一点,因为这是迄今为止有效的循环。但是,也许您有不同的解决方案来将数据“添加”到列表中(请参阅下面的我的 cmets)。
  • 在'listJ_subs'的for循环中,您可以将listJ_subs[[i]]替换为listJ_subs[[names(listJii)[i]]]
【解决方案2】:

贾斯塔tidyverse解决方案:

library(tidyverse)
Jdef %>% 
 nest_by(Date_record) %>%
  inner_join(Jdef %>% nest_by(Date_record, Age, FocalID, .key="Focalised")) -> nested

每一行相当于列表的一个元素。

# A tibble: 1,043 x 5
# Rowwise:  Date_record
   Date_record                 data   Age FocalID             Focalised
   <chr>       <list<tbl_df[,106]>> <dbl> <chr>    <list<tbl_df[,104]>>
 1 10 2018              [262 × 106]    26 Moussaka            [5 × 104]
 2 10 2018              [262 × 106]    27 Moussaka            [2 × 104]
 3 10 2018              [262 × 106]    33 Etosha              [2 × 104]
 4 10 2018              [262 × 106]    34 Etosha              [4 × 104]
 5 10 2018              [262 × 106]    35 Ishiguro            [1 × 104]
 6 10 2018              [262 × 106]    36 Houston             [1 × 104]
 7 10 2018              [262 × 106]    36 Ishiguro            [5 × 104]
 8 10 2018              [262 × 106]    36 Seuss               [1 × 104]
 9 10 2018              [262 × 106]    37 Houston             [4 × 104]
10 10 2018              [262 × 106]    37 Seuss               [5 × 104]
# … with 1,033 more rows

使用tidyverse 循环遍历每一行并附加

nested %>%
   pmap(function(Date_record, data, Age, FocalID,  Focalised) 
        bind_rows(Focalised, data) %>% mutate(Date_record=Date_record) %>% replace_na(list(Age=Age,FocalID=FocalID))) -> res
res[[1]]
# A tibble: 262 x 107
   Sex   Date       Time  Year_record Month_record Troop ObsNumber ScanType Observer DOB_corrected MethDOB Year_birth Month_birth Age3mo Mother Parity NoInfants Absolute_rank
   <chr> <date>     <chr>       <dbl>        <dbl> <chr>     <dbl> <chr>    <chr>    <date>        <chr>        <dbl>       <dbl>  <dbl> <chr>  <chr>  <chr>             <dbl>
 1 M     2018-10-24 11:4…        2018           10 J            15 10mScan  Charlot… 2016-07-26    BA            2016           7      9 Lusaka multi  5                     5
 2 M     2018-10-04 14:1…        2018           10 J            26 10mScan  Charlot… 2016-07-26    BA            2016           7      9 Lusaka multi  5                     5
 3 M     2018-10-23 10:3…        2018           10 J            16 10mScan  Charlot… 2016-07-26    BA            2016           7      9 Lusaka multi  5                     5
 4 M     2018-10-04 07:1…        2018           10 J             7 10mScan  Charlot… 2016-07-26    BA            2016           7      9 Lusaka multi  5                     5
 5 M     2018-10-14 08:1…        2018           10 J            13 10mScan  Charlot… 2016-07-26    BA            2016           7      9 Lusaka multi  5                     5
 6 M     2018-10-14 14:3…        2018           10 J            34 10mScan  Charlot… 2014-07-23    BE            2014           7     17 Preto… multi  4                    11
 7 M     2018-10-26 08:1…        2018           10 J            13 10mScan  Charlot… 2014-07-23    BE            2014           7     17 Preto… multi  4                    11
 8 M     2018-10-24 07:4…        2018           10 J             5 10mScan  Charlot… 2014-07-23    BE            2014           7     17 Preto… multi  4                    11
 9 M     2018-10-23 07:1…        2018           10 J             3 10mScan  Charlot… 2014-07-23    BE            2014           7     17 Preto… multi  4                    11
10 M     2018-10-30 08:2…        2018           10 J            21 10mScan  Charlot… 2014-07-23    BE            2014           7     17 Preto… multi  4                    11

此外,如果您只想获取日期 split 已经将 thos 放在元素的名称中,那么:

rep(names(listJii), lengths(listJii))

purrr:

listJii %>% map(map,  map, `[[`, 1, "Date_record")

baseR:

lapply(listJii, lapply, lapply, `[`, 1, "Date_record")

有些拆分不是data.frames,这可能会引发一些错误,这也是rep 解决方案不等同于purrr|baseR 的原因

【讨论】:

  • @VittoriaRoatti 你没有提到你想如何“添加”记录
  • 是的,抱歉,可能不清楚。在创建 listJii 和 data_to_add 之后,我想在 listJii 的每个 FocalID 子元素中添加同一年和同一年的所有记录。我需要它,因为然后我将应用社交网络功能,并且为了分析社交网络随时间的变化,我需要一个列表列表,其中每个 FocalID 在每个 Age 和 Date_record 期间都有自己的网络。因此,我想通过循环添加数据并使用 data_to_add 创建 listJ_subs,其中包含 Jdef 中与 data_to_add 相同月/年获取的所有记录。
  • 另外,您的 tidyverse 解决方案看起来很棒(而且很简单),但输出与我原来的 listJii(使用 lapply 创建)的输出不同,所以我不确定这是否只是一种不同的可视化方式列表或列表结构是否不同......(对不起,我是列表新手......)。实际上我应该注意,在这种情况下,“添加”是一个令人困惑的术语,因为我没有添加,而是用数据填充新 listJ_subs 的元素......
  • @VittoriaRoatti 是否需要排除具有相同FocalID 的记录?
  • @VittoriaRoatti 我认为您尝试做的事情是多余的,这是因为最后一个代码基本上只是复制在该date_record 中创建的数据;聚焦列的每一行也相当于嵌套列表中的一个元素。
猜你喜欢
  • 1970-01-01
  • 2022-01-03
  • 2023-02-02
  • 2016-09-21
  • 1970-01-01
  • 2020-03-28
  • 1970-01-01
  • 1970-01-01
  • 2019-05-08
相关资源
最近更新 更多