Объединение CSV-файлов с несовместимой длиной строки и сохранение базового имени в качестве заголовка столбца - PullRequest
0 голосов
/ 10 мая 2019

У меня есть каталог, полный CSV-файлов, у всех из которых есть общий столбец (Class), а затем целочисленное значение, хотя они имеют несовместимую длину файла.Пример [1:5, ]:

                              Class Abundance_inds
1                       Chaetognath              2
2      Copepod_Calanoid_Acartia_spp              9
3  Copepod_Calanoid_Centropages_spp              4
4       Copepod_Calanoid_Temora_spp              1
5          Copepod_Calanoid_Unknown              5

Они экспортируются для другого сценария R, поэтому перед объединением необходимо обрезать первый столбец, я могу успешно объединить его, используя:

test <- read.csv(file = csvs[1])[ ,2:3]

test2 <- read.csv(file = csvs[2])[ ,2:3]

изатем:

library(tidyverse)
mergedcsvs <- list(test, test2) %>% reduce(full_join, by = "Class")

, который дает следующий и желаемый результат независимо от того, сколько файлов [1:4,]:

                              Class Abundance_inds.x Abundance_inds.y
1                       Chaetognath                2                4
2      Copepod_Calanoid_Acartia_spp                9               11
3  Copepod_Calanoid_Centropages_spp                4                8
4       Copepod_Calanoid_Temora_spp                1               NA

Я также хочу использовать basename файла в качествеЗаголовок столбца, я знаю, что могу извлечь, использует это:

basename1 <- csvs[1]
basename2 <- csvs[2]

Я знаю, что я могу создать список basenames, затем использовать это заголовки столбцов, но кажется довольно непрактичным, чтобы создать кадр данных для каждогоодин CSV (есть много), затем вручную сделать это.

Поскольку CSV экспортируются из другого R-скрипта, у них есть дополнительный ненужный первый столбец, который необходимо удалить.

Конечно, есть лучший способ!Любая помощь будет отличной.

(у меня был беспорядок с этим , но я не могу заставить его работать на меня)

Большое спасибо

Ответы [ 4 ]

3 голосов
/ 10 мая 2019

Используя тестовый ввод, показанный в примечании в конце, прочитайте файлы, указанные в символьном векторе filenames, а затем merge их.Наконец, установите имена.Пакет инструментов поставляется с R, поэтому вам не нужно его устанавливать.

library(tools)

LL <- Map(read.csv, filenames, as.is = TRUE)
r <- Reduce(function(...) merge(..., all = TRUE, by = "Class"), LL)
names(r)[-1] <- basename(file_path_sans_ext(filenames))

дает:

                             Class DF1 DF2 DF3
1                      Chaetognath   2  NA   2
2     Copepod_Calanoid_Acartia_spp   9   9   9
3 Copepod_Calanoid_Centropages_spp   4   4  NA
4      Copepod_Calanoid_Temora_spp   1   1   1
5         Copepod_Calanoid_Unknown  NA   5   5

В зависимости от того, что вы хотите для вывода, вам может понадобиться all = FALSE вместо показанного аргумента all.

Примечание

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

Lines <- "                              Class Abundance_inds
1                       Chaetognath              2
2      Copepod_Calanoid_Acartia_spp              9
3  Copepod_Calanoid_Centropages_spp              4
4       Copepod_Calanoid_Temora_spp              1
5          Copepod_Calanoid_Unknown              5"
DF <- read.table(text = Lines, as.is = TRUE)
L <- list(DF1 = DF[1:4, ], DF2 = DF[2:5, ], DF3 = DF[-3, ])
filenames <- paste0(names(L), ".csv")
for(i in seq_along(filenames)) write.csv(L[[i]], filenames[i], row.names = FALSE)
2 голосов
/ 10 мая 2019

Еще один ответ, используя быстрый fread из library(data.table)

library(tidyverse)
library(data.table)
library(tools)

write.csv(data.frame(stringsAsFactors=FALSE,
                     Class = c("Chaetognath", "Copepod_Calanoid_Acartia_spp",
                               "Copepod_Calanoid_Centropages_spp",
                               "Copepod_Calanoid_Temora_spp"),
                     Abundance_inds = c(2, 9, 4, 1)
), file = "x.csv")

write.csv(data.frame(stringsAsFactors=FALSE,
                     Class = c("Chaetognath", "Copepod_Calanoid_Acartia_spp",
                               "Copepod_Calanoid_Centropages_spp"),
                     Whatever = c(4, 11, 8)
), file = "y.csv")

csvPaths <- list.files(".", "\\.csv$", full.names = TRUE)

csvList <- list()
for(csvPath in csvPaths){
  csvList[[csvPath]] <- fread(csvPath, col.names = c("Class", basename(file_path_sans_ext(csvPath))), drop = 1)
}

mergedcsvs <- csvList %>% reduce(full_join, by = "Class")

#                              Class x.csv y.csv
# 1                      Chaetognath     2     4
# 2     Copepod_Calanoid_Acartia_spp     9    11
# 3 Copepod_Calanoid_Centropages_spp     4     8
# 4      Copepod_Calanoid_Temora_spp     1    NA

Редактировать: Вот только data.table единственный способ (избегая library(tidyverse))

csvPaths <- list.files(".", "\\.csv$", full.names = TRUE)

csvList <- list()
for(csvPath in csvPaths){
  csvList[[csvPath]] <- fread(csvPath, drop = 1, col.names = c("class", "vars"))[, id := basename(file_path_sans_ext(csvPath))]
}

DT <- rbindlist(csvList, use.names = FALSE)
mergedDT <- dcast.data.table(DT, class ~ id, value.var = "vars")
mergedDT
2 голосов
/ 10 мая 2019

одной из возможностей было бы чтение data.frames во вложенную таблицу.Поэтому сначала вы определяете функцию, которая описывает, как читать и преобразовывать один фрейм данных.В вашем случае это выглядит так:

library(tidyverse)

read_onecsv <- function(csvname, columnname) {
  read.csv(file = csvname) %>% as_tibble() %>% 
    select(2:3) %>% mutate(type = columnname)
}

Эта функция читает один CSV-файл, преобразует его в таблицу, выбирает столбцы 2 и 3, а затем создает фиктивный столбец (с именем type).который содержит более поздние имена столбцов.

Затем вы создаете таблицу со всеми csvnames и всеми columnnames и запускаете следующее:

tibble(csvnames = c("csv1.csv", "csv2.csv"), columnnames = c("col1", "col2")) %>%
    mutate(data = map2(csvnames, columnnames, read_onecsv))%>%
    unnest() %>%
    spread(type, Abundance_inds)
0 голосов
/ 10 мая 2019

Возможное решение с использованием list.files и lapply.

library(readr)

## read all names with .csv at the end form your working directory and save as variable
fileNames <- list.files(pattern = '.csv')

## read all files, merge and save as tibble
fileList <- lapply(1:length(fileNames), function(i) read_csv(fileNames[i]) %>%
 select(-1)
) %>% 
  reduce(full_join, by = 'class')

## rename columns
names(fileList) <- c(names(fileList)[1], sub('.csv', "", fileNames))

## output
# A tibble: 4 x 3
  class  test1 test10
  <chr>  <dbl>  <dbl>
1 banana     1      1
2 apples     1      1
3 orange    10     NA
4 ginger    NA      5


Я создал два файла .csv (test1.csv & test10.csv) для целей тестирования

Файл test1.csv

number, class,value
1,banana,1
2,apples,1
3,orange,10

Файл test10.csv

number, class,value
1,banana,1
2,apples,1
3,ginger,5
...