【问题标题】:Using R and regex expressions to convert delimited file to csv使用 R 和正则表达式将分隔文件转换为 csv
【发布时间】:2017-09-26 01:14:33
【问题描述】:

我正在尝试将原始气象站数据从数据记录器转换为易于使用的 csv 文件。数据如下所示。数据采用这种空格分隔格式,其中第一行数据有 47 列,第一个值等于 111,第 47 个等于 329.1。第二行的第一个值也是111,第47个是354.2。并非所有行都具有相同的列数,任​​何数字前面的“-”符号表示这是一个负数。

01+0111.  02+0262.  03+1900.  04-15.68  05+64.50  06+08.82  07+1.013  08+0.943
09+342.1  10+21.26  11+0.000  12+31.76  13+18.46  14+16.50  15+1800.  16+5.250
17+69.46  18+1859.  19+55.25  20+27.61  21+1808.  22+50.25  23+2.367  24+1806.
25+15.25  26+14.78  27+1859.  28+55.25  29+60.11  30+1800.  31-5.250  32+0.000
33+1854.  34+5.250  35+0.447  36+1819.  37+50.25  38+14.80  39+69.40  40+0.073
41+275.3  42+0.447  43+18.29  44+22.30  45+22.43  46+2.367  47+329.1
01+0111.  02+0262.  03+2000.  04-14.28  05+070.7  06+0.012  07+0.755  08+0.694
09+337.5  10+22.90  11+0.000  12+0.044  13+18.13  14+14.78  15+1900.  16+15.25
17+072.6  18+1908.  19+15.25  20+0.146  21+1946.  22+10.25  23+1.567  24+1948.
25+25.25  26+14.02  27+1959.  28+20.25  29+69.21  30+1936.  31-25.25  32+0.000
33+1900.  34+20.25  35+0.447  36+1900.  37+5.250  38+14.02  39+69.95  40+0.000
41+343.6  42+0.607  43+17.97  44+21.97  45+22.13  46+1.567  47+354.2
01+0111.  02+0262.  03+2100.  04-13.01  05+075.7  06+0.007  07+0.617  08+0.528
09+20.10  10+30.68  11+0.000  12+0.026  13+17.79  14+14.02  15+2000.  16+5.250
17+082.7  18+2050.  19+55.25  20+0.146  21+2028.  22+30.25  23+1.407  24+2001.
25+25.25  26+11.78  27+2051.  28+40.25  29+69.68  30+2001.  31-25.25  32+0.000
33+2000.  34+5.250  35+0.447  36+2002.  37+25.25  38+12.00  39+081.0  40+0.000
41+39.42  42+0.447  43+17.61  44+21.68  45+21.82  46+1.407  47+349.4
01+0111.  02+0262.  03+1900.  04-15.68  05+64.50  06+08.82  
01+0111.  02+0262.  03+2100.  04-13.01  05+075.7  06+0.007  07+0.617  08+0.528  
09+20.10  10+30.68  11+0.000  

我正在这样读取数据:

test <- readLines(data)

这会导致一个数据框,其中每个观察都由一行数据组成,采用字符格式,这是没有用的。也许有办法解决这个问题,但我尝试了很多方法,但没有运气。我确信有一种方法可以将上述数据读入 5 行,每行包含适当数量的列和逗号分隔,并且每个值前面没有列号(如下所示)。但是,我不知道该怎么做,尤其是使用正则表达式。如果有人可以提供帮助,我将不胜感激。谢谢你。

