r语言 - 合并行长度不一致的 csv 文件,并将基本名称保留为列标题



我有一个充满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 脚本导出时,它们具有另一个不需要的第一列,需要删除该列。

当然有更好的方法!任何帮助都会很棒。

(我对此一团糟,但我无法让它为我工作)

非常感谢

使用最后注释中显示的测试输入读取filenames字符向量中给出的文件,然后merge它们。 最后设置名称。 工具包附带,因此您无需安装它。

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)

使用快速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

一种可能性是将data.frames读入嵌套的tibble中。 因此,首先定义一个描述如何读取和转换单个数据帧的函数。在您的情况下,它看起来像这样:

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

此函数读取一个 csv 文件,将其转换为 tibble,选择列 2 和 3,然后创建一个包含后面列名称的虚拟列(名为type)。

接下来,创建一个包含所有csvnames和所有columnnames的 tibble,并运行以下命令:

tibble(csvnames = c("csv1.csv", "csv2.csv"), columnnames = c("col1", "col2")) %>%
mutate(data = map2(csvnames, columnnames, read_onecsv))%>%
unnest() %>%
spread(type, Abundance_inds)

具有list.fileslapply的可能解决方案。

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)用于测试目的

文件测试1.csv

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

文件测试10.csv

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

最新更新