Как перебирать папку с CSV-файлами в R

#r #file #loops #csv

#r #файл #петли #csv

Вопрос:

У меня есть папка, содержащая кучу CSV-файлов с названиями «yob1980», «yob1981», «yob1982» и т.д.

Я должен использовать цикл for, чтобы просмотреть каждый файл и поместить его содержимое во фрейм данных — столбцы в фрейме данных должны быть «1980», «1981», «1982» и т.д.

Вот что у меня есть:

 file_list <- list.files()

temp = list.files(pattern="*.txt")
babynames <- do.call(rbind,lapply(temp,read.csv, FALSE))

names(babynames) <- c("Name", "Gender", "Count")
 

Я чувствую, что мне нужен цикл for, но я не уверен, как перебирать файлы. Кто-нибудь укажет мне правильное направление?

Комментарии:

1. Являются ли CSV-файлы файлами с одним столбцом и без заголовков? И соответствуют ли они одним и тем же идентификаторам записей?

2. То, что у вас уже есть, выполняет цикл по всем файлам ( lapply выполняет неявный for цикл по всем файлам). И вы уже создаете один фрейм данных ( do.call(rbind, ....) ). В чем вопрос?

3. @Parfait файлы CSV не имеют заголовков, и внутри них есть три столбца, которые содержат имя, пол и количество этого имени

4. @MichaelGriffiths Я пытаюсь добавить столбец в фрейм данных, который включает год, которому соответствует имя.

5. file_list Для чего это нужно?

Ответ №1:

Мой любимый способ сделать это — использовать ldply из plyr пакета. Его преимущество заключается в возврате фрейма данных, поэтому вам не нужно выполнять шаг rbind после этого:

 library( plyr )
babynames <- ldply( .data = list.files(pattern="*.txt"),
                    .fun = read.csv,
                    header = FALSE,
                    col.names=c("Name", "Gender", "Count") )
 

В качестве дополнительного преимущества вы можете очень легко выполнять многопоточный импорт, что значительно ускоряет импорт больших наборов данных из нескольких файлов:

 library( plyr )
library( doMC )
registerDoMC( cores = 4 )
babynames <- ldply( .data = list.files(pattern="*.txt"),
                    .fun = read.csv,
                    header = FALSE,
                    col.names=c("Name", "Gender", "Count"),
                    .parallel = TRUE )
 

Слегка изменив вышесказанное, чтобы включить Year столбец в результирующий фрейм данных, вы можете сначала создать функцию, а затем выполнить эту функцию внутри ldply таким же образом, как вы бы выполнили read.csv

 readFun <- function( filename ) {

    # read in the data
    data <- read.csv( filename, 
                      header = FALSE, 
                      col.names = c( "Name", "Gender", "Count" ) )

    # add a "Year" column by removing both "yob" and ".txt" from file name
    data$Year <- gsub( "yob|.txt", "", filename )

    return( data )
}

# execute that function across all files, outputting a data frame
doMC::registerDoMC( cores = 4 )
babynames <- plyr::ldply( .data = list.files(pattern="*.txt"),
                          .fun = readFun,
                          .parallel = TRUE )
 

Это предоставит вам ваши данные в сжатом и аккуратном виде, и именно так я бы рекомендовал двигаться дальше. Хотя затем можно разделить данные за каждый год на отдельные столбцы, это, вероятно, не лучший способ.

Примечание: в зависимости от ваших предпочтений, может быть хорошей идеей преобразовать Year столбец, скажем, integer в class . Но это зависит от вас.

Комментарии:

1. Этот способ создает фрейм данных вместо списка — у меня возникли проблемы с преобразованием метода Майкла из списка в фрейм данных. Однако, как мне добавить годы в новый столбец в мой фрейм данных? Вроде как добавление в python

2. Вы включили последнюю строку rbind метода @Michael Griffiths? Это должно привести к преобразованию во фрейм данных.

3. То, о чем вы просите, звучит не как an append , а как новый столбец для каждого файла. Для большинства наборов данных это не очень хорошая идея. Являются ли ваши name gender столбцы и одинаковыми для каждого файла?

4. doMC у меня это не сработало, но doFuture::registerDoFuture(); future::plan("multisession", workers = 8) сработало.

Ответ №2:

Использование purrr

 library(tidyverse)

files <- list.files(path = "./data/", pattern = "*.csv")

df <- files %>% 
    map(function(x) {
        read.csv(paste0("./data/", x))
    }) %>%
    reduce(rbind)
 

Ответ №3:

Рассмотрим анонимную функцию в lapply() :

 files = list.files(pattern="*.txt")

dfList <- lapply(files, function(i) {
     df <- read.csv(i, header=FALSE, col.names=c("Name", "Gender", "Count"))
     df$Year <- gsub("yob", "", i) 
     return(df)
})

finaldf <- do.call(rbind, dflist)
 

Ответ №4:

for Цикл может быть более подходящим, чем lapply в этом случае.

 file_list = list.files(pattern="*.txt")
data_list <- vector("list", "length" = length(file.list))

for (i in seq_along(file_list)) {
    filename = file_list[[i]]

    # Read data in
    df <- read.csv(filename, header = FALSE, col.names = c("Name", "Gender", "Count"))

    # Extract year from filename
    year = gsub("yob", "", filename)
    df[["Filename"]] = year

    # Add year to data_list
    data_list[[i]] <- df
}

babynames <- do.call(rbind, data_list)
 

Комментарии:

1. Я изменил # Extract year из filename year = gsub(«yob», «», filename) df[[«Filename»]] = year, чтобы вручную увеличить год, потому что .txt был конечным, но спасибо за помощь!