【问题标题】:Iterating/looping over a list迭代/循环列表
【发布时间】:2020-01-15 09:42:29
【问题描述】:

我有一些街道数据,我运行了这个 R 代码来获取列表中 38 个 csv 文件的内容(将来会添加更多文件):

    common_path  <- "0_data/source_data/DB/Speed/"
    csv_files <- list.files(
    path = common_path,        # directory to search within
    pattern = ".*(1|2).*csv$", # 
    recursive = TRUE,          # search subdirectories
    full.names = TRUE          # return the full path
    )
    data_lst = lapply(csv_files, read.csv2)

他们的头长这样:

Data Example

这是可重现格式的数据帧的头部:

structure(list(typ = c(100L, 100L, 100L, 100L, 100L, 100L, 100L, 
100L, 100L, 1L, 1L, 1L, 1L, 1L, 1L), date.and.time = structure(c(1L, 
1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 3L, 4L, 5L, 6L, 7L), .Label = c("2019/11/07 18:07:27.000", 
"2019/11/07 18:07:36.290", "2019/11/07 18:07:40.030", "2019/11/07 18:07:41.930", 
"2019/11/07 18:07:43.720", "2019/11/07 18:07:46.380", "2019/11/07 18:07:54.010"
), class = "factor"), speed..km.h. = c(NA, NA, NA, NA, NA, NA, 
NA, NA, NA, 42L, 44L, 43L, 42L, 41L, 43L), length..m. = c(NA, 
NA, NA, NA, NA, NA, NA, NA, NA, 3.2, 4.2, 3.2, 3.9, 3.7, 3.2), 
    range..m. = c(NA, NA, NA, NA, NA, NA, NA, NA, NA, 0L, 0L, 
    0L, 0L, 0L, 0L), notes = c("Serial No = 1", "Direction = NORTH", 
    "Counting type = SINGLE LANE", "Ref count sense = IN", "Install height = 42 decimeter", 
    "Axis distance = 58 decimeter", "Road type = STANDARD", "Road slope = FLAT", 
    "Start of campain", "", "", "", "", "", "")), row.names = c("1", 
"2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", 
"14", "15"), class = "data.frame")

我想做的是:

  • 从“notes”列中获取前9行的信息

  • 将“注释”列中的信息添加为单独的变量

  • 之后删除前 9 行或 bascally 所有行 “典型”列 == 100

我可以毫无问题地手动为列表中的对象执行此操作,如下面的代码所示:

data_lst[[1]]$serial <- data_lst[[1]]$notes[1]
data_lst[[1]]$direction <- data_lst[[1]]$notes[2]
data_lst[[1]]$lane <- data_lst[[1]]$notes[3]
data_lst[[1]]$install_height <- data_lst[[1]]$notes[5]
data_lst[[1]]$axis <- data_lst[[1]]$notes[6]
data_lst[[1]]$notes <- NULL 
data_lst[[1]] <- data_lst[[1]][-c(1:9),]

但是当我尝试循环这个过程时会出现问题,因为我对循环非常缺乏经验。我做了这样的事情,

for(i in data_lst){
  data_lst[[i]]$serial <- data_lst[[i]]$notes[1]
}

从我的数据中获取“串行”信息,但我得到了这个错误:

error:
in data_lst[[i]] : invalid subscript type 'list'

热烈欢迎任何帮助:)

【问题讨论】:

  • 将该图像粘贴为文本,阅读可重现的示例。

标签: r list loops


【解决方案1】:

如果您想为列表中的每个条目做一些相当复杂的事情,最好通过编写一个函数来分离出您希望应用于每个条目的逻辑。这使您的代码更具可读性、更模块化,并且将来更易于调试或修改。

在您的情况下,您可以编写一个函数来对列表中的每个数据框进行操作,以创建不同组件的命名列表:您想要的所有命名注释,以及修改后的数据框。也许是这样的:

change_data_frame_to_named_list <- function(old_frame)
{
  return(list(serial         = old_frame$notes[1],
              direction      = old_frame$notes[2],
              lane           = old_frame$notes[3],
              install_height = old_frame$notes[5],
              xaxis          = old_frame$notes[6],
              data           = old_frame[-which(old_frame$type == 100), -6]
              ))
}

现在您要做的就是将此函数应用于列表中的所有元素。在 R 中最惯用的方法是根本不使用循环,而是使用 lapply(列表应用的缩写)。这将列表作为第一个参数,将您希望应用于每个元素的函数作为第二个参数。

这意味着你可以这样做:

result <- lapply(data_lst, change_data_frame_to_named_list)

这相当于一个循环的版本,但更短更整洁。

如果你真的想把它作为一个循环来做,相当于:

result <- list()
for (i in seq_along(data_lst))
{
  result[[i]] = change_data_frame_to_named_list(data_lst[[i]])
}

在任何一种情况下,变量result 都是与data_lst 长度相同的列表,其中每个条目本身就是一个命名列表,包含您的新数据框及其关联的命名注释。

编辑


OP 请求了一个类似的方法,以他已经用他的手写循环制作的格式返回数据。这是如何实现的。由于逻辑被分离到函数中,我们只需要改变函数本身:

change_data_frame <- function(old_frame)
{
  old_frame$serial         <- old_frame$notes[1]
  old_frame$direction      <- old_frame$notes[2]
  old_frame$lane           <- old_frame$notes[3]
  old_frame$install_height <- old_frame$notes[5]
  old_frame$xaxis          <- old_frame$notes[6]
  old_frame$notes          <- NULL

  return(old_frame[-which(old_frame$typ == 100),])    
}

# Now you just do as you did before
result <- lapply(data_lst, change_data_frame)

# and to get all dfs into one big data frame...
do.call("rbind", result)

【讨论】:

  • 感谢这似乎比循环更有效!是否还有一种平滑的方法可以将所有列表元素组合在一个巨大的小标题/数据框中?我使用 data_lst %&gt;% reduce(full_join) ,这需要很长时间并且会产生很多警告
  • 是的,奥斯卡。在这种情况下,你会做do.call("rbind", lapply(result, function(x) x$data))。这个大数据框不会包含所有单独的注释:这些在逻辑上不属于数据框的任何行。
  • 谢谢艾伦。问题是,我需要将此信息保存到文件中。现在我将它作为每个数据帧中的变量。这在逻辑上似乎是错误的,但它对我有用,而且我在每一行都有所有信息。我对我现在得到的结果很满意。但是,通往它的道路可能会更顺畅。
  • @OscarThees 如果你想这样做,请参阅我的更新。
【解决方案2】:

您执行 for 循环的方式,i 是一个列表,而不是一个索引。如果你想要索引,你应该使用函数seq_along,为你的列表返回一个索引向量。检查这个例子:

> l = list("apple", "banana", "carrot")
> l
[[1]]
[1] "apple"

[[2]]
[1] "banana"

[[3]]
[1] "carrot"

> for(p in l) print(p)
[1] "apple"
[1] "banana"
[1] "carrot"
> for(i in seq_along(l)) print(i)
[1] 1
[1] 2
[1] 3

【讨论】:

    【解决方案3】:

    在 for 循环中,您始终必须指定循环的开始位置和结束位置。您想遍历列表中的每个元素,这意味着您需要for (i in seq_along(data_lst))。 运行 seq_along(data_lst) 将创建一个从 1 到列表中元素数的序列。

    【讨论】:

      猜你喜欢
      • 2017-04-30
      • 1970-01-01
      • 2017-09-22
      • 2021-07-12
      • 2021-04-11
      • 1970-01-01
      • 1970-01-01
      • 2016-09-04
      相关资源
      最近更新 更多