提问人:Eric Boorman 提问时间:8/21/2023 更新时间:8/22/2023 访问量:50
rbindlist 在 R 中具有不同的变量名称/位置:创建输出数据帧/文件
rbindlist with different variable names / positions in R: Creating an output dataframe/file
问:
我有一个问题,是两个不同帖子的组合。
我正在尝试将多个文件放在一个文件夹中。我的一些变量在一个文件中的命名与另一个文件不同。我想确定变量被错误标记的特定文件。从这里,我可以手动调整各个文件。我可以将use.names切换为FALSE,但列的顺序不正确。因此,当我使用 rbindlist 时,我通常会在最后获得一系列额外的变体,其中包含某些文件中使用的不同拼写。
names (data1)
id, var1, var2, var3
names (data2)
id, variable1, variable2, variable3
names (finaldata)
id, var1, var2, var3, variable1, variable2, variable3
我认为处理此问题的最佳方法是创建一个包含所有不同文件和相应变量名称的新数据帧/文件。然后,我可以扫描这个新文件,看看哪些原始文件可能需要编辑。
首先,我创建了一个函数,该函数将读取单个文件,然后为我提供文件名和文件中变量的列表。
varnames <- function (file) {
temp <- read_excel (file)
colnames (temp)
x <- colnames (temp)
y <- file
z <- c (y, x)
as.data.frame (z)
}
这将为我提供以下输出。
varname (data1)
data1
var1
var2
var3
...
接下来,我可以生成所有文件的列表,就像使用 rbindlist 一样。
file.list <- list.files(path = "file_path", pattern='*.xlsx', full.names = FALSE)
从这里,我可以使用 cbind 将所有元素组合在一起。现在,我可以使用任何方法来识别错误(扫描新的数据文件,创建虚拟变量和子集等)。
cbind(lapply(file.list, varnames))
errors <- cbind(lapply(file.list, varnames))
write.csv (cbind(lapply(file.list, varnames)), "variable_names_across_files.csv")
这是我希望生成的理想输出。
filename. var_1_name var_2_name var_3_name
file1 variable1 var_2 var3
file2 variable1 variable2 variable3
file3 var1 var2 var3
file4 var1 var2 var3
file5 var1 variable2 var3
我提出的解决方案确实有效,但效率非常低。此代码将需要很长时间才能运行。就上下文而言,我正在处理大约 2000 个文件,每个文件大约有 120 个案例。由于数据集的最终大小,这可能是不可避免的,但我想看看是否有另一种方法来看待和解决这个问题。
答:
使用 Excel 或用 rust 或其他方法编写的快速实用程序 xlsx2csv 将每个文件写入 csv,并且所有文件都在当前目录中,并且需要该目录中的所有文件:
xlsx2csv <- \(x) system(sprintf("xlsx2csv %s %s", x, sub("xlsx$", "csv", x)))
lapply(Sys.glob("*.xlsx"), xlsx2csv)
我们假设每个文件都有相同数量的列,首先运行它生成一个矩阵,其中每行显示一个文件的标题——文件名将是该矩阵的行名。使用它手动修复列名以使其保持一致。它们不必按相同的顺序排列。
Files <- Sys.glob("*.csv")
t(mapply(read.table, Files, sep = ",", nrows = 1))
然后使用它将它们读入单个文件。
library(dplyr)
rbind_rows(Map(read.csv, as.list(Files)))
可重复性测试
library(dplyr)
# write test files
write.csv(BOD[1:3,], "BOD.csv", quote = FALSE, row.names = FALSE)
write.csv(BOD[1:2, 2:1], "BOD2.csv", quote = FALSE, row.names = FALSE)
Files <- Sys.glob("BOD*.csv")
t(mapply(read.table, Files, sep = ",", nrows = 1))
## V1 V2
## BOD.csv "Time" "demand"
## BOD2.csv "demand" "Time"
# looks ok so proceed - if not we would edit headings first
bind_rows(Map(read.csv, as.list(Files)))
## Time demand
## 1 1 8.3
## 2 2 10.3
## 3 3 19.0
## 4 1 8.3
## 5 2 10.3
一些问题可能是 excel 文件的读取速度比 csv 文件慢。一个小的调整是添加参数,以在代码中读取列名。使用函数的一个版本进行说明:n_max = 0
varnames_short
dir.create("temp_excels")
sapply(sample(letters, 15), \(x) runif(n = 100000), simplify = FALSE) |>
{\(x) append(list(group = sample(LETTERS, 100000, replace = TRUE)), x)}() |>
as.data.frame() |>
openxlsx::write.xlsx("temp_excels/testfile1.xlsx")
sapply(sample(letters, 15), \(x) runif(n = 100000), simplify = FALSE) |>
{\(x) append(list(group = sample(LETTERS, 100000, replace = TRUE)), x)}() |>
as.data.frame() |>
openxlsx::write.xlsx("temp_excels/testfile2.xlsx")
sapply(sample(letters, 15), \(x) runif(n = 100000), simplify = FALSE) |>
{\(x) append(list(group = sample(LETTERS, 100000, replace = TRUE)), x)}() |>
as.data.frame() |>
openxlsx::write.xlsx("temp_excels/testfile3.xlsx")
library(readxl)
varnames <- function (file) {
temp <- read_excel (file)
x <- colnames (temp)
y <- file
z <- c (y, x)
as.data.frame (z)
}
varnames_short <- function (file) {
temp <- read_excel (file, n_max = 0)
x <- colnames (temp)
y <- file
z <- c (y, x)
as.data.frame (z)
}
file.list <- list.files(path = "temp_excels", pattern='*.xlsx', full.names = TRUE)
microbenchmark::microbenchmark(
cbind(lapply(file.list, varnames)),
cbind(lapply(file.list, varnames_short)),
times = 3
)
#> Unit: seconds
#> expr min lq mean median
#> cbind(lapply(file.list, varnames)) 5.261145 5.281157 5.319038 5.301169
#> cbind(lapply(file.list, varnames_short)) 2.566850 2.662395 2.701599 2.757940
#> uq max neval cld
#> 5.347984 5.394799 3 a
#> 2.768974 2.780008 3 b
如果您有较长的数据集,这可能会使您的时间减半。但可能无法满足您的所有需求。您还可以并行读取文件。
评论