Tengo un directorio lleno de archivos csv, todos tienen una columna común (Class) y luego un valor entero, aunque tienen una longitud de archivo inconsistente. Un ejemplo [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

Se están exportando para otro script R, por lo que la primera columna debe recortarse antes de fusionarse, puedo fusionarlos con éxito usando:

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

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

Y entonces:

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

Que proporciona el siguiente resultado deseado sin importar cuántos archivos [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

También quiero usar el basename del archivo como encabezado de columna, sé que puedo extraerlo usando esto:

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

Sé que puedo crear una lista de basenames y luego usar estos encabezados de columna, pero parece poco práctico crear un marco de datos para cada csv (hay muchos) y luego hacerlo manualmente.

A medida que los CSV se exportan desde otro script R, tienen una primera columna adicional innecesaria que debe eliminarse.

¡Seguramente hay una mejor manera! Cualquier ayuda sería genial.

(He tenido un lío con esto pero no puedo hacer que funcione para mí)

Muchas gracias

-1
Jim 10 may. 2019 a las 16:08

4 respuestas

La mejor respuesta

Una respuesta más usando el rápido fread de 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

Editar: Aquí hay una única forma data.table (evitando 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
ismirsehregal 10 may. 2019 a las 20:35

Utilizando la entrada de prueba que se muestra en la Nota al final, lea los archivos que figuran en el vector de caracteres filenames y luego merge. Finalmente establece los nombres. El paquete de herramientas viene con R, por lo que no necesita instalarlo.

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))

Dando:

                             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

Dependiendo de lo que desee para la salida, puede necesitar all = FALSE en lugar del argumento all que se muestra.

Nota

He proporcionado datos de prueba para usted a continuación esta vez, pero esto realmente debería haberse proporcionado en el problema junto con exactamente qué salida espera.

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)
3
G. Grothendieck 10 may. 2019 a las 14:08

Una posibilidad sería leer los data.frames en un tibble anidado. Por lo tanto, primero define una función que describe cómo leer y transformar un único marco de datos. En su caso, así es como podría verse:

library(tidyverse)

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

Esta función lee un archivo csv, lo transforma en un tibble, selecciona las columnas 2 y 3 y luego crea una columna ficticia (llamada type) que contiene los nombres de las columnas posteriores.

Luego, crea un tibble con todos csvnames y todos columnnames y ejecuta lo siguiente:

tibble(csvnames = c("csv1.csv", "csv2.csv"), columnnames = c("col1", "col2")) %>%
    mutate(data = map2(csvnames, columnnames, read_onecsv))%>%
    unnest() %>%
    spread(type, Abundance_inds)
2
Cettt 10 may. 2019 a las 13:40

Posible solución con list.files y 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


He creado dos archivos .csv (test1.csv y test10.csv) con fines de prueba

Archivo test1.csv

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

Archivo test10.csv

number, class,value
1,banana,1
2,apples,1
3,ginger,5
0
Patrick 10 may. 2019 a las 21:33