R 将TIBLE转换为参数列表
我正在尝试将TIBLE转换为函数调用的参数列表。我这样做的原因是,我想创建一个简单的文件规范Tibble,用于读取具有不同列的多个固定宽度文件。这样,我只需要使用pull和select指定文件中的列,然后就可以自动加载和解析文件。但是,我在使用cols对象指定列格式时遇到了问题 在本例中,假设我有一个如下格式的TIBLE:R 将TIBLE转换为参数列表,r,parameter-passing,do.call,tibble,R,Parameter Passing,Do.call,Tibble,我正在尝试将TIBLE转换为函数调用的参数列表。我这样做的原因是,我想创建一个简单的文件规范Tibble,用于读取具有不同列的多个固定宽度文件。这样,我只需要使用pull和select指定文件中的列,然后就可以自动加载和解析文件。但是,我在使用cols对象指定列格式时遇到了问题 在本例中,假设我有一个如下格式的TIBLE: > (filespec <- tibble(ID = c("Title", "Date", "ATTR"), Length = c(23, 8, 6), Type
> (filespec <- tibble(ID = c("Title", "Date", "ATTR"), Length = c(23, 8, 6), Type = c("col_character()", "col_date()", "col_factor(levels=c(123456,654321)")))
# A tibble: 3 x 3
ID Length Type
<chr> <dbl> <chr>
1 Title 23 col_character()
2 Date 8 col_date()
3 ATTR 6 col_factor(levels=c(123456,654321)
> (cols(Title = col_character(), Date = col_date(), ATTR=col_factor(levels=c(123456,654321))))
cols(
Title = col_character(),
Date = col_date(format = ""),
ATTR = col_factor(levels = c(123456, 654321), ordered = FALSE)
)
从我读到的其他问题中,我知道这可以通过do.call来完成。但我不知道如何以自动方式将列ID和类型转换为cols对象。这是我尝试的一个例子
> do.call(cols, select(filespec,ID, Type))
Error in switch(x, `_` = , `-` = col_skip(), `?` = col_guess(), c = col_character(), :
EXPR must be a length 1 vector
我假设select需要使用另一个执行行到参数映射的函数进行包装,这是如何实现的?我可能会采用稍微不同的方法,并将文件规格存储在一个简单的列表中:
library(purrr)
library(readr)
filespec <- list(Title = list(Length = 23,
Type = col_character()),
Date = list(Length = 8,
Type = col_date()),
ATTR = list(Length = 6,
Type = col_factor(levels = 123456,654321)))
a <- at_depth(.x = filespec,.depth = 1,.f = "Type")
> invoke(.f = cols,.x = a)
cols(
Title = col_character(),
Date = col_date(format = ""),
ATTR = col_factor(levels = 123456, ordered = 654321, include_na = FALSE)
)
tl;医生:有很多事情使这比看起来更复杂。但这是可行的,并且一旦理解了各个部分,生成的代码(最后提供)就不复杂了
正如评论中所讨论的,我基本上更喜欢乔兰的方法。事实上,当您发现自己将代码表达式存储在字符串中时,这应该会引起警钟:这是一种反模式,称为(riff-on,与之相反)。不幸的是,R中充满了严格类型的代码
也就是说,您的用例(基于文件的配置)本身就是一个好主意。我会考虑以不同的格式存储信息而不是R代码片段。但是,它确实起作用了。那么,让我们来探讨一下为什么您的代码不起作用
第一个问题是:将TIBLE传递给do.call
。TIBLES是列的列表,因此do.call
允许这样做。但是,在内部,您的呼叫被转换为等同于:
cols(
ID = c("Title", "Date", "ATTR"),
Type = c("col_character()", "col_date()", "col_factor(levels=c(123456,654321))")
)
-但这根本不是我们想要的代码
我们需要解决两个问题:
Type
列作为参数值,使用ID
列作为参数名称。我们可以通过创建一个新列表来实现这一点,该列表将ID
作为名称,将Type
作为值:setNames(Type,ID)
cols
不知道如何处理字符串参数。它需要列规范-类型为收集器的对象
换言之,无论您是编写“col\u date()”
还是col\u date()
,这都是一个巨大的差异
Type
列解析为R代码,并且我们需要评估解析后的表达式。R提供了两个方便的函数(分别是parse
和eval
)来完成这一任务。但不要让两个简单函数的存在欺骗了你:这是一个极其复杂的操作。R基本上需要对代码片段运行完整的解析器和解释器。如果代码不是你所期望的,它会变得更加复杂。例如,文本可能包含代码unlink('/',recursive=TRUE)
,而不是col\u date()
。然后R会很高兴地擦除你的硬盘
这只是parse
/eval
复杂且通常避免的原因之一。其他原因包括:如果代码中存在解析错误(事实上,您的代码确实包含缺少的右括号…),会发生什么
但我们走了。现在我们把所有的部分都放在一起了,我们可以相对容易地将它们连接起来:
filespec %>%
mutate(Parsed = lapply(Type, function (x) parse(text = x, encoding = 'UTF-8'))) %>%
mutate(ColSpec = lapply(Parsed, eval)) %>%
with(setNames(ColSpec, ID)) %>%
do.call(cols, .)
逐段执行此代码,查看它的功能,并让自己相信它工作正常。您可以使用
do.调用来执行此操作,但您的代码,就其现状而言,不远程做你想做的事-你需要首先了解做什么。在你可以使用它之前,call
实际上做什么。我是R的新手,所以这都是一次学习经验。我想我理解了do.call的作用,它调用一个函数,并使用其他参数作为参数。根据我对下面答案的评论,我想我在这里逃避的是如何以自动方式创建命名列表。我不想手动键入所有的field=type参数,我将它们分为两列,我只想让R为我创建命名列表。是的,您在问题描述中的位置是正确的。从你的问题来看,你似乎不明白这一点。但这部分问题实际上可以通过使用setNames
轻松解决。另一个更大的问题是,参数是字符串,而不是代码。因此,您首先需要对它们进行评估,虽然这是可能的(通过parse/eval),但这很混乱,可能不是一个好主意。Joran的方法很好。我喜欢这个解决方案,它很有效!我使用TIBLE的主要原因是因为最终我可能会有50-60列,而在源文件中维护该列表可能会很烦人,所以我希望通过csv以读入的方式完成。有没有一种简单的方法可以将我需要的两列从tibble中取出,并将它们转换成一个列表?我是R新手,我认为以自动方式创建命名列表的方法让我不知所措。setNames/with部分正是我所需要的。我知道eval问题,但打算稍后再解决,最有可能的方法是从类型strings->S3 objects进行简单映射。
filespec %>%
mutate(Parsed = lapply(Type, function (x) parse(text = x, encoding = 'UTF-8'))) %>%
mutate(ColSpec = lapply(Parsed, eval)) %>%
with(setNames(ColSpec, ID)) %>%
do.call(cols, .)