我尝试使用
read_csv
中的 {readr}
将 CSV
文件读入 R。为了演示我的真正问题,我首先将参数 guess_max
重置为 5(默认为 1000)
library(readr)
formals(read_csv)$guess_max <- 5
并以较小的文字数据为例:
csv <- I(
"ID, Col1, Col2, VarA, VarB, VarC
1, NA, NA, NA, NA, NA
2, NA, NA, NA, NA, NA
3, NA, NA, NA, NA, NA
4, NA, NA, NA, NA, NA
5, 0, 1, x, y, z
6, NA, NA, NA, NA, NA")
read_csv(csv)
# # A tibble: 6 × 6
# ID Col1 Col2 VarA VarB VarC
# <dbl> <lgl> <lgl> <lgl> <lgl> <lgl>
# 1 1 NA NA NA NA NA
# 2 2 NA NA NA NA NA
# 3 3 NA NA NA NA NA
# 4 4 NA NA NA NA NA
# 5 5 FALSE* TRUE* NA* NA* NA*
# 6 6 NA NA NA NA NA
*
:出现解析问题
受
guess_max
影响,仅前 5 行(列名称和 ID
1 到 4)用于猜测列类型。因为 ID
1 到 4 中的值全部缺失,所以所有列都被猜测为 logical
并且被错误解析:
0
,1
(整数)→ FALSE
,TRUE
(逻辑)'x'
、'y'
、'z'
(字符)→ NA
(逻辑)在这种情况下我必须手动设置
col_types
:
read_csv(csv, col_types = cols(Col1 = col_integer(), Col2 = col_integer(),
VarA = col_character(), VarB = col_character(), VarC = col_character()))
# # A tibble: 6 × 6
# ID Col1 Col2 VarA VarB VarC
# <dbl> <int> <int> <chr> <chr> <chr>
# 1 1 NA NA NA NA NA
# 2 2 NA NA NA NA NA
# 3 3 NA NA NA NA NA
# 4 4 NA NA NA NA NA
# 5 5 0 1 x y z
# 6 6 NA NA NA NA NA
当列数量较多时,一一提供列类型很烦人。如果我想要指定的那些列的名称具有某些模式,我希望使用类似
across()
中的 {dplyr}
。伪代码如下:
read_csv(csv, col_types = cols(across(starts_with("Col"), col_integer()),
across(starts_with("Var"), col_character())))
是否可以通过
readr
本身或其他附加包实现?
提前致谢!
我需要使用
col_xxx()
而不是它们的缩写('i'
、'c'
等)来创建更通用的列规范,例如
cols(across(contains("Date"), col_date(format = "%m-%d-%Y")),
across(Fct1:Fct9, col_factor(levels = custom_levels)))
read_delim()
系列使用 tidy-selection 来选择带有参数 col_select
的列。您可以利用此参数将 tidy-selection 合并到列类型的规范中。下面是一个简单的实现。关键是将 n_max = 0L
设置为仅读取列名称行。
col_across <- function(.cols, .fns, file) {
col_selected <- read_csv(file, n_max = 0L, col_select = {{.cols}}, show_col_types = FALSE)
lapply(col_selected, function(x) .fns)
}
df <- read_csv(csv,
col_types = c(col_across(starts_with("Col"), col_integer(), csv),
col_across(VarA:VarC, col_factor(c('x', 'y', 'z')), csv))
)
上面的方法很简单,但是还过得去,但有一些缺点:
csv
)传递给每个col_across()
。read_delim
家族包括多种变体,例如read_csv
,read_csv2
,read_tsv
。调用df <- read_xxx(...)
时,必须确认col_across()
使用了一致的read_xxx
来读取列名。开发了
col_across
的改进版本,自动检测使用的是哪个read_xxx
,并从外部调用中检索文件源。
col_across <- function(.cols, .fns) {
sc <- sys.call(1L)
sc <- match.call(match.fun(sc[[1L]]), sc)
read_call <- sc[c(1L, match("file", names(sc), 0L))]
read_call$n_max <- 0L
read_call$col_select <- substitute(.cols)
read_call$show_col_types <- FALSE
lapply(eval(read_call, parent.frame()), function(x) .fns)
}
df <- read_csv(csv,
col_types = c(col_across(starts_with("Col"), col_integer()),
col_across(VarA:VarC, col_factor(c('x', 'y', 'z'))))
)
请注意,此版本的
只能在col_across
系列内使用,因为read_delim
与across
中的mutate
对应。{dplyr}
spec(df)
# cols(
# ID = col_double(),
# Col1 = col_integer(),
# Col2 = col_integer(),
# VarA = col_factor(levels = c("x", "y", "z"), ordered = FALSE, include_na = FALSE),
# VarB = col_factor(levels = c("x", "y", "z"), ordered = FALSE, include_na = FALSE),
# VarC = col_factor(levels = c("x", "y", "z"), ordered = FALSE, include_na = FALSE)
# )