Рекурсивно импортируйте данные из вложенных CSV-файлов и создайте столбец ID с месяцем и годом из имени файла. - PullRequest
1 голос
/ 09 июля 2019

У меня есть данные о качестве воздуха из нескольких комнат из нескольких домов.Я хотел бы рекурсивно импортировать данные из файлов .csv, которые вложены в папки, имеющие следующую структуру файлов.У меня проблема в том, что, хотя я могу импортировать их со списками без имен папок или файлов (имен комнат или дат), я не могу понять, как извлечь имя комнаты, месяц или год и приписать его списку, которыйсозданный таким образом, что я могу манипулировать данными для построения графика и т. д.

Может кто-нибудь помочь, пожалуйста, извлечь номер дома, название комнаты, месяц и год из имен файлов / папок и использовать rbind для создания data.frame с ID столбцы, представляющие номер дома, комнату, месяц и год?

Этот код работает для одной папки:

filenames <- list.files( pattern="*.csv", full.names=TRUE,recursive = T)
ldf <- lapply(filenames, read.csv)

Для преобразования во фрейм данных без идентификаторов (теряетhouse и room, к которым относятся файлы)

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

Каждый CSV-файл выглядит следующим образом:

enter image description here

Редактировать: включить желаемый вывод.

В идеале мне хотелось бы получить вывод data.frame со столбцами: время, дата, var1, var2, var2, houseNum, roomName

Это то, что я получил, основываясь на ответе AkselA:

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

Ответы [ 2 ]

4 голосов
/ 09 июля 2019

Используя предоставленную вами ссылку на sharepoint, я сделал компактный mcve , который фиксирует большинство нарушений в полном наборе данных. Основной проблемой были пустые data.frames, и их было не сложно найти, хотя не все из них имели (no data) в имени файла. Хотя достаточно просто отбросить пустые data.frames, я решил оставить их, заполнив одну строку NA s. Если эти строки оказываются неприятными, их достаточно легко удалить позже.
Я пробовал это на полном наборе данных, и он отлично работает.

Пример данных

# 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
1 голос
/ 09 июля 2019

Следующее должно приблизить вас. Импортируйте файлы CSV с readr read_csv в purrr map. Задайте имена списков, используя paths, затем свяжите кадры данных, используя аргумент .id = "path", чтобы включить столбец с именами элементов списка. Разделите пути с помощью "/" с помощью тидира separate. Удалите лишние строки из имен файлов, используя stringr str_remove и другие переменные в качестве шаблонов. Наконец, разделите то, что осталось от имен файлов, с помощью другого вызова отделить:

library(tidyverse)

paths <- list.files(pattern = "csv$", recursive = T)

map(paths, read_csv) %>% 
    set_names(paths) %>% 
    bind_rows(.id = "path") %>% 
    separate(path, c("house", "room", "file"), "/") %>% 
    mutate(file = file %>%
               str_remove(regex(house, T)) %>%
               str_remove(regex(room, T)) %>% 
               str_remove("\\.csv") %>% 
               str_trim,
           house = parse_number(house)
           ) %>% 
    separate(file, c("month", "year"), convert = T)

, который возвращает следующее, используя некоторые синтетические данные:

# A tibble: 4 x 8
  house room        month    year time   var1  var2  var3 
  <dbl> <chr>       <chr>   <int> <drtn> <chr> <chr> <chr>
1     1 Kitchen     Apr      2019 02:00  blah  bleh  bluh 
2     1 Living room June     2018 12:00  blah  bleh  bluh 
3     2 Kitchen     July     2019 08:00  blah  bleh  bluh 
4     2 Living room January  2016 16:00  blah  bleh  bluh 
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...