我有一些文本形式的数据,取自网页。它很长,但遵循以下形式:
<p><span class="monthyear">Jan 2001</span>
<br><b>Foo text (2)</b></p>
<p><span class="monthyear">Nov 2006</span>
<br><b>Bar text (29)</b>
<br><b>More bar text (4)</b>
<br><b>Yet more bar text (102)</b></p>
<p><span class="monthyear">Apr 2004</span>
<br><b>Further foo text (1)</b>
<br><b>Combination foo and bar text (41)</b></p>
我想将相关部分提取到数据框中,如下所示:
monthyear info n
1 Jan 2001 Foo text 2
2 Nov 2006 Bar text 29
3 Nov 2006 More bar text 4
......但我不知道该怎么做。如果我在名为text的字符向量中有html,我可以使用stringr包中的函数提取 monthyear 数据:
monthyear <- str_extract_all(
text[1],perl("(?<=\\\"monthyear\\\">).*?20[0-9]{2}")
)
我可以用同样的方式提取 info 和 n 数据,但假设有多个 info 和 monthyear 条目的> n 条目,我不知道如何将它们组合起来。我是不是错了?
答案 0 :(得分:2)
不幸的是,我们无法始终控制数据源的质量,因此我们不得不采取一些繁琐的手动处理。 (有人说数据分析师的大部分时间花在清理数据上,而不是用于分析。)
正如评论中已经指出的那样,正则表达式不是使用HTML的最佳工具,因为HTML一般来说并不是一种非常规的常规语言(我认为它被称为无上下文语言)。但是,如果HTML源代码有些常规(因为它们在您提供的示例数据中),您仍然可以有效地使用它们。
这是一个循序渐进的例子。我已将HTML标头标记添加到您的示例文本中,并将其存储在此处:http://ideone.com/O1PC05
使用readLines
x1 <- readLines("http://ideone.com/plain/O1PC05")
隔离&#34;身体&#34;的网页
bodycontent <- grep("<body>|</body>", x1)
x2 <- x1[(bodycontent[1]+1):(bodycontent[2]-1)]
grepl
返回TRUE
或FALSE
for if&#34; monthyear&#34;在给定的行中找到了。使用cumsum
创建&#34; groups&#34;和split
将字符向量转换为列表。
x3 <- split(x2, cumsum(grepl("monthyear", x2)))
如果您愿意,可以分多步执行以下操作。基本的想法是lapply
在您的列表上,用标签替换所有HTML标签,并用标签替换括号。之后,您可以使用read.delim
,但期望获得大量NA
值的列,因为我们正在插入比我们需要的更多标签。
这很可能是因为几个原因你会失败的地方。 (1)假设源数据确实结构良好......(2)但是,文本本身可能有括号......(3)或者,正文中可能还有其他内容,包括脚本标签,表标签,等等将被读入并尝试处理。
x4 <- read.delim(header = FALSE,
stringsAsFactors = FALSE,
strip.white = TRUE,
sep = "\t",
text =
unlist(lapply(x3,
function(x) {
temp <- gsub("<(.|\n)*?>", "\t", x)
paste(gsub("[()]", "\t", temp),
collapse="\t")
})))
我提到在第4步中,我们最终会得到很多垃圾栏。让我们摆脱那些。
x5 <- x4[apply(x4, 2, function(x) !all(is.na(x)))]
现在,让我们以更有意义的方式命名列。我们知道第一栏将是&#34; monthyear&#34;设计变量,其他应该&#34; info&#34;和&#34; n&#34;,所以我们可以在rep
中包含一些基本的paste
来获取变量名。虽然我们正在使用它,但我们将使用动物园中的as.yearmon
&#34;包转换我们的&#34; monyear&#34;变量到实际日期,允许我们对实际日期进行排序和做其他漂亮的事情。
myseq <- ncol(x5[-1])/2 # We expect pairs of columns, right?
names(x5) <- c("monthyear",
paste(rep(c("info", "n"), myseq),
sep(1:myseq, each = 2), sep = "."))
library(zoo)
x5$monthyear <- as.Date(as.yearmon(x5$monthyear, "%b %Y"))
x5
# monthyear info.1 n.1 info.2 n.2 info.3 n.3
# 1 2001-01-01 Foo text 2 NA NA
# 2 2006-11-01 Bar text 29 More bar text 4 Yet more bar text 102
# 3 2004-04-01 Further foo text 1 Combination foo and bar text 41 NA
如果您真的想要长篇数据,请使用reshape
:
x6 <- reshape(x5,
direction = "long",
idvar = "monthyear",
varying = 2:ncol(x5))
执行一些可选的清理工作,例如按日期排序输出,重置行名称以及删除不完整的案例:
x6 <- x6[order(x6$monthyear), ]
rownames(x6) <- NULL
x6[complete.cases(x6), ]
# monthyear time info n
# 1 2001-01-01 1 Foo text 2
# 4 2004-04-01 1 Further foo text 1
# 5 2004-04-01 2 Combination foo and bar text 41
# 7 2006-11-01 1 Bar text 29
# 8 2006-11-01 2 More bar text 4
# 9 2006-11-01 3 Yet more bar text 102
无论如何,试试看,并根据需要进行修改。我的猜测是,在某些时候,您必须在纯文本编辑器中打开文件,然后在那里进行一些初步清理。