111,262,1900,-15.68,64.50,8.82,1.013,0.943,342.1,21.26,0,31.76,18.46,16.50,1800,5.250,69.46,1859,55.25,27.61,1808,50.25,2.367,1806,15.25,14.78,1859,55.25,60.11,1800,-5.250,0,1854,5.250,0.447,1819,50.25,14.80,69.40,0.073,275.3,0.447,18.29,22.30,22.43,2.367,329.1
111,262,2000,-14.28,70.7,0.012,0.755,0.694,337.5,22.90,0,0.044,18.13,14.78,1900,15.25,072.6,1908,15.25,0.146,1946,10.25,1.567,1948,25.25,14.02,1959,20.25,69.21,1936,-25.25,0,1900,20.25,0.447,1900,5.250,14.02,69.95,0,343.6,0.607,17.97,21.97,22.13,1.567,354.2
111,262,2100,-13.01,75.7,0.007,0.617,0.528,20.10,30.68,0,0.026,17.79,14.02,2000,5.250,082.7,2050,55.25,0.146,2028,30.25,1.407,2001,25.25,11.78,2051,40.25,69.68,2001,-25.25,0,2000,5.250,0.447,2002,25.25,12.00,081.0,0,39.42,0.447,17.61,21.68,21.82,1.407,349.4
111,262,1900,-15.68,64.50,8.82  
111,262,2100,-13.01,75.7,0.007,0.617,0.528,20.10,30.68,0  

This is what my data looks like after I read it in:
c("01+0111.  02+0262.  03+1900.  04-15.68  05+64.50  06+08.82  
07+1.013       08+0.943", 
"09+342.1  10+21.26  11+0.000  12+31.76  13+18.46  14+16.50  15+1800.
16+5.250", 
"17+69.46  18+1859.  19+55.25  20+27.61  21+1808.  22+50.25  23+2.367  
24+1806.", 
"25+15.25  26+14.78  27+1859.  28+55.25  29+60.11  30+1800.  31-5.250  
32+0.000", 
"33+1854.  34+5.250  35+0.447  36+1819.  37+50.25  38+14.80  39+69.40
40+0.073", 
"41+275.3  42+0.447  43+18.29  44+22.30  45+22.43  46+2.367  47+329.1", 
"01+0111.  02+0262.  03+2000.  04-14.28  05+070.7  06+0.012  07+0.755   
08+0.694", 
"09+337.5  10+22.90  11+0.000  12+0.044  13+18.13  14+14.78  15+1900. 
16+15.25", 
"17+072.6  18+1908.  19+15.25  20+0.146  21+1946.  22+10.25  23+1.567 
24+1948.", 
"25+25.25  26+14.02  27+1959.  28+20.25  29+69.21  30+1936.  31-25.25  
32+0.000", 
"33+1900.  34+20.25  35+0.447  36+1900.  37+5.250  38+14.02  39+69.95     
40+0.000", 
"41+343.6  42+0.607  43+17.97  44+21.97  45+22.13  46+1.567  47+354.2", 
"01+0111.  02+0262.  03+2100.  04-13.01  05+075.7  06+0.007  07+0.617 
08+0.528", 
"09+20.10  10+30.68  11+0.000  12+0.026  13+17.79  14+14.02  15+2000.    
16+5.250", 
"17+082.7  18+2050.  19+55.25  20+0.146  21+2028.  22+30.25  23+1.407  
24+2001.", 
"25+25.25  26+11.78  27+2051.  28+40.25  29+69.68  30+2001.  31-25.25 
32+0.000", 
"33+2000.  34+5.250  35+0.447  36+2002.  37+25.25  38+12.00  39+081.0 
40+0.000", 
"41+39.42  42+0.447  43+17.61  44+21.68  45+21.82  46+1.407  47+349.4", 
"01+0111.  02+0262.  03+1900.  04-15.68  05+64.50  06+08.82", 
"01+0111.  02+0262.  03+2100.  04-13.01  05+075.7  06+0.007  07+0.617
08+0.528", 
"09+20.10  10+30.68  11+0.000")

