R 递归地从嵌套的csv文件导入数据,并从文件名创建一个ID列,其中包含月份和年份

R 递归地从嵌套的csv文件导入数据,并从文件名创建一个ID列,其中包含月份和年份,r,tidyverse,R,Tidyverse,我从一堆房子的一堆房间里得到了一些空气质量数据。我想从嵌套在具有以下文件结构的文件夹中的.csv文件中递归导入数据。我遇到的问题是,虽然我可以导入没有文件夹或文件名、房间名或日期名的列表,但我无法确定如何提取房间名、月份或年份,并将其属性化为创建的列表,以便我可以操作用于绘图等的数据 是否有人可以帮助从文件/文件夹名称中提取门牌号、房间名、月份和年份,并使用rbind创建一个data.frame,其中ID列表示门牌号、房间、月份和年份 此代码适用于一个文件夹: filenames <- l

我从一堆房子的一堆房间里得到了一些空气质量数据。我想从嵌套在具有以下文件结构的文件夹中的.csv文件中递归导入数据。我遇到的问题是,虽然我可以导入没有文件夹或文件名、房间名或日期名的列表,但我无法确定如何提取房间名、月份或年份,并将其属性化为创建的列表,以便我可以操作用于绘图等的数据

是否有人可以帮助从文件/文件夹名称中提取门牌号、房间名、月份和年份,并使用rbind创建一个data.frame,其中ID列表示门牌号、房间、月份和年份

此代码适用于一个文件夹:

filenames <- list.files( pattern="*.csv", full.names=TRUE,recursive = T)
ldf <- lapply(filenames, read.csv)
每个csv文件如下所示:

编辑:包括所需的输出。 理想情况下,我想要的输出是带有列的data.frame: 时间,日期,var1,var2,var2,houseNum,roomName

这是我根据阿克塞拉的回答得出的结论:
使用您提供的sharepoint链接,我制作了一个压缩文件,该文件捕获了完整数据集中的大部分异常情况。主要的问题是data.frames是空的,并且它们并不难找到,即使不是所有的文件名中都没有数据。虽然丢弃空的data.frames很容易,但我选择通过填充一行NAs来保留它们。如果这些行被证明是一个讨厌的东西,它们很容易在以后移除。 我已经在完整的数据集上尝试过了,效果很好

样本数据 处理
使用您提供的sharepoint链接,我制作了一个压缩文件,该文件捕获了完整数据集中的大部分异常情况。主要的问题是data.frames是空的,并且它们并不难找到,即使不是所有的文件名中都没有数据。虽然丢弃空的data.frames很容易,但我选择通过填充一行NAs来保留它们。如果这些行被证明是一个讨厌的东西,它们很容易在以后移除。 我已经在完整的数据集上尝试过了,效果很好

样本数据 处理
下面的内容应该能让你接近。在purrr的映射中导入带有readr的read_csv的csv。使用路径设置列表名称,然后使用argument.id=path绑定数据帧,以包含具有列表元素名称的列。用tidyr的分离键分离路径。使用stringr的str_Remove和其他变量作为模式从文件名中删除冗余字符串。最后,使用另一个对分离的调用来分离剩下的文件名:

图书馆管理员 路径% 设置\u名称路径%>% bind_rows.id=路径%>% 分离路径、chouse、文件室、文件,/%>% mutatefile=文件%>% str_Removegexhouse,T%>% str_拆卸器机房,T%>% str_删除\\.csv%>% Stru_trim, house=parse_numberhouse %>% 分离文件,cmonth,year,convert=T 使用一些合成数据返回以下内容:

一个tibble:4x8 房间月份年份时间变量1变量2变量3 1 1厨房2019年4月02:00诸如此类 2 1客厅2018年6月12:00诸如此类 3 2厨房2019年7月08:00诸如此类 42客厅2016年1月16:00等等
下面的内容应该能让你接近。在purrr的映射中导入带有readr的read_csv的csv。使用路径设置列表名称,然后使用argument.id=path绑定数据帧,以包含具有列表元素名称的列。用tidyr的分离键分离路径。使用stringr的str_Remove和其他变量作为模式从文件名中删除冗余字符串。最后,使用另一个对分离的调用来分离剩下的文件名:

图书馆管理员 路径% 设置\u名称路径%>% bind_rows.id=路径%>% 分离路径、chouse、文件室、文件,/%>% mutatefile=文件%>% str_Removegexhouse,T%>% str_拆卸器机房,T%>% str_删除\\.csv%>% Stru_trim, house=parse_numberhouse %>% 分离文件,cmonth,year,convert=T 使用一些合成数据返回以下内容:

一个tibble:4x8 房间月份年份时间变量1变量2变量3 1 1厨房2019年4月02:00诸如此类 2 1客厅2018年6月12:00诸如此类 3 2厨房2019年7月08:00诸如此类 42客厅2016年1月16:00等等 如果在包含所有房屋目录的目录中运行list.files命令,则可以将所有CSV作为数据帧导入到列表中,并且列表的名称是完整的文件路径。如果然后运行dplyr::bind_rows或data.table::rbindlist,则可以将它们组合到一个数据帧中,该数据帧具有完整文件的ID列
路径那么唯一的问题就是如何将文件路径house number、room name和number、date中的信息提取到它们自己的列中,对吗?……要做到这一点,真正重要的唯一输入是作为字符串的文件路径,就像它在list names/ID列中一样,以及您想要的输出。你能分享这两个吗?我不认为其他栏目有什么关系,你分享的图片也没什么用处。文件结构允许我猜测文件路径字符串的外观,但是复制/粘贴比猜测要好得多。如果有你想要的样本输出,那就太好了。e、 例如,您的文件结构在一个名为kitchen的文件夹中有一个名为卧房的文件,您希望新列是什么?您知道dplyr::bind_rowsldf、.id=House_Room这个语法,对吗?请参阅帮助页?bind_rows。提供数据帧列表时,标签取自列表的名称。如果找不到名称,则使用数字序列。如果由于某种原因,您的列表没有命名,请先使用指定了.id参数的bind_行,然后再使用donamesldf=filenames。@HCAI我的意思是建议将bind_行与Gregor建议的组合起来。获取完整目录作为名称,然后使用.id将其作为列。如果在包含所有目录的目录中运行list.files命令,则可能会将所有CSV作为数据帧导入列表中,并且列表的名称是完整文件路径。如果然后运行dplyr::bind_rows或data.table::rbindlist,则可以将它们组合到一个数据帧中,该数据帧具有完整文件路径的ID列。那么唯一的问题就是如何将文件路径house number、room name和number、date中的信息提取到它们自己的列中,对吗?……要做到这一点,真正重要的唯一输入是作为字符串的文件路径,就像它在list names/ID列中一样,以及您想要的输出。你能分享这两个吗?我不认为其他栏目有什么关系,你分享的图片也没什么用处。文件结构允许我猜测文件路径字符串的外观,但是复制/粘贴比猜测要好得多。如果有你想要的样本输出,那就太好了。e、 例如,您的文件结构在一个名为kitchen的文件夹中有一个名为卧房的文件,您希望新列是什么?您知道dplyr::bind_rowsldf、.id=House_Room这个语法,对吗?请参阅帮助页?bind_rows。提供数据帧列表时,标签取自列表的名称。如果找不到名称,则使用数字序列。如果由于某种原因,您的列表没有命名,请先使用指定了.id参数的bind_行,然后再使用donamesldf=filenames。@HCAI我的意思是建议将bind_行与Gregor建议的组合起来。获取完整目录作为名称,然后使用.id将其作为列。谢谢,这看起来很不错。有一些奇怪的命名文件,但我会手动修复它们。我可以问一下我如何使用dtf将这些名字应用到我制作的列表中吗?@HCAI:我添加了一个建议。当然,您可以随意更改格式。我仍然不确定如何将dtf中的元数据绑定到ldf中导入的数据列表。您的ldf是另一个姓名列表。很抱歉问你一个奇怪的问题:我该怎么做?@HCAI:如果你这么做了。callrbind,ldf.bind,这能满足你的需要吗?@HCAI:我已经更新了答案。找到问题并不难,您只需使用一小部分数据运行代码,直到崩溃,然后检查导致崩溃的数据。在本例中,主要问题是一些空的data.frames。谢谢,这看起来不错。有一些奇怪的命名文件,但我会手动修复它们。我可以问一下我如何使用dtf将这些名字应用到我制作的列表中吗?@HCAI:我添加了一个建议。当然,您可以随意更改格式。我仍然不确定如何将dtf中的元数据绑定到ldf中导入的数据列表。您的ldf是另一个姓名列表。很抱歉问你一个奇怪的问题:我该怎么做?@HCAI:如果你这么做了。callrbind,ldf.bind,这能满足你的需要吗?@HCAI:我已经更新了答案。找到问题并不难,您只需使用一小部分数据运行代码,直到崩溃,然后检查导致崩溃的数据。在这种情况下,主要问题是一些空数据。frames.Wow,这看起来也不错,谢谢。我试图逐行运行,因为它会在第一个map函数中生成警告。警告:7639解析失败。A行TIBLE:5 x 5列row col预期实际文件预期实际1 1 NA 8列9列'./House 01 DOMBOLY 2018年10月.csv'。这一定是因为文件看起来像什么,对吗?很抱歉在这里问这种类型的问题。@HCAI这是因为我包含了参数col_types=cccc,所以read_csv需要四个字符的变量。我将从代码中删除它,以便您可以再试一次,让read_csv猜出类。@HCA
我做了一些修改以保持灵活性。这包括删除创建日期时间的部分。问题是时间变量不能按原样工作。我认为代码现在可以正常工作,不会抛出任何错误。再次感谢。我一行一行地走,却被困在了另一条路上。我进入了House 1卧房,在目录中留下了一个文件:>路径[1]House 01卧房2019年4月.csv。但是路径条目中没有/。有什么想法吗?啊,我明白了。看看路径,它实际上有点混乱:路径[1]House 01 foobot data/House 01卧房/House 01卧房2019年4月.csv,所以尽管我很喜欢tidyverse的想法,但它仍然有点卡住了。哇,这看起来也不错,谢谢。我试图逐行运行,因为它会在第一个map函数中生成警告。警告:7639解析失败。A行TIBLE:5 x 5列row col预期实际文件预期实际1 1 NA 8列9列'./House 01 DOMBOLY 2018年10月.csv'。这一定是因为文件看起来像什么,对吗?很抱歉在这里问这种类型的问题。@HCAI这是因为我包含了参数col_types=cccc,所以read_csv需要四个字符的变量。我将从代码中删除它,以便您可以再试一次,让read_csv猜测类。@HCAI我做了一些修改以保持灵活性。这包括删除创建日期时间的部分。问题是时间变量不能按原样工作。我认为代码现在可以正常工作,不会抛出任何错误。再次感谢。我一行一行地走,却被困在了另一条路上。我进入了House 1卧房,在目录中留下了一个文件:>路径[1]House 01卧房2019年4月.csv。但是路径条目中没有/。有什么想法吗?啊,我明白了。从路径上看,它实际上有点混乱:路径[1]House 01 foobot data/House 01卧房/House 01卧房2019年4月.csv,因此它仍然有点难以理解,尽管我确实喜欢tidyverse的想法。
df <- dplyr::bind_rows(ldf)
df <- purrr::map_df(ldf, dplyr::bind_rows)
df <- purrr::map_df(ldf, ~.x)
.
├── House 01
|   ├── Kitchen
|   |   ├──House 01 kitchen Apr 2019.csv
|   |   ├──House 01 kitchen December 2019.csv
|   |   ├──House 01 kitchen February 2018.csv
|   └── Living room
|   |   ├──House 01 living room Apr 2019.csv
|   |   ├──House 01 living room December 2019.csv
|   |   ├──House 01 living room February 2018.csv
├── House 02
|   ├── Kitchen
|   |   ├──House 02 kitchen Apr 2019.csv
|   |   ├──House 02 kitchen December 2019.csv
|   |   ├──House 02 kitchen February 2018.csv
|   └── Living room
|   |   ├──House 02 living room Apr 2019.csv
|   |   ├──House 02 living room December 2019.csv
|   |   ├──House 02 living room February 2018.csv
filenames <- list.files( pattern="*.csv", full.names=TRUE,recursive = T)
  
   >filenames
     [1] "./House 01 Bedroom/House 01 bedroom Apr 2019.csv"             
     [2] "./House 01 Bedroom/House 01 bedroom December 2018.csv"        
     [3] "./House 01 Bedroom/House 01 bedroom February 2019.csv"        
     [4] "./House 01 Bedroom/House 01 bedroom January 2018.csv"         
     [5] "./House 01 Bedroom/House 01 bedroom March 2019.csv"           
     [6] "./House 01 Bedroom/House 01 bedroom May 2019.csv"             
     [7] "./House 01 Bedroom/House 01 bedroom November 2018.csv"        
     [8] "./House 01 Bedroom/House 01 bedroom October 2018.csv"         
     [9] "./House 01 Kitchen/House 01 kit Apr 2019.csv"                 
    [10] "./House 01 Kitchen/House 01 kit May 2019.csv"                 
    [11] "./House 01 Kitchen/House 01 kitchen December 2018.csv"        
    [12] "./House 01 Kitchen/House 01 kitchen February 2019.csv"        
    [13] "./House 01 Kitchen/House 01 kitchen January 2019.csv"         
    [14] "./House 01 Kitchen/House 01 kitchen March 2019.csv"           
    [15] "./House 01 Kitchen/House 01 kitchen November 2018.csv"        
    [16] "./House 01 Kitchen/House 01 kitchen October 2018.csv"         
    [17] "./House 01 Living room/House 01 Liv Apr 2019.csv"             
    [18] "./House 01 Living room/House 01 Liv May 2019.csv"             
    [19] "./House 01 Living room/House 01 living room December 2018.csv"
    [20] "./House 01 Living room/House 01 living room February 2018.csv"
    [21] "./House 01 Living room/House 01 living room January 2018.csv" 
    [22] "./House 01 Living room/House 01 living room March 2018.csv"   
    [23] "./House 01 Living room/House 01 living room November 2018.csv"
    [24] "./House 01 Living room/House 01 living room October 2018.csv" 
    > 



