从文本文件中提取模式之间的数据

时间:2016-06-06 17:13:58

标签: r regex import

我正在使用文本文件,其中的数据如下所示

*******************************
Sensor 1028 at site 101
SID = 16384
Tag = AI.1028.BT.VOLT
04/07/16 05:00:00  12.65
04/07/16 06:00:00  12.64
04/07/16 07:00:00  12.68
04/07/16 08:00:00  13.08
04/07/16 09:00:00  13.76
*******************************
Sensor 1171 at well 102
SID = 20062
Tag = AI.1171.WT.LEV
04/07/16 05:00:00  0.95
04/07/16 06:00:00  0.90
04/07/16 07:00:00  0.82
04/07/16 08:00:00  0.71
04/07/16 09:00:00  0.59
04/07/16 10:00:00  0.48

我希望能够提取每个标签的数据并创建一个数据框,如下所示 -

Tag  Timestamp          Value
1028 04/07/16 05:00:00  12.65
1028 04/07/16 06:00:00  12.64
1028 04/07/16 07:00:00  12.68
1028 04/07/16 08:00:00  13.08
1028 04/07/16 09:00:00  13.76
1171 04/07/16 05:00:00  0.95
1171 04/07/16 06:00:00  0.90
1171 04/07/16 07:00:00  0.82
1171 04/07/16 08:00:00  0.71
1171 04/07/16 09:00:00  0.59
1171 04/07/16 10:00:00  0.48

标签是模式中的数字部分,例如“Tag = AI.1028.BT.VOLT”中的1028和“Tag = AI.1171.WT.LEV”中的1171.

我已经查看了类似行上的其他问题,但我对R相对较新,除了使用readLines导入文本文件并使用grep提取模式之外,我无法做任何事情。

非常感谢任何帮助。谢谢!

3 个答案:

答案 0 :(得分:5)

使用data.table包,我会按如下方式处理:

sensortext <- readLines('sensors.txt')

library(data.table)
DT <- data.table(txt = sensortext[!grepl(pattern = '\\*+', sensortext)])

DT <- DT[, grp := cumsum(grepl('Sensor', txt))
         ][, `:=` (tag = as.numeric(gsub('^.*(\\d+{4}).*','\\1', grep('Tag =', txt, value = TRUE))),
                   sid = as.numeric(gsub('^.*(\\d+{5}).*','\\1', grep('SID = ', txt, value = TRUE))),
                   type = strsplit(grep('Sensor ', txt, value = TRUE),' ')[[1]][4],
                   type.nr = as.numeric(gsub('^.*(\\d+{3}).*','\\1', grep('Sensor ', txt, value = TRUE)))), 
           by = grp
           ][, .SD[4:.N], by = grp
             ][, c('datetime','value') := tstrsplit(txt, '\\s+{2}', type.convert = TRUE)
               ][, c('grp','txt') := NULL
                 ][, datetime := as.POSIXct(strptime(datetime, "%d/%m/%y %H:%M:%S"))]

给出:

> DT
     tag   sid type type.nr            datetime value
 1: 1028 16384 site     101 2016-07-04 05:00:00 12.65
 2: 1028 16384 site     101 2016-07-04 06:00:00 12.64
 3: 1028 16384 site     101 2016-07-04 07:00:00 12.68
 4: 1028 16384 site     101 2016-07-04 08:00:00 13.08
 5: 1028 16384 site     101 2016-07-04 09:00:00 13.76
 6: 1171 20062 well     102 2016-07-04 05:00:00  0.95
 7: 1171 20062 well     102 2016-07-04 06:00:00  0.90
 8: 1171 20062 well     102 2016-07-04 07:00:00  0.82
 9: 1171 20062 well     102 2016-07-04 08:00:00  0.71
10: 1171 20062 well     102 2016-07-04 09:00:00  0.59
11: 1171 20062 well     102 2016-07-04 10:00:00  0.48

解释

  • 使用readLines功能,您可以阅读文本文件。之后,将其转换为1列数据表data.table(txt = sensortext[!grepl(pattern = '\\*+', sensortext)])
  • 使用[, grp := cumsum(grepl('Sensor', txt))]创建一个分隔不同数据集的分组变量。 grepl('Sensor', txt)创建一个逻辑值,检测以Sensor开头的行(并指示新数据空间的开始)。使用cumsum创建分组变量。
  • 使用tag = as.numeric(gsub('^.*(\\d+{4}).*','\\1', grep('Tag =', txt, value = TRUE))),您可以提取标记号(以及sidtypetype.nr)。
  • 使用[, .SD[4:.N], by = grp],您可以删除每个组的前三行(因为它们不包含数据,并且在前面的步骤中已经提取了所需的信息)。
  • 使用[, c('datetime','value') := tstrsplit(txt, '\\s+{2}', type.convert = TRUE)],您将txt列中仍为文本格式的数据转换为三个数据列。 type.convert = TRUE确保value列获得正确的格式(在这种情况下为数字)。
  • 使用grp删除txt[, c('grp','txt') := NULL]列(因为不再需要它们)。
  • 最后使用datetimePOSIXct列格式转换为as.POSIXct(strptime(datetime, "%d/%m/%y %H:%M:%S"))格式。

