如何使用不同的分隔符读取数据?

14

我有一个看起来像这样的文件:

a 1,2,3,5
b 4,5,6,7
c 5,6,7,8
...

数据集中第一列和第二列之间的分隔符是'\t',其他列之间的分隔符是逗号。我该如何将这种数据集读取为一个有5个字段的dataframe。

3个回答

29

我可能会这样做。

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 = 参数使其知道您正在直接传递一个字符向量以供读取(而不是包含文本数据的文件名)。

1
很好的答案,但我认为这里不需要使用textConnection,因为read.table有一个可以代替它的text参数。 - A5C1D2H2I1M1N2O1R2T1
@AnandaMahto - 非常酷!我一直以为 text= 只能接受单个字符的字符串,但是我刚刚测试了你的建议,并确认它确实可以接受由多个元素组成的字符向量。 - Josh O'Brien
我有同样的问题,我的字符中包含“{”和“}”,我想用空白替换它们。这能在同一个gsub()函数中完成吗? - Abhishek Singh
@AbhishekSingh 我建议你自己尝试一下,创建一个小的示例(例如在此示例中使用的 "file.txt"),然后在括号之间的字符列表中添加冒号。有关R正则表达式语法的帮助,请参见 ?regex。祝你好运。 - Josh O'Brien
谢谢Josh。我后来发现这是一个JSON文件,需要以不同的方式读取。再次感谢 :) - Abhishek Singh
显示剩余2条评论

8

平衡数据

从你提出问题的方式来看,似乎你知道你的数据是“平衡”的(矩形)。

你是否正在寻找更快的选项?你可能想将"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具有一个有趣的功能,可以让您即时创建“长”数据集 :-) - A5C1D2H2I1M1N2O1R2T1
在我的系统上,使用read.table函数读取300K行的“文件”需要160.323秒,而使用我的cSplit + fread方法只需要2.769秒。 - A5C1D2H2I1M1N2O1R2T1

1
Scanner scan = new Scanner(file);
while (scan.hasNextLine()) {
    String[] a = scan.nextLine().replace("\\t", ",").split(",");
    //do something with the array
}
scan.close();

这个程序:

  1. 创建一个扫描器来处理文件 (Scanner scan)
  2. 基于hasNextLine(),为每一行文件扫描下一行 (scan.nextLine())
  3. 将制表符替换为逗号 (.replace("\t", ",")),使分隔符都相同
  4. 通过逗号拆分成数组。现在可以处理所有数据,无论每行的长度如何。
  5. 完成后不要忘记关闭扫描器。

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