【问题标题】:How to Read Certain Lines of A Data File Into R如何将数据文件的某些行读入 R
【发布时间】:2016-06-20 12:50:12
【问题描述】:

我有一个包含 40,000 多行的大型数据文件。它是一个日志输入列表,看起来有点像这样:

    D 20160602 14:15:43.559 F7982D62 Req Agr:131 Mra:0 Exp:0 Mxr:0 Mnr:0 Mxd:0 Mnd:0 Nro:0      
    D 20160602 14:15:43.559 F7982D62 Set Agr:130 Mra:0 Exp:0 Mxr:0 Mnr:0 Mxd:0 Mnd:0 Nro:0 I 20160602 14:15:43.559 F7982D62 GET 156.145.15.85:36773 xqixh8sl AES "/pcgc/public/Other/exome/fastq/PCGC0065109_HS_EX__1-04692__v3_FCAD2HMUACXX_L4_p1of1_P2.fastq.gz" "" 
    M 20160602 14:15:43.595 DOC1: F7982D62 Request for unencrypted meta data on encrypted transaction
    M 20160602 14:15:48.353 DOC1: F7982D62 Transaction has been acknowledged at 722875647 
    F 20160602 14:15:48.398 F7982D62 GET 156.145.15.85:36773 xqixh8sl AES "/pcgc/public/Other/exome/fastq/PCGC0065109_HS_EX__1-04692__v3_FCAD2HMUACXX_L4_p1of1_P2.fastq.gz" "" 50725464 (4,32) "Remote Application: Session Aborted: Aborted by user interrupt" 
    M 20160602 14:15:48.780 DOC1: F7982D63 New download request D 20160602 14:15:48.780 F7982D63 META: 134 Path: /pcgc/public/CTD/exome/fastq/PCGC0033175_HS_EX__1-00304-01__v1_FCBC0RE4ACXX_L3_p32of96_P2.fastq.gz user: xqixh8sl pack: arg: feat: cE,s

既然它这么大,我不想把整个东西读入内存。我只需要以行标识符“F”开头并有 (0, 0) 错误的行,如下所示:

    F 20160602 14:25:11.321 F7982D50 GET 156.145.15.85:37525 xqixh8sl AES "/pcgc/public/Other/exome/fastq/PCGC0077248_HS_EX__1-06808__v3_FCC49HJACXX_L7_p1of1_P1.fastq.gz" "" 3322771022 (0,0) "1499.61 seconds (17.7 megabits/sec)"

我可以忽略的所有其他内容。我的问题是:我想要一种方法来逐行读取此文件并评估它是否需要保留该行以供导入。目前,我正在使用for 循环遍历每一行并使用readLines() 函数。它看起来像这样:

library(stringr)
con <- file("dataSet.txt", open = "r")
Fdata <- data.frame
i <- 1
j <- 1
lineLength <- length(readLines(con))
for (i in 1:lineLength){
  line <- readLines("dataSet.txt", 1)
  if (str_sub(line, 1, 1) == 'F' && grepl("\\(0\\,0\\)", line)[i]){
    print(line)
    Fdata[j,] <- rbind(line)
    i <- i + 1
    j <- j + 1
  }
  i <- i + 1
}
print(Fdata)

它运行良好,但它给我的输出不是我想要的。它只是一遍又一遍地打印文件的第一行。

    [1] "C 20160525 05:27:47.915 Rotated log file: /var/log/servedat-201605250527.log"
    [1] "C 20160525 05:27:47.915 Rotated log file: /var/log/servedat-201605250527.log"
    [1] "C 20160525 05:27:47.915 Rotated log file: /var/log/servedat-201605250527.log"
    [1] "C 20160525 05:27:47.915 Rotated log file: /var/log/servedat-201605250527.log"

我怎样才能让它评估我是否需要这条线,以及如何正确存储它(作为向量、数据框、矩阵,这并不重要),以便我可以在外面打印它for循环?

更新

我已将代码更改为:

    library(stringr)
    con <- file("dataSet.txt", open = "r")
    Fdata <- data.frame
    i <- 1
    j <- 1
    lineLength <- length(readLines(con))
    for (i in 1:lineLength){
      line <- readLines(con, 1)
      print(line)
      if (str_sub(line, 1, 1) == 'F' && grepl("\\(0\\,0\\)", line)[i]){
        print(line)
        Fdata[j,] <- rbind(line)
        i <- i + 1
        j <- j + 1
      }
      i <- i + 1
    }
    print(Fdata)

但是,当我检查存储在行中的值时,它说它是空的。我不明白为什么它改变了。此外,它告诉我 if 语句没有正确的 TRUE/FALSE 条件,这也让我感到困惑,因为 grepl() 应该返回 TRUE/FALSE 值。

更新

我设法摆脱了错误,但是当我调用 Fdata 时仍然没有得到任何东西。我检查了我的变量,R 说那行是空的,它没有字符。我分配不正确吗?我希望 line 成为我在数据文件中解析并评估是否需要存储它的行。这是我更新的代码:

library(stringr)
con <- file("dataSet.txt", open = "r")
Fdata <- data.frame
i <- 1
j <- 1
lineLength <- length(readLines("dataSet.txt))
for (i in 1:lineLength){
  line <- readLines(con, 1)
  print(line)
  if (str_sub(line, 1, 1) == 'F' && grepl("\\(0\\,0\\)", line)){
    print(line)
    Fdata[j,] <- rbind(line)
    i <- i + 1
    j <- j + 1
  }
  i <- i + 1
}
print(Fdata) 

【问题讨论】:

  • 您需要将文件连接传递到readLines,而不是用字符串指定。将 for 循环中的第一行更改为 line &lt;- readLines(con, 1) 应该可以解决这里的问题。
  • 另外我认为你不需要转义逗号。使用grepl("\\(0,0\\), line)
  • 您遇到的问题是grepl("\\(0\\,0\\)", line)[i],其中i 可能是一个很大的数字,而grepl 返回长度为1 的向量。删除[i] 应该可以使其工作。
  • 它消除了错误,但是当我调用 Fdata 时仍然没有打印任何内容。当我检查 line 变量时,R 一直说它是空的。我是不是分配错了?
  • 您遇到的另一个问题是lineLength &lt;- length(readLines(con))。对于文件流,您只能读取一次,因为您在 for 循环之前已读取它,con 指向文件末尾,因此您将在 for 循环中不再读取任何内容。而且你的程序两次读取文件,这似乎偏离了你构建整个想法的最初目的。请检查我在下面给出的答案。用你的文件名替换文件名。

标签: r line


【解决方案1】:

看看这个:

con <- file("test1.txt", "r")
lines <- c()
while(TRUE) {
  line = readLines(con, 1)
  if(length(line) == 0) break
  else if(grepl("^\\s*F{1}", line) && grepl("(0,0)", line, fixed = TRUE)) lines <- c(lines, line)
}

lines
# [1] "F 20160602 14:25:11.321 F7982D50 GET 156.145.15.85:37525 xqixh8sl AES \"/pcgc/public/Other/exome/fastq/PCGC0077248_HS_EX__1-06808__v3_FCC49HJACXX_L7_p1of1_P1.fastq.gz\" \"\" 3322771022 (0,0) \"1499.61 seconds (17.7 megabits/sec)\""

将文件流传递给readLines,以便它可以逐行读取。使用正则表达式^\\s*F{1} 捕获以字母F 开头的行,其中^ 表示字符串的开头。使用fixed=T 捕获(0,0) 的完全匹配。如果两个检查都是TRUE,则将结果附加到行。

数据

D 20160602 14:15:43.559 F7982D62 Req Agr:131 Mra:0 Exp:0 Mxr:0 Mnr:0 Mxd:0 Mnd:0 Nro:0      
D 20160602 14:15:43.559 F7982D62 Set Agr:130 Mra:0 Exp:0 Mxr:0 Mnr:0 Mxd:0 Mnd:0 Nro:0 I 20160602 14:15:43.559 F7982D62 GET 156.145.15.85:36773 xqixh8sl AES "/pcgc/public/Other/exome/fastq/PCGC0065109_HS_EX__1-04692__v3_FCAD2HMUACXX_L4_p1of1_P2.fastq.gz" "" 
M 20160602 14:15:43.595 DOC1: F7982D62 Request for unencrypted meta data on encrypted transaction
M 20160602 14:15:48.353 DOC1: F7982D62 Transaction has been acknowledged at 722875647 
F 20160602 14:15:48.398 F7982D62 GET 156.145.15.85:36773 xqixh8sl AES "/pcgc/public/Other/exome/fastq/PCGC0065109_HS_EX__1-04692__v3_FCAD2HMUACXX_L4_p1of1_P2.fastq.gz" "" 50725464 (4,32) "Remote Application: Session Aborted: Aborted by user interrupt" 
M 20160602 14:15:48.780 DOC1: F7982D63 New download request D 20160602 14:15:48.780 F7982D63 META: 134 Path: /pcgc/public/CTD/exome/fastq/PCGC0033175_HS_EX__1-00304-01__v1_FCBC0RE4ACXX_L3_p32of96_P2.fastq.gz user: xqixh8sl pack: arg: feat: cE,s
F 20160602 14:25:11.321 F7982D50 GET 156.145.15.85:37525 xqixh8sl AES "/pcgc/public/Other/exome/fastq/PCGC0077248_HS_EX__1-06808__v3_FCC49HJACXX_L7_p1of1_P1.fastq.gz" "" 3322771022 (0,0) "1499.61 seconds (17.7 megabits/sec)"

【讨论】:

  • 非常感谢!这很好用。一个简单的问题:由于 while 循环仅在条件为真时运行,这是否意味着它会在遇到使条件为假的行时立即停止?我对它为什么起作用感到有点困惑。其他一切都有意义。
  • 是的,这是真的,这就是我们在这里使用的逻辑。我们将条件指定为始终为 TRUE,并且每当我们到达最后一行(这意味着行的长度为零)时,我们就会跳出循环,这是关键字 break 所做的并停止 while。跨度>
  • FWIW,通过就地修改来增长列表比将向量连接到自身要高效得多,这将在内存中为每个分配复制向量。所以向量lines &lt;- c(lines, line) 最好是列表lines[[length(lines)+1]]&lt;-line
【解决方案2】:

如果你有足够的内存,40,000 行对于 R 来说应该不会太多。出于性能原因,最好一次读取所有行并使用向量性能来分析结果。

您的代码可以简化为:

library(stringr)

line <- readLines("dataSet.txt")

foundset<-line[which(str_sub(line, 1, 1) == 'F' & grepl("(0,0)", line, fixed = TRUE))]
#rm("line")  #include this line to free up memory if there is a concern

这会读入所有以字母“F”开头的行和子集。所有这些行都在向量 foundset 中。

【讨论】:

  • 我的部分任务是尝试不将整个数据集读入 R。我已经这样做了,但目标是学习如何逐行阅读。部分原因是效率和性能问题。此外,他们希望我为此编写一个循环,因为我将很快处理数百万行代码,我将不得不解析并只存储我需要的行。不过还是谢谢你的建议。
  • disk.frame 包旨在自动执行此分块。 diskframe.com
【解决方案3】:

类似这样的答案 (What is a good way to read line-by-line in R?) 也可以:

cat('  D 20160602 14:15:43.559 F7982D62 Req Agr:131 Mra:0 Exp:0 Mxr:0 Mnr:0 Mxd:0 Mnd:0 Nro:0',      
    'D 20160602 14:15:43.559 F7982D62 Set Agr:130 Mra:0 Exp:0 Mxr:0 Mnr:0 Mxd:0 Mnd:0 Nro:0 I 20160602 14:15:43.559 F7982D62 GET 156.145.15.85:36773 xqixh8sl AES "/pcgc/public/Other/exome/fastq/PCGC0065109_HS_EX__1-04692__v3_FCAD2HMUACXX_L4_p1of1_P2.fastq.gz" ""',
    'M 20160602 14:15:43.595 DOC1: F7982D62 Request for unencrypted meta data on encrypted transaction',
    'M 20160602 14:15:48.353 DOC1: F7982D62 Transaction has been acknowledged at 722875647',
    'F 20160602 14:15:48.398 F7982D62 GET 156.145.15.85:36773 xqixh8sl AES "/pcgc/public/Other/exome/fastq/PCGC0065109_HS_EX__1-04692__v3_FCAD2HMUACXX_L4_p1of1_P2.fastq.gz" "" 50725464 (4,32) "Remote Application: Session Aborted: Aborted by user interrupt"',
    'M 20160602 14:15:48.780 DOC1: F7982D63 New download request D 20160602 14:15:48.780 F7982D63 META: 134 Path: /pcgc/public/CTD/exome/fastq/PCGC0033175_HS_EX__1-00304-01__v1_FCBC0RE4ACXX_L3_p32of96_P2.fastq.gz user: xqixh8sl pack: arg: feat: cE,s")',
    'F 20160602 14:15:48.398 F7982D62 GET 156.145.15.85:36773 xqixh8sl AES "/pcgc/public/Other/exome/fastq/PCGC0065109_HS_EX__1-04692__v3_FCAD2HMUACXX_L4_p1of1_P2.fastq.gz" "" 50725464 (4,32) "Remote Application: Session Aborted: Aborted by user interrupt" (0,0)',
    file="test",
    sep="\n")


library(stringr)
con  <- file("test", open = "r")
res<-c()

while (length(oneLine <- readLines(con, n = 1, warn = FALSE)) > 0) {
  if (substr(str_trim(oneLine),1,1) =="F" & (regexpr("(0,0)",oneLine)[1] > 0) ){

    res<-c(res,oneLine)
  } 

} 

close(con)
res
[1] "F 20160602 14:15:48.398 F7982D62 GET 156.145.15.85:36773 xqixh8sl AES \"/pcgc/public/Other/exome/fastq/PCGC0065109_HS_EX__1-04692__v3_FCAD2HMUACXX_L4_p1of1_P2.fastq.gz\" \"\" 50725464 (4,32) \"Remote Application: Session Aborted: Aborted by user interrupt\" (0,0)"

请注意,我在其中添加了最后一行,目的是展示 while 循环的工作原理。

【讨论】:

  • 您能解释一下您使用的 cat() 函数吗?另外,既然是这么大的文件,我可以只使用文件名和分隔符而不是复制所有行作为字符串吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-07-16
  • 2012-02-29
  • 1970-01-01
  • 2022-07-06
相关资源
最近更新 更多