我有一个看起来像这样的文件:
a 1,2,3,5
b 4,5,6,7
c 5,6,7,8
...
数据集中第一列和第二列之间的分隔符是'\t',其他列之间的分隔符是逗号。我该如何将这种数据集读取为一个有5个字段的dataframe。
我有一个看起来像这样的文件:
a 1,2,3,5
b 4,5,6,7
c 5,6,7,8
...
数据集中第一列和第二列之间的分隔符是'\t',其他列之间的分隔符是逗号。我该如何将这种数据集读取为一个有5个字段的dataframe。
我可能会这样做。
read.table(text = gsub(",", "\t", readLines("file.txt")))
V1 V2 V3 V4 V5
1 a 1 2 3 5
2 b 4 5 6 7
3 c 5 6 7 8
对此进行简单解释:
readLines()
将文件读取为 R 中的字符向量,每行一个元素。gsub(",", "\t", ...)
将每个逗号替换为制表符,这样我们现在有了只有一种分隔字符的行。read.table()
的 text =
参数使其知道您正在直接传递一个字符向量以供读取(而不是包含文本数据的文件名)。从你提出问题的方式来看,似乎你知道你的数据是“平衡”的(矩形)。
你是否正在寻找更快的选项?你可能想将"data.table"中的fread
与我的实验性concat.split.DT
函数结合使用。
解决方案可能如下所示(用"\t"
替换" "
以获得制表符):
concat.split.DT(fread("yourfile.txt", sep = " ", header=FALSE), "V2", ",")
让我们编造一些数据:
x <- c("a\t1,2,3,5", "b\t4,5,6,7","c\t5,6,7,8")
X <- c(replicate(10000, x))
temp <- tempfile()
writeLines(X, temp, sep="\n") ## Write it to a temporary file
乔希的答案:
system.time(out1 <- read.table(text = gsub(",", "\t", readLines(temp))))
# user system elapsed
# 0.679 0.000 0.676
head(out1)
# V1 V2 V3 V4 V5
# 1 a 1 2 3 5
# 2 b 4 5 6 7
# 3 c 5 6 7 8
# 4 a 1 2 3 5
# 5 b 4 5 6 7
# 6 c 5 6 7 8
dim(out1)
# [1] 30000 5
fread
+ concat.split.DT
(类似于两次使用 fread
,但仍然非常快):system.time(out2 <- concat.split.DT(fread(temp, sep = "\t", header=FALSE), "V2", ","))
# user system elapsed
# 0.027 0.000 0.028
head(out2)
# V1 V2_1 V2_2 V2_3 V2_4
# 1: a 1 2 3 5
# 2: b 4 5 6 7
# 3: c 5 6 7 8
# 4: a 1 2 3 5
# 5: b 4 5 6 7
# 6: c 5 6 7 8
dim(out2)
# [1] 30000 5
虽然这并不适用于您的问题,但我应该提及它以使其他需要解决类似问题的人受益:
上述方法的一个限制是 concat.split.DT
只能处理“平衡”的数据。与 read.table
不同,fread
没有 fill
参数(我记得在某个地方读到过它很可能不会有这样的参数)。
以下是一个“不平衡”数据的示例:
x2 <- c("a\t1,2,3,5,6,7", "b\t4,5,6,7","c\t5,6,7,8,9,10,11,12,13")
X2 <- c(replicate(10000, x2))
temp2 <- tempfile()
writeLines(X2, temp2, sep="\n")
read.table
可以使用fill = TRUE
参数处理该问题:
system.time(out1b <- read.table(text = gsub(",", "\t", readLines(temp2)), fill=TRUE))
# user system elapsed
# 1.151 0.000 1.152
head(out1b)
# V1 V2 V3 V4 V5 V6 V7 V8 V9 V10
# 1 a 1 2 3 5 6 7 NA NA NA
# 2 b 4 5 6 7 NA NA NA NA NA
# 3 c 5 6 7 8 9 10 11 12 13
# 4 a 1 2 3 5 6 7 NA NA NA
# 5 b 4 5 6 7 NA NA NA NA NA
# 6 c 5 6 7 8 9 10 11 12 13
concat.split.DT
在这种情况下会给出一个糟糕的错误,但是你可以尝试我的cSplit
函数代替。它不太快,但仍然表现不错:
system.time(out2b <- cSplit(fread(temp2, sep = "\t", header=FALSE), "V2", ","))
# user system elapsed
# 0.393 0.004 0.399
head(out2b)
# V1 V2_1 V2_2 V2_3 V2_4 V2_5 V2_6 V2_7 V2_8 V2_9
# 1: a 1 2 3 5 6 7 NA NA NA
# 2: b 4 5 6 7 NA NA NA NA NA
# 3: c 5 6 7 8 9 10 11 12 13
# 4: a 1 2 3 5 6 7 NA NA NA
# 5: b 4 5 6 7 NA NA NA NA NA
# 6: c 5 6 7 8 9 10 11 12 13
read.table
选项更好地扩展,因此在30K行的比较中并不能很好地展示它。将复制次数更改为100K,并进行比较。此外,cSplit
具有一个有趣的功能,可以让您即时创建“长”数据集 :-) - A5C1D2H2I1M1N2O1R2T1read.table
函数读取300K行的“文件”需要160.323秒,而使用我的cSplit
+ fread
方法只需要2.769秒。 - A5C1D2H2I1M1N2O1R2T1Scanner scan = new Scanner(file);
while (scan.hasNextLine()) {
String[] a = scan.nextLine().replace("\\t", ",").split(",");
//do something with the array
}
scan.close();
这个程序:
textConnection
,因为read.table
有一个可以代替它的text
参数。 - A5C1D2H2I1M1N2O1R2T1text=
只能接受单个字符的字符串,但是我刚刚测试了你的建议,并确认它确实可以接受由多个元素组成的字符向量。 - Josh O'Brien"file.txt"
),然后在括号之间的字符列表中添加冒号。有关R正则表达式语法的帮助,请参见?regex
。祝你好运。 - Josh O'Brien