ldf <- lapply(filenames, read.csv)
ldf<-lapply(ldf, function(y) { y["X"] <- NULL; y })

dn <- do.call(rbind, strsplit(dirname(filenames), "/")) #extracts month and year from
dn <- dn[,-(1:(ncol(dn)-2))]



colnames(dtf) <- c("House", "Room", "Month", "Year")
dtf$Date <- as.Date(paste(dtf$Month, dtf$Year, 1), "%b %Y %d")
cnamez<-c("Time","DevTime","pm2.5","Temp","RH","CO2","VOC.ppb","allpol")
ldf<-lapply(ldf, setNames, cnamez)



names(ldf)<-dn #Names the data frames 
AllData<-bind_rows(ldf, .id = "ID") #Binds them into data frames

library(stringr)
tmp<-str_split_fixed(AllData$ID, " ", 2) #Splits House number and room
tmp <- tmp[,-c(1,4)] #Removes excess
AllData$House<-tmp[,1] #Assigns house to be first column of tmp
AllData$Room<-tmp[,2]
AllData$ID<-NULL #Gets rid of ID column

head(AllData)
        Time          DevTime     pm2.5    Temp      RH   CO2 VOC.ppb    allpol House    Room
1 1554073200 01/04/2019 00:00  7.320007 18.7700 48.9200 452.0     125  7.320007    01 Bedroom
2 1554073500 01/04/2019 00:05  7.550003 18.7595 48.9190 451.0     125  7.550003    01 Bedroom
3 1554073800 01/04/2019 00:10  8.240021 18.7270 48.9600 453.0     126  8.382878    01 Bedroom
4 1554074100 01/04/2019 00:15 14.450012 18.7205 48.9815 452.5     126 14.592871    01 Bedroom
5 1554074400 01/04/2019 00:20 19.740020 18.7050 48.9930 463.0     129 20.311450    01 Bedroom
6 1554074700 01/04/2019 00:25 17.210022 18.6995 48.9875 468.0     130 17.924307    01 Bedroom
# set.seed(2)
# filenames <- list.files("Foobot", recursive=TRUE, full.names=TRUE)
# filenames[sample(length(filenames), 5)][c(1, 4, 5)]
# ldf <- lapply(filenames, read.csv, stringsAsFactors=FALSE)
# s <- sapply(ldf, nrow) != 0
# ldf[s] <- lapply(ldf[s], function(x) x[sample(nrow(x), sample(2:3)),])
# ldf <- lapply(ldf, "rownames<-", NULL)

