使用R中的data.table将一个字符串列拆分成可变数量的列

3
我想分析多年的Quicken家庭财务记录。我将文件导出为qif并使用bank2csv程序呈现为csv。在Quicken中,可以使用类别(例如汽车,税收),子类别(例如汽车:服务,汽车:燃料)和标签(例如自己,配偶,儿子)。bank2csv将类别:子类别/标签渲染为连接字符串。我想将类别放入类别列中,子类别放入子类别列中,并将标签放入标签列中。我看到了一个类似的问题,但遗憾的是,那个方法通过strsplit然后使用unlist以及按索引对每个元素进行赋值来运行。这在此处不起作用,因为有时没有标签,有时没有子类别。将字符串拆分为列表并将该列表保存在一列中非常容易,但是如何将列表的第一个元素分配到一个列中,如果存在第二个元素,则将其分配到第二个列中。肯定有一种优雅且简单的方法。
简化样例
library(data.table)
library(stringi)
dt <- data.table(category.tag=c("toys/David", "toys/David", "toys/James", "toys", "toys", "toys/James"), transaction=1:6)

我如何创建第三和第四列: 类别,标签。一些标签可能是 NA
我可以做以下操作,但它并不能帮助我更进一步。我需要一种指定结果列表中第一个或第二个元素的方法(而不是整个列表)。
dt[, category:= strsplit(x = category.tag, split = "/") ]
4个回答

6

最新版的data.table v1.9.5加入了两个函数transpose()tstrsplit()

使用这些函数,我们可以做到:

require(data.table)
dt[, c("category", "tag") := tstrsplit(category.tag, "/", fixed=TRUE)]
#    category.tag transaction category   tag
# 1:   toys/David           1     toys David
# 2:   toys/David           2     toys David
# 3:   toys/James           3     toys James
# 4:         toys           4     toys    NA
# 5:         toys           5     toys    NA
# 6:   toys/James           6     toys James

tstrsplittranspose(strsplit(as.character(x), ...))的包装器。您还可以传递fill=.来使用任何其他值填充缺失值,而不是NA

transpose()也可用于列表数据框数据表


4
你可以使用 cSplit
library(splitstackshape)
dt[, c("category", "tag") := cSplit(dt[,.(category.tag)], "category.tag", "/")]
dt
#    category.tag transaction category   tag
# 1:   toys/David           1     toys David
# 2:   toys/David           2     toys David
# 3:   toys/James           3     toys James
# 4:         toys           4     toys    NA
# 5:         toys           5     toys    NA
# 6:   toys/James           6     toys James

“cSplit”是“concat.split”的缩写吗? - Farrel
@AnandaMahto,我问的原因是当我在Rdocumenctation.org上搜索函数时,没有任何结果。 - Farrel
1
@Farrel,Rdocumentation上“splitstackshape”包的版本是1.2,但当前版本是1.4.2。concat.split本质上是Gabor的read.table方法,而cSplit则是使用strsplit更快实现。到1.6版本时,我计划转移到stri_split,这将会更快。这里是一个(功能性)玩具实现,展示了cSplit即将到来的样子。 - A5C1D2H2I1M1N2O1R2T1
有一点复杂。在我的简单示例中,我只展示了两个可能的字段,其中一个是哈希分隔符。实际上,它是类别:子类别/标签。我可以指定任何两个分隔符,如 sep =“:| /”,或者分类是第一个,子类别是冒号后面的任何内容,标签是斜杠/后面的任何内容? - Farrel
我决定运行两行代码。第一行将类别:子类别与标签分开,标签在/之后。然后我运行了一条类似的代码,用冒号作为分隔符将类别与子类别分开。这个方法很有效。 - Farrel
显示剩余3条评论

2

1) 尝试使用read.table

read <- function(x) read.table(text = x, sep  = "/", fill = TRUE, na.strings = "")
dt[, c("category", "tag") := read(category.tag)]

不需要额外的软件包。

2) 另一种方法是使用 tidyr 软件包中的 separate 命令:

library(tidyr)
separate(dt, category.tag, c("category", "tag"), extra = "drop")

以上是从github上的tidyr版本0.1.0.9000。要安装它,请确保已安装devtools R包,并发出以下命令:devtools::install_github("hadley/tidyr")
更新:合并了Richard Scrivens的意见和一些小改进。添加了tidyr解决方案。

非常棒。现在我该如何决定将“已接受”的奖项授予给谁呢?是你还是@RichardScriven?我喜欢被介绍新的包,这些包可能会让我在解决其他问题时更加轻松,但我也认为使用新的包会带来一些额外的开销,而不是使用已经加载的函数。 - Farrel
获取 Error in strsplit(value, sep, ...) : unused argument (extra = "drop") - Farrel
我正在使用版本0.1。 - Farrel
让我们在聊天中继续这个讨论 - Farrel

1

既然您已经加载了“stringi”,您还可以查看stri_split_fixedsimplify参数:

setnames(cbind(dt, stri_split_fixed(dt$category.tag, "/", simplify = TRUE)), 
         c("V1", "V2"), c("category", "tag"))[]
#    category.tag transaction category   tag
# 1:   toys/David           1     toys David
# 2:   toys/David           2     toys David
# 3:   toys/James           3     toys James
# 4:         toys           4     toys    NA
# 5:         toys           5     toys    NA
# 6:   toys/James           6     toys James

虽然我必须承认我有点偏爱 cSplit :-)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接