要查看每个步骤的作用,您还可以使用以下代码:

DT[, grp := cumsum(grepl('Sensor', txt))]
DT[, `:=` (tag = as.numeric(gsub('^.*(\\d+{4}).*','\\1', grep('Tag =', txt, value = TRUE))),
           sid = as.numeric(gsub('^.*(\\d+{5}).*','\\1', grep('SID = ', txt, value = TRUE))),
           type = strsplit(grep('Sensor ', txt, value = TRUE),' ')[[1]][4],
           type.nr = as.numeric(gsub('^.*(\\d+{3}).*','\\1', grep('Sensor ', txt, value = TRUE)))),
   by = grp][]
DT <- DT[, .SD[4:.N], by = grp][]
DT[, c('datetime','value') := tstrsplit(txt, '\\s+{2}', type.convert = TRUE)][]
DT[, c('grp','txt') := NULL][]
DT[, datetime := as.POSIXct(strptime(datetime, "%d/%m/%y %H:%M:%S"))][]

向每一行添加[],确保将结果打印到控制台。

基础R的替代方案:

sensortext <- readLines('sensors.txt')

rawlist <- split(sensortext, cumsum(grepl(pattern = '\\*+', sensortext)))
l <- lapply(rawlist, function(x) read.fwf(textConnection(x[-c(1:4)]), widths = c(17,7), header = FALSE))
reps <- sapply(l, nrow)

df <- do.call(rbind, l)
df$V1 <- strptime(df$V1, '%d/%m/%y %H:%M:%S')
names(df) <- c('datetime','value')

df$tag <- rep(as.numeric(gsub('^.*(\\d+{4}).*','\\1', grep('Tag =', sensortext, value = TRUE))), reps)
df$sid  <- rep(as.numeric(gsub('^.*(\\d+{5}).*','\\1', grep('SID = ', sensortext, value = TRUE))), reps)
df$type  <- rep(sapply(strsplit(grep('Sensor ', sensortext, value = TRUE),' '), '[', 4), reps)
df$type.nr <- rep(as.numeric(gsub('^.*(\\d+{3}).*','\\1', grep('Sensor ', sensortext, value = TRUE))), reps)

给出相同的结果:

> df
               datetime value  tag   sid type type.nr
1.1 2016-07-04 05:00:00 12.65 1028 16384 site     101
1.2 2016-07-04 06:00:00 12.64 1028 16384 site     101
1.3 2016-07-04 07:00:00 12.68 1028 16384 site     101
1.4 2016-07-04 08:00:00 13.08 1028 16384 site     101
1.5 2016-07-04 09:00:00 13.76 1028 16384 site     101
2.1 2016-07-04 05:00:00  0.95 1171 20062 well     102
2.2 2016-07-04 06:00:00  0.90 1171 20062 well     102
2.3 2016-07-04 07:00:00  0.82 1171 20062 well     102
2.4 2016-07-04 08:00:00  0.71 1171 20062 well     102
2.5 2016-07-04 09:00:00  0.59 1171 20062 well     102
2.6 2016-07-04 10:00:00  0.48 1171 20062 well     102

答案 1 :(得分:2)

这是一个基础R解决方案。我们找到以Sensor开头的行或日期,然后在每对Sensor行之间提取后面的行。

date_rows <- grep("^\\d+/", s)
#  [1]  4  5  6  7  8 13 14 15 16 17 18
sensor_rows <- grep("^Sensor", s)
# [1]  1 10

tab <- lapply(seq_along(sensor_rows), function(x) {
  tag <- sub("^Sensor (\\d+)+.*", "\\1", s[sensor_rows[x]])
  last_row <- ifelse(is.na(sensor_rows[x + 1L]), length(s), sensor_rows[x + 1L] - 1L)
  rows <- seq.int(sensor_rows[x], last_row)
  # extract date rows between the current tag line and the next
  values <- s[intersect(rows, date_rows)]
  data.frame(tag = rep(tag, length(values)), value = values, stringsAsFactors = FALSE)
})
tab <- do.call("rbind", tab)

到目前为止,我们已为每行数据分配了一个标记,并将其移到表格中。