filenames <- c(
  "Foobot/House 04 foobot data/House 04 bedroom/House 04 bed Mar 2019.csv",
  "Foobot/House 03 foobot data/House 03 Living room/House 03 Liv May 2019.csv",
  "Foobot/House 18 foobot data/House 18 living room/House 18 liv Feb 2019.csv")

ldf <- list(structure(list(time..s.=logical(0), Device.Local.Time=logical(0),
  pm..ugm3.=logical(0), tmp..C.=logical(0), hum..pc.=logical(0),
  co2..ppm.=logical(0), voc..ppb.=logical(0), allpollu....=logical(0),
  X=logical(0)), class="data.frame", row.names=integer(0)),
  structure(list(time..s.=c(1557342000L, 1556863500L),
  Device.Local.Time=c("08/05/2019 20:00", "03/05/2019 07:05"),
  pm..ugm3.=c(18.660004, 43.5), tmp..C.=c(17.73, 17.5), hum..pc.=c(55.947,
  50.739), co2..ppm.=c(1187, 1003), voc..ppb.=c(328, 277),
  allpollu....=c(45.99334, 59.928574)), row.names=c(NA, -2L),
  class="data.frame"), structure(list(time..s.=c(1549291500L, 1550995200L,
  1550111100L), Device.Local.Time=c("04/02/2019 14:45", "24/02/2019 08:00",
  "14/02/2019 02:25"), pm..ugm3.=c(13.76001, 8.4700165, 11), tmp..C.=c(21.407,
  16.972, 20.918), hum..pc.=c(48.643997, 55.678, 52.008), co2..ppm.=c(643, 910,
  738), voc..ppb.=c(178, 251.5, 204.5), allpollu....=c(21.331438, 26.541447,
  22.357143), X=c(NA, NA, NA)), row.names=c(NA, -3L), class="data.frame"))
