读取以空格分隔的文本文件,其中第一列也有空格

时间:2013-12-27 19:43:02

标签: r text-files

我正在尝试将文本文件读入 R ,如下所示:

Ant farm 45 67 89
Cookie 5 43 21
Mouse hole 5 87 32
Ferret 3 56 87

我的问题是文件是以空格分隔的,并且第一个变量有一些包含空格的条目,因此读入 R 会因为不同的行包含更多列而产生错误。有谁知道一种方法来阅读这个?

3 个答案:

答案 0 :(得分:2)

将数据集读入character向量(我使用textConnection()以避免创建测试文件;您只能readLines("your_file.txt")):

 r <- readLines(textConnection(
 "Ant farm 45 67 89
 Cookie 5 43 21
 Mouse hole 5 87 32
 Ferret 3 56 87"))

在以空格分隔的单词周围加上(单个)引号:

r2 <- gsub("([[:alpha:]]+) +([[:alpha:]]+)","'\\1 \\2'",r)

(正如@CarlWitthoft建议的那样,如果您不介意用_之类的其他分隔符替换空格,则可以改用gsub(" +([[:alpha:]]+)","_\\1",r)。)

现在阅读结果:

dat <- read.table(textConnection(r2))

如果您的文件很大,最好使用sed等命令行工具在R外部执行此操作...

答案 1 :(得分:2)

Ben的方法效果很好,但这是使用gsubfn package中的`strapplycgsubfnstrapply的另一种方法。

首先读入数据并设置col.names,分隔符和要使用的模式:

r <- readLines(textConnection(
 "Ant farm 45 67 89
Cookie 5 43 21
Mouse hole 5 87 32
Ferret 3 56 87"))

library(gsubfn)

col.names <- c("group", "x1", "x2", "x3")
sep <- ","  # if comma can appear in fields use something else
pat <- "^(.*) +(\\d+) +(\\d+) +(\\d+) *$"

1)gsubfn

tmp <- sapply(strapplyc(r, pat), paste, collapse = sep)
read.table(text = tmp, col.names = col.names, as.is = TRUE, sep = sep)

2)strapplyc 或者相同的代码,但最后两个语句被替换为:

tmp <- gsubfn(pat, ... ~ paste(..., sep = sep), r)
read.table(text = tmp, col.names = col.names, as.is = TRUE, sep = sep)

3)strapply 。这个以及后面的变体不需要定义sep

library(data.table)
tmp <- strapply(r, pat,
  ~ data.table(
      group = group, 
      x1 = as.numeric(x1), 
      x2 = as.numeric(x2), 
      x3 = as.numeric(x3)
    ))
rbindlist(tmp)

3a)这个涉及一些额外的操作,所以我们可能会赞成其他解决方案之一,但为了完整性,这里是。 combine=list会阻止单个输出被激活,simplify=c会删除combine=list添加的额外图层。最后,我们rbind将所有内容放在一起。

tmp <- strapply(r, pat,
  ~ data.frame(
      group = group, 
      x1 = as.numeric(x1), 
      x2 = as.numeric(x2), 
      x3 = as.numeric(x3),
      stringsAsFactors = FALSE
    ), combine = list, simplify = c)
do.call(rbind, tmp)

4)read.pattern gsubfn软件包的开发版本有一个新函数read.pattern,对于这类问题特别直接:

library(devtools) # source_url
source_url("https://gsubfn.googlecode.com/svn/trunk/R/read.pattern.R") # from dev repo

read.pattern(text = r, pattern = pat, col.names = col.names, as.is = TRUE)

注意:这些方法有几个优点(尽管Ben的方法也可以针对这些情况进行修改)。这种方法在最后3个数字之前取任何东西并将其用作第一个字段,因此如果第一个字段有3个或更多字,或者其中一个“字”是一组数字(例如“17英寸蚂蚁农场”)那么它仍然有效。

答案 2 :(得分:2)

假设您使用的是linux或osx,并且要读取的文件名为test

read.table(pipe('perl -pe "s/(\\D+) (\\d+) (\\d+) (\\d+)/\\1\t\\2\t\\3\t\\4/" test'), sep='\t')

您还可以使用相同的方法创建更通用的函数来读取任何类型化的输入

read_typed = function(file, types, sep=' ', ...){

  all_types = c('character' = '([\\w ]+)', 'integer' = '(\\d+)', 'numeric' = '([\\d.eE\\-+]+)', 'logical' = '([TF]|TRUE|FALSE)')
  command = paste0('perl -pe "s/', paste0(all_types[types], collapse=sep),
                   '/',
                   paste0('\\', seq_along(types), collapse='\t'),
                   '/" ', file)
  read.table(sep='\t', pipe(command), ...)
}
read_typed('test', c("character", 'integer', 'integer', 'integer'))