head(tab)
#    tag                    value
# 1 1028 04/07/16 05:00:00  12.65
# 2 1028 04/07/16 06:00:00  12.64
# 3 1028 04/07/16 07:00:00  12.68
# 4 1028 04/07/16 08:00:00  13.08
# 5 1028 04/07/16 09:00:00  13.76
# 6 1171  04/07/16 05:00:00  0.95

剩下的就是在值列中拆分字符串。顺便说一下,这将是data.table的tstrsplit。

split_values <- t(data.frame(strsplit(tab$value, "\\s+"), stringsAsFactors = FALSE))
colnames(split_values) <- c("date", "time", "value")
tab <- as.data.frame(cbind(tag = tab[, "tag"], split_values), stringsAsFactors = FALSE,
                     row.names = FALSE)

tab$timestamp <- paste(tab$date, tab$time)
tab <- tab[, c("tag", "timestamp", "value")]
str(tab)
# 'data.frame': 11 obs. of  3 variables:
#  $ tag      : chr  "1028" "1028" "1028" "1028" ...
#  $ timestamp: chr  "04/07/16 05:00:00" "04/07/16 06:00:00" "04/07/16 07:00:00" "04/07/16 08:00:00" ...
#  $ value    : chr  "12.65" "12.64" "12.68" "13.08" ...

以下是我阅读数据的方式:

s = scan(what = "character", fill = TRUE, blank.lines.skip = TRUE, sep = "\n",
     text = "Sensor 1028 at site 101
SID = 16384
Tag = AI.1028.BT.VOLT
04/07/16 05:00:00  12.65
04/07/16 06:00:00  12.64
04/07/16 07:00:00  12.68
04/07/16 08:00:00  13.08
04/07/16 09:00:00  13.76
*******************************
Sensor 1171 at well 102
SID = 20062
Tag = AI.1171.WT.LEV
04/07/16 05:00:00  0.95
04/07/16 06:00:00  0.90
04/07/16 07:00:00  0.82
04/07/16 08:00:00  0.71
04/07/16 09:00:00  0.59
04/07/16 10:00:00  0.48")

答案 2 :(得分:1)

这是使用dplyr,tidyr和我在GitHub上的文本整形包进行分割文件的一种方法。

您的数据

   x <- c("*******************************", "Sensor 1028 at site 101", 
        "SID = 16384", "Tag = AI.1028.BT.VOLT", "04/07/16 05:00:00  12.65", 
        "04/07/16 06:00:00  12.64", "04/07/16 07:00:00  12.68", "04/07/16 08:00:00  13.08", 
        "04/07/16 09:00:00  13.76", "*******************************", 
        "Sensor 1171 at well 102", "SID = 20062", "Tag = AI.1171.WT.LEV", 
        "04/07/16 05:00:00  0.95", "04/07/16 06:00:00  0.90", "04/07/16 07:00:00  0.82", 
        "04/07/16 08:00:00  0.71", "04/07/16 09:00:00  0.59", "04/07/16 10:00:00  0.48"
        )

读取和重塑数据的代码

if (!require("pacman")) install.packages("pacman"); library(pacman)
p_load_current_gh('trinker/textshape')
p_load(dplyr, tidyr)

## x <- readLines('myfile.txt') 
split_match(x, "\\*{5,}", regex=TRUE) %>%  ## find multi asterisks and split
    lapply(function(x){

        data_frame(Timestamp_Value = x[-c(1:3)]) %>%  ## make dataframe of n-first 3 elements
            separate(Timestamp_Value, c('Timestamp', 'Value'), sep = "\\s{2,}") %>%  # split timestamp and value apart
            mutate(Tag = gsub("(^[A-Za-z ]+)(\\d+)(\\s.+$)", "\\2", x[1])) %>%  ## add Tag with regex gsub
            select(3, 1:2)
    }) %>%
    bind_rows() %>%  ## bind list of data frames together
    mutate(
        Tag = as.numeric(Tag),
        Timestamp = as.POSIXct(strptime(Timestamp, "%d/%m/%y %H:%M:%S")),
        Value = as.numeric(Value)
    )  ## add appropriate classes

收率:

Source: local data frame [11 x 3]

     Tag           Timestamp Value
   (dbl)              (time) (dbl)
1   1028 2016-07-04 05:00:00 12.65
2   1028 2016-07-04 06:00:00 12.64
3   1028 2016-07-04 07:00:00 12.68
4   1028 2016-07-04 08:00:00 13.08
5   1028 2016-07-04 09:00:00 13.76
6   1171 2016-07-04 05:00:00  0.95
7   1171 2016-07-04 06:00:00  0.90
8   1171 2016-07-04 07:00:00  0.82
9   1171 2016-07-04 08:00:00  0.71
10  1171 2016-07-04 09:00:00  0.59
11  1171 2016-07-04 10:00:00  0.48