# One of the data.frames have zero rows
sapply(ldf, dim)
#      [,1] [,2] [,3]
# [1,]    0    2    3
# [2,]    9    8    9

# Forcing all the data.frames to have at least one row results in
# padding with NAs for those that have less
ldf <- lapply(ldf, 
  function(x) data.frame(
    lapply(x, "length<-", max(c(1, nrow(x)))),
    stringsAsFactors=FALSE))

# Extract metadata from the directory names
dn <- do.call(rbind, strsplit(dirname(filenames), "/"))
dn <- dn[,-(1:(ncol(dn)-2))]
dn[,1] <- sub("^(House [0-9]+) .*", "\\1", dn[,1])
dn[,2] <- tolower(sub("^House [0-9]+ ", "", dn[,2]))

# Extract metadata from the base names
bn <- strsplit(sub("\\.csv$", "", basename(filenames)), " ")
bn <- t(sapply(bn, tail, 2))

# Combine and create Date column
dtf <- data.frame(dn, bn, stringsAsFactors=FALSE)
colnames(dtf) <- c("House", "Room", "Month", "Year")
dtf$Date <- as.Date(paste(dtf$Month, dtf$Year, 1), "%b %Y %d")

# Multi-argument intersection function
intsect <- function(x) {
    Reduce(function(x, y) unique(y[match(x, y, 0L)]), x)
}

# Create vectors of valid column names
ldf.cn <- intsect(lapply(ldf, colnames))
dtf.cn <- colnames(dtf)

# Bind metadata and sensor data
ldf.cbind <- mapply(function(dtf, ldf) {
    d <- cbind(c(dtf), ldf, stringsAsFactors=FALSE)
    d <- d[, c("House", "Room", "Date", ldf.cn)]
    d
}, split(dtf, 1:nrow(dtf)), ldf, SIMPLIFY=FALSE)

# Bind list of data.frames to one tall data.frame
ldf.rbind <- do.call(rbind, ldf.cbind)

# Convert to date-time
ldf.rbind$Device.Local.Time <- as.POSIXct(
  ldf.rbind$Device.Local.Time, format="%d/%m/%Y %H:%M")

# Control that all the column classes make sense
sapply(ldf.rbind[1,], function(x) class(x)[1])
#             House              Room              Date          time..s. 
#       "character"       "character"            "Date"         "integer" 
# Device.Local.Time         pm..ugm3.           tmp..C.          hum..pc. 
#         "POSIXct"         "numeric"         "numeric"         "numeric" 
#         co2..ppm.         voc..ppb.      allpollu.... 
#         "numeric"         "numeric"         "numeric"

# Inspect subset of final data.frame
ldf.rbind[sample(nrow(ldf.rbind), 3),]
#        House        Room       Date   time..s.   Device.Local.Time
# 1   House 04     bedroom 2019-03-01         NA                <NA>
# 3.3 House 18 living room 2019-02-01 1550111100 2019-02-14 02:25:00
# 2.2 House 03 living room 2019-05-01 1556863500 2019-05-03 07:05:00
#     pm..ugm3. tmp..C. hum..pc. co2..ppm. voc..ppb. allpollu....
# 1          NA      NA       NA        NA        NA           NA
# 3.3      11.0  20.918   52.008       738     204.5     22.35714
# 2.2      43.5  17.500   50.739      1003     277.0     59.92857