从字符串中删除最后三个点

5
我有一个文本数据文件,可能会使用readLines读取。每个字符串的初始部分都包含许多无意义的内容,后面是我需要的数据。无意义的内容和数据通常由三个点分隔开。我想在最后三个点之后拆分字符串,或者用某种标记替换最后三个点,告诉R将这三个点左侧的所有内容视为一列。
以下是Stackoverflow上类似的帖子,可以定位最后一个句点:

R: Find the last dot in a string

然而,在我的情况下,有些数据具有小数点,因此仅定位最后一个句点是不够的。此外,我认为...在R中具有特殊含义,这可能会使问题变得复杂。另一个潜在的复杂性是,有些点比其他点大。此外,在某些行中,其中一个三个点被逗号替换。
除了上述帖子中的gregexpr外,我还尝试过使用gsub,但无法找到解决方案。
以下是一个示例数据集及我希望实现的结果:
aa = matrix(c(
'first string of junk... 0.2 0 1', 
'next string ........2 0 2', 
'%%%... ! 1959 ...  0 3 3',
'year .. 2 .,.  7 6 5',
'this_string   is . not fine .•. 4 2 3'), 
nrow=5, byrow=TRUE,
dimnames = list(NULL, c("C1")))

aa <- as.data.frame(aa, stringsAsFactors=F)
aa

# desired result
#                             C1  C2 C3 C4
# 1        first string of junk  0.2  0  1
# 2            next string .....   2  0  2
# 3             %%%... ! 1959      0  3  3
# 4                 year .. 2      7  6  5
# 5 this_string   is . not fine    4  2  3

我希望这个问题不被认为太具体。文本数据文件是使用昨天我在R中读取MSWord文件的帖子中概述的步骤创建的。

有些行不包含无意义的字符或三个点,而只包含数据。但是,这可能会给后续的帖子带来一些复杂性。

感谢您的任何建议。


你能先搜索并替换所有逗号和句点为普通的句点吗? - Feng Mai
我认为我不能用点号替换逗号,因为数据中包含更大数字的逗号:4,500。我在帖子中忘记提到了这一点。虽然也许我可以用点号替换逗号,然后在消除无意义内容后从数据中删除点号。 - Mark Miller
3个回答

5

这个方法可以解决问题,虽然不是特别优雅...

options(stringsAsFactors = FALSE)


# Search for three consecutive characters of your delimiters, then pull out
# all of the characters after that
# (in parentheses, represented in replace by \\1)
nums <- as.vector(gsub(aa$C1, pattern = "^.*[.,•]{3}\\s*(.*)", replace = "\\1"))

# Use strsplit to break the results apart at spaces and just get the numbers
# Use unlist to conver that into a bare vector of numbers
# Use matrix(, nrow = length(x)) to convert it back into a
# matrix of appropriate length
num.mat <- do.call(rbind, strsplit(nums, split = " "))


# Mash it back together with your original strings
result <- as.data.frame(cbind(aa, num.mat))

# Give it informative names
names(result) <- c("original.string", "num1", "num2", "num3")

值得注意的是,当我尝试从 Vim 发送此代码时,“大点”会给我带来麻烦 - 但是当从网站复制时,它可以正常工作。因此,我的流程是在 Vim 中进行编辑,粘贴到网站,然后再粘贴到我的控制台...这样做是不对的。 - Matt Parker
看起来代码可能将数字4、2、3(来自最后一个字符串)分配给数据集中的所有5个字符串。 - Mark Miller
@MarkMiller 啊,抱歉 - 我是在使用 aa 矩阵,而不是数据框。如果你想使用数据框,你可以这样赋值 numsas.vector(gsub(aa$C1, pattern = "^.*[.,•]{3}\\s*(.*)", replace = "\\1")) - Matt Parker

2

这将帮助您完成大部分工作,并且不会在包含逗号的数字方面出现问题:

# First, use a regex to eliminate the bad pattern.  This regex
# eliminates any three-character combination of periods, commas,
# and big dots (•), so long as the combination is followed by 
# 0-2 spaces and then a digit.
aa.sub <- as.matrix(
  apply(aa, 1, function (x) 
    gsub('[•.,]{3}(\\s{0,2}\\d)', '\\1', x, perl = TRUE)))

# Second: it looks as though you want your data split into columns.
# So this regex splits on spaces that are (a) preceded by a letter, 
# digit, or space, and (b) followed by a digit.  The result is a 
# list, each element of which is a list containing the parts of 
# one of the strings in aa.
aa.list <- apply(aa.sub, 1, function (x) 
  strsplit(x, '(?<=[\\w\\d\\s])\\s(?=\\d)', perl = TRUE))  

# Remove the second element in aa.  There is no space before the 
# first data column in this string.  As a result, strsplit() split
# it into three columns, not 4.  That in turn throws off the code
# below.
aa.list <- aa.list[-2]

# Make the data frame.
aa.list <- lapply(aa.list, unlist)  # convert list of lists to list of vectors
aa.df   <- data.frame(aa.list)      
aa.df   <- data.frame(t(aa.df), row.names = NULL, stringsAsFactors = FALSE) 

唯一剩下的事情是修改strsplit()的正则表达式,使其能够处理aa中的第二个字符串。或者也许最好手动处理这种情况。

如果我在第二个字符串的最后一个点和数字2之间加入一个空格,你能否修改aa.list行来处理它?在我的真实数据中,我认为最后一个点后总是有一个空格,而当我创建“aa”时,我没有意识到这一点。我也可以尝试弄清楚如何修改aa.list。 - Mark Miller
是的,如果在第二个字符串中最后一个点和“the”之间添加一个空格,那么第二步中的正则表达式就可以修改以处理该字符串。这有点棘手,但可行。话虽如此,我认为@MattParker有更好的想法:首先将每个字符串分成“坏”部分(第一列)和良好行为部分(数据列)。然后对第一列应用正则表达式。然后重新连接两个部分。如果您以这种方式执行,则可以使strsplit中的正则表达式保持简单。否则,正则表达式将更加复杂。 - user697473

0

反转字符串
如果需要,反转你正在搜索的模式 - 在你的情况下不需要
反转结果

[俳句伪代码]

a = 'first string of junk... 0.2 0 1' // string to search
b = 'junk' // pattern to match 

ra = reverseString(a) // now equals '1 0 2.0 ...knuj fo gnirts tsrif'
rb = reverseString (b) // now equals 'knuj'

// run your regular expression search / replace - search in 'ra' for 'rb'
// put the result in rResult
// and then unreverse the result
// apologies for not knowing the syntax for 'R' regex

[/haiku-pseudocode]


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接