This is the result of dput(text) after running the code:
structure(list(X01 = c(111, 342.1, 69.46, 15.25, 1854, 275.3, 
111, 337.5, 72.6, 25.25, 1900, 343.6, 111, 20.1, 82.7, 25.25, 
2000, 39.42, 111, 111, 20.1), X02 = c(262, 21.26, 1859, 14.78, 
5.25, 0.447, 262, 22.9, 1908, 14.02, 20.25, 0.607, 262, 30.68, 
2050, 11.78, 5.25, 0.447, 262, 262, 30.68), X03 = c(1900, 0, 
55.25, 1859, 0.447, 18.29, 2000, 0, 15.25, 1959, 0.447, 17.97, 
2100, 0, 55.25, 2051, 0.447, 17.61, 1900, 2100, 0), X04 = c(-15.68, 
31.76, 27.61, 55.25, 1819, 22.3, -14.28, 0.044, 0.146, 20.25, 
1900, 21.97, -13.01, 0.026, 0.146, 40.25, 2002, 21.68, -15.68, 
-13.01, NA), X05 = c(64.5, 18.46, 1808, 60.11, 50.25, 22.43, 
70.7, 18.13, 1946, 69.21, 5.25, 22.13, 75.7, 17.79, 2028, 69.68, 
25.25, 21.82, 64.5, 75.7, NA), X06 = c(8.82, 16.5, 50.25, 1800, 
14.8, 2.367, 0.012, 14.78, 10.25, 1936, 14.02, 1.567, 0.007, 
14.02, 30.25, 2001, 12, 1.407, 8.82, 0.007, NA), X07 = c(1.013, 
1800, 2.367, -5.25, 69.4, 329.1, 0.755, 1900, 1.567, -25.25, 
69.95, 354.2, 0.617, 2000, 1.407, -25.25, 81, 349.4, NA, 0.617, 
NA), X08 = c(0.943, 5.25, 1806, 0, 0.073, NA, 0.694, 15.25, 1948, 
0, 0, NA, 0.528, 5.25, 2001, 0, 0, NA, NA, 0.528, NA), X09 = c(NA_real_, 
NA_real_, NA_real_, NA_real_, NA_real_, NA_real_, NA_real_, NA_real_, 
NA_real_, NA_real_, NA_real_, NA_real_, NA_real_, NA_real_, NA_real_, 
NA_real_, NA_real_, NA_real_, NA_real_, NA_real_, NA_real_), 
X10 = c(NA_real_, NA_real_, NA_real_, NA_real_, NA_real_, 
NA_real_, NA_real_, NA_real_, NA_real_, NA_real_, NA_real_, 
NA_real_, NA_real_, NA_real_, NA_real_, NA_real_, NA_real_, 
NA_real_, NA_real_, NA_real_, NA_real_), X11 = c(NA_real_, 
.....)

【问题讨论】:

  • 在您的问题中使用dput(test) 来显示您的数据在您阅读后的样子
  • 您从中读取数据的原始文件类型是什么?
  • Richard,这是一个 .dat 文件。

标签: r regex csv


【解决方案1】:

您没有明确说明数据的格式,但似乎每个字段的前缀 nn+ 是列号,应该忽略。 如果是这样,删除它的简单模式可能会有所帮助。但这不适用于负数。

> line <- "01+0111.  02+0262.  03+1900.  04-15.68  05+64.50  06+08.82  07+1.013  08+0.943"
> gsub("\\d\\d\\+", "", line)
[1] "0111.  0262.  1900.  04-15.68  64.50  08.82  1.013  0.943"

是否有任何理由将它们保留为 5 行最多 47 列的形式,但列数可能因行而异?

【讨论】:

  • 我无法控制逐行变化的列数,但之所以如此,是因为某些行按小时或按天汇总数据。
【解决方案2】:

假设您可以忽略 +/- 符号之前的数字并且它们对于每一行都是连续的,您可以将其转换为具有以下数字的数据框:

library(dplyr)
library(stringr)
library(tidyr)
test %>% 
  as_data_frame %>% 
  separate(value, into = sprintf("X%02d", 1:47), sep = "\\s+") %>% 
  mutate_all(str_replace, pattern = "\\d{2}((\\+)|(?=\\-))", replacement = "") %>% 
  mutate_all(as.numeric)
# A tibble: 3 x 47
    X01   X02   X03    X04   X05   X06   X07   X08   X09   X10   X11    X12   X13
  <dbl> <dbl> <dbl>  <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>  <dbl> <dbl>
1   111   262  1900 -15.68  64.5 8.820 1.013 0.943 342.1 21.26     0 31.760 18.46
2   111   262  2000 -14.28  70.7 0.012 0.755 0.694 337.5 22.90     0  0.044 18.13
3   111   262  2100 -13.01  75.7 0.007 0.617 0.528  20.1 30.68     0  0.026 17.79
# ... with 34 more variables: X14 <dbl>, X15 <dbl>, X16 <dbl>, X17 <dbl>,
#   X18 <dbl>, X19 <dbl>, X20 <dbl>, X21 <dbl>, X22 <dbl>, X23 <dbl>, X24 <dbl>,
#   X25 <dbl>, X26 <dbl>, X27 <dbl>, X28 <dbl>, X29 <dbl>, X30 <dbl>, X31 <dbl>,
#   X32 <dbl>, X33 <dbl>, X34 <dbl>, X35 <dbl>, X36 <dbl>, X37 <dbl>, X38 <dbl>,
#   X39 <dbl>, X40 <dbl>, X41 <dbl>, X42 <dbl>, X43 <dbl>, X44 <dbl>, X45 <dbl>,
#   X46 <dbl>, X47 <dbl>

首先,我将每行上的每个条目分开到它自己的列中。

使用正则表达式的下一步有点棘手。每个 + 或 - 前面都有两个数字,所以我们匹配它。我们还想匹配一个+,如果它在那里,但只匹配并保留一个-。这使用了一个积极的前瞻,如果字符串前面包含 - 则匹配,但不包含 -。我在这里玩过正则表达式:https://regex101.com/r/VvIRlu/1

之后,我可以将所有列更改为数字,我们就完成了。

【讨论】:

  • 谢谢,但我的数据并没有像你的那样被转换成 3 行。我的行数保持不变,每行都附加了一堆 NA。我不确定有什么不同,因为我使用的是您的确切代码。
  • 你能运行然后分享dput(test)的结果吗?
  • 我添加了 dput 的结果,用于读取 mt 数据和运行代码。
  • 哦,我现在明白了,我以为每一行都包含一整行数据。让我修正一下这个例子。
  • 每行的值数是否一致为8? 01+ 之后的值是否总是开始一行?
【解决方案3】:

我假设01 前缀表示新案例的开始(使用str_split("^01|\n01"),并且每个所需的列都以两个数字为前缀,前面有一个空格(即@ 987654323@)。这会将文本分成不同的大小写。

然后在purrr::map2_dftidyr::spread 的帮助下,这些案例将转换为单个tibble。最后进行了小修改,以使用gtools::mixedorder 将列按正确的顺序排列。

要将数据转换为逗号分隔,您可以将其写入 csv 文件。例如,只需将解决方案中的最后一行更改为df %&gt;% select(gtools::mixedorder(names(.))) %&gt;% write_csv("df.csv")

解决方案

library(tidyverse)
library(stringr)

cases <- txt %>% 
  str_split("^01|\n01") %>% unlist() %>% 
  str_split("\\s[0-9]{2}") %>% .[-1]

df <- map2_df(cases, 1:length(cases), function(x, y) { 
  x %>%
    as.numeric() %>%
    as_tibble() %>%
    rownames_to_column() %>%
    mutate(rowname = paste0("V", rowname)) %>%
    mutate(id = y)
  }) %>% spread(rowname, value) 

df %>% select(gtools::mixedorder(names(.)))

#> # A tibble: 5 x 48
#>      id    V1    V2    V3     V4    V5    V6    V7    V8    V9   V10   V11
#> * <int> <dbl> <dbl> <dbl>  <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1     1   111   262  1900 -15.68  64.5 8.820 1.013 0.943 342.1 21.26     0
#> 2     2   111   262  2000 -14.28  70.7 0.012 0.755 0.694 337.5 22.90     0
#> 3     3   111   262  2100 -13.01  75.7 0.007 0.617 0.528  20.1 30.68     0
#> 4     4   111   262  1900 -15.68  64.5 8.820    NA    NA    NA    NA    NA
#> 5     5   111   262  2100 -13.01  75.7 0.007 0.617 0.528  20.1 30.68     0
#> # ... with 36 more variables: V12 <dbl>, V13 <dbl>, V14 <dbl>, V15 <dbl>,
#> #   V16 <dbl>, V17 <dbl>, V18 <dbl>, V19 <dbl>, V20 <dbl>, V21 <dbl>,
#> #   V22 <dbl>, V23 <dbl>, V24 <dbl>, V25 <dbl>, V26 <dbl>, V27 <dbl>,
#> #   V28 <dbl>, V29 <dbl>, V30 <dbl>, V31 <dbl>, V32 <dbl>, V33 <dbl>,
#> #   V34 <dbl>, V35 <dbl>, V36 <dbl>, V37 <dbl>, V38 <dbl>, V39 <dbl>,
#> #   V40 <dbl>, V41 <dbl>, V42 <dbl>, V43 <dbl>, V44 <dbl>, V45 <dbl>,
#> #   V46 <dbl>, V47 <dbl>

来源数据
上面的解决方案不会尝试在导入时使用readLines 将文本拆分为行。相反,readr::read_file 应该与建议的解决方案一起使用。此示例中使用的文本txt 是:

txt <- "01+0111.  02+0262.  03+1900.  04-15.68  05+64.50  06+08.82  07+1.013  08+0.943
09+342.1  10+21.26  11+0.000  12+31.76  13+18.46  14+16.50  15+1800.  16+5.250
17+69.46  18+1859.  19+55.25  20+27.61  21+1808.  22+50.25  23+2.367  24+1806.
25+15.25  26+14.78  27+1859.  28+55.25  29+60.11  30+1800.  31-5.250  32+0.000
33+1854.  34+5.250  35+0.447  36+1819.  37+50.25  38+14.80  39+69.40  40+0.073
41+275.3  42+0.447  43+18.29  44+22.30  45+22.43  46+2.367  47+329.1
01+0111.  02+0262.  03+2000.  04-14.28  05+070.7  06+0.012  07+0.755  08+0.694
09+337.5  10+22.90  11+0.000  12+0.044  13+18.13  14+14.78  15+1900.  16+15.25
17+072.6  18+1908.  19+15.25  20+0.146  21+1946.  22+10.25  23+1.567  24+1948.
25+25.25  26+14.02  27+1959.  28+20.25  29+69.21  30+1936.  31-25.25  32+0.000
33+1900.  34+20.25  35+0.447  36+1900.  37+5.250  38+14.02  39+69.95  40+0.000
41+343.6  42+0.607  43+17.97  44+21.97  45+22.13  46+1.567  47+354.2
01+0111.  02+0262.  03+2100.  04-13.01  05+075.7  06+0.007  07+0.617  08+0.528
09+20.10  10+30.68  11+0.000  12+0.026  13+17.79  14+14.02  15+2000.  16+5.250
17+082.7  18+2050.  19+55.25  20+0.146  21+2028.  22+30.25  23+1.407  24+2001.
25+25.25  26+11.78  27+2051.  28+40.25  29+69.68  30+2001.  31-25.25  32+0.000
33+2000.  34+5.250  35+0.447  36+2002.  37+25.25  38+12.00  39+081.0  40+0.000
41+39.42  42+0.447  43+17.61  44+21.68  45+21.82  46+1.407  47+349.4
01+0111.  02+0262.  03+1900.  04-15.68  05+64.50  06+08.82  
01+0111.  02+0262.  03+2100.  04-13.01  05+075.7  06+0.007  07+0.617  08+0.528  
09+20.10  10+30.68  11+0.000  "

【讨论】:

    猜你喜欢
    • 2018-06-12
    • 1970-01-01
    • 1970-01-01
    • 2022-10-14
    • 2022-10-24
    • 2012-05-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多