Regex 按id在行之间合并字符串
我希望通过id变量在行之间合并字符串。我知道如何使用下面的Regex 按id在行之间合并字符串,regex,r,string,merge,Regex,R,String,Merge,我希望通过id变量在行之间合并字符串。我知道如何使用下面的R代码。然而,我的代码似乎过于复杂 在本例中,每个字符串有两个不是点的元素。id中的每对连续行都有一个公共元素。因此,合并两行后,这些元素中只剩下一个 显示所需结果,下面的R代码返回所需结果。谢谢你的建议。很抱歉,我的R代码太长且复杂,但它确实有效,我的目标是在baseR中获得更高效的代码 my.data <- read.table(text = ' id my.string 2 11..
R
代码。然而,我的代码似乎过于复杂
在本例中,每个字符串有两个不是点的元素。id中的每对连续行都有一个公共元素。因此,合并两行后,这些元素中只剩下一个
显示所需结果,下面的R
代码返回所需结果。谢谢你的建议。很抱歉,我的R
代码太长且复杂,但它确实有效,我的目标是在baseR
中获得更高效的代码
my.data <- read.table(text = '
id my.string
2 11..................
2 .1...2..............
2 .....2...3..........
5 ....................
6 ......2.....2.......
6 ............2...4...
7 .1...2..............
7 .....2....3.........
7 ..........3..3......
7 .............34.....
8 ....1.....1.........
8 ..........12........
8 ...........2....3...
9 ..................44
10 .2.......2..........
11 ...2...2............
11 .......2.....2......
11 .............2...2..
', header = TRUE, na.strings = 'NA', stringsAsFactors = FALSE)
my.data
desired.result <- read.table(text = '
id my.string
2 11...2...3..........
5 ....................
6 ......2.....2...4...
7 .1...2....3..34.....
8 ....1.....12....3...
9 ..................44
10 .2.......2..........
11 ...2...2.....2...2..
', header = TRUE, na.strings = 'NA', stringsAsFactors = FALSE)
# obtain position of first and last non-dot
# from: http://stackoverflow.com/questions/29229333/position-of-first-and-last-non-dot-in-a-string-with-regex
first.last.dot <- data.frame(my.data, do.call(rbind, gregexpr("^\\.*\\K[^.]|[^.](?=\\.*$)", my.data[,2], perl=TRUE)))
# obtain non-dot elements
first.last.dot$first.element <- as.numeric(substr(first.last.dot$my.string, first.last.dot$X1, first.last.dot$X1))
first.last.dot$last.element <- as.numeric(substr(first.last.dot$my.string, first.last.dot$X2, first.last.dot$X2))
# obtain some book-keeping variables
first.last.dot$number.within.group <- sequence(rle(first.last.dot$id)$lengths)
most.records.per.id <- max(first.last.dot$number.within.group)
n.ids <- length(unique(first.last.dot$id))
# create matrices for recording data
positions.per.id <- matrix(NA, nrow = (n.ids), ncol=(most.records.per.id+1))
values.per.id <- matrix(NA, nrow = (n.ids), ncol=(most.records.per.id+1))
# use nested for-loops to fill matrices with data
positions.per.id[1,1] = first.last.dot$X1[1]
values.per.id[1,1] = first.last.dot$first.element[1]
positions.per.id[1,2] = first.last.dot$X2[1]
values.per.id[1,2] = first.last.dot$last.element[1]
j = 1
for(i in 2:nrow(first.last.dot)) {
if(first.last.dot$id[i] != first.last.dot$id[i-1]) j = j + 1
positions.per.id[j, (first.last.dot$number.within.group[i]+0)] = first.last.dot$X1[i]
positions.per.id[j, (first.last.dot$number.within.group[i]+1)] = first.last.dot$X2[i]
values.per.id[j, (first.last.dot$number.within.group[i]+0)] = first.last.dot$first.element[i]
values.per.id[j, (first.last.dot$number.within.group[i]+1)] = first.last.dot$last.element[i]
}
# convert matrix data into new strings using nested for-loops
new.strings <- matrix(0, nrow = nrow(positions.per.id), ncol = nchar(my.data$my.string[1]))
for(i in 1:nrow(positions.per.id)) {
for(j in 1:ncol(positions.per.id)) {
new.strings[i,positions.per.id[i,j]] <- values.per.id[i,j]
}
}
# format new strings
new.strings[is.na(new.strings)] <- 0
new.strings[new.strings==0] <- '.'
new.strings2 <- data.frame(id = unique(first.last.dot$id), my.string = (do.call(paste0, as.data.frame(new.strings))), stringsAsFactors = FALSE)
new.strings2
all.equal(desired.result, new.strings2)
# [1] TRUE
my.data在base R中这样做有点受虐狂,所以我不会这么做,但只要有点毅力,你可以自己做。这是一个数据表
版本(您需要从github
安装最新的1.9.5版本才能获得tstrsplit
):
伙计,这很难。请不要让我解释我做了什么
data.frame(id=unique(my.data$id), my.string=sapply(lapply(unique(my.data$id), function(id) gsub('^$','.',substr(gsub('\\.','',do.call(paste0,strsplit(my.data[my.data$id==id,'my.string'],''))),1,1)) ), function(x) paste0(x,collapse='') ), stringsAsFactors=F );
好的,我来解释一下:
它以这个lappy()
调用开始:
lapply(unique(my.data$id), function(id) ... )
如您所见,上面的代码基本上迭代data.frame中的唯一ID,依次处理每个ID。以下是函数的内容:
gsub('^$','.',substr(gsub('\\.','',do.call(paste0,strsplit(my.data[my.data$id==id,'my.string'],''))),1,1))
让我们把它分成几部分,从最里面的子表达式开始:
strsplit(my.data[my.data$id==id,'my.string'],'')
do.call(paste0,...)
以上为当前id
值的所有my.string
单元格编制索引,并使用strsplit()
拆分每个字符串。这将生成字符向量的列表
,每个列表组件包含一个字符串向量,其中整个向量对应于被拆分的输入字符串。使用空字符串作为分隔符会导致每个输入字符串中的每个单独字符成为对应于所述输入字符串的列表组件中的输出向量中的一个元素
下面是上面表达式生成的示例(对于id==2):
上面的strsplit()
调用包含在以下内容中(其中..
表示前面的表达式):
调用一次paste0()
,将strsplit()生成的输出向量作为参数传递。这会对所有向量进行一种元素级粘贴,因此对于每个唯一的id,最终会得到一个这样的向量:
[1] "1.." "11." "..." "..." "..." ".22" "..." "..." "..." "..3" "..." "..." "..." "..." "..." "..." "..." "..." "..." "..."
[1] "1" "11" "" "" "" "22" "" "" "" "3" "" "" "" "" "" "" "" "" "" ""
上面的paste0()
调用包含在以下内容中:
gsub('\\.','',...)
substr(...,1,1)
gsub('^$','.',...)
从所有元素中去除所有文字点,对于每个唯一id,结果如下:
[1] "1.." "11." "..." "..." "..." ".22" "..." "..." "..." "..3" "..." "..." "..." "..." "..." "..." "..." "..." "..." "..."
[1] "1" "11" "" "" "" "22" "" "" "" "3" "" "" "" "" "" "" "" "" "" ""
上面的gsub()
调用包含在以下内容中:
gsub('\\.','',...)
substr(...,1,1)
gsub('^$','.',...)
它只提取每个元素的第一个字符,如果它存在,则是该位置所需的字符。空元素是可以接受的,因为这只是意味着id在该位置的任何输入字符串中都没有非点字符
上述substr()
调用包含在以下内容中:
gsub('\\.','',...)
substr(...,1,1)
gsub('^$','.',...)
这只是将空元素替换为文字点,这显然是在我们将字符串重新组合在一起之前所必需的。对于id==2,我们有:
[[1]]
[1] "1" "1" "." "." "." "." "." "." "." "." "." "." "." "." "." "." "." "." "." "."
[[2]]
[1] "." "1" "." "." "." "2" "." "." "." "." "." "." "." "." "." "." "." "." "." "."
[[3]]
[1] "." "." "." "." "." "2" "." "." "." "3" "." "." "." "." "." "." "." "." "." "."
[1] "1" "1" "." "." "." "2" "." "." "." "3" "." "." "." "." "." "." "." "." "." "."
这就完成了赋予lappy()
调用的函数。因此,从该调用中产生的将是表示所需输出字符串的字符向量的列表。剩下的就是将这些向量的元素折叠成一个字符串,这就是为什么我们需要这样:
sapply(..., function(x) paste0(x,collapse='') )
使用sapply()
[1] "11...2...3.........." "...................." "......2.....2...4..." ".1...2....3..34....." "....1.....12....3..." "..................44" ".2.......2.........." "...2...2.....2...2.."
因此,剩下的就是生成完整的输出data.frame,类似于输入data.frame:
data.frame(id=unique(my.data$id), my.string=..., stringsAsFactors=F )
导致:
id my.string
1 2 11...2...3..........
2 5 ....................
3 6 ......2.....2...4...
4 7 .1...2....3..34.....
5 8 ....1.....12....3...
6 9 ..................44
7 10 .2.......2..........
8 11 ...2...2.....2...2..
我们完了 这里可以使用stringi
和dplyr
包中的函数:
library(stringi)
library(dplyr)
# split my.string
m <- stri_split_boundaries(my.data$my.string, type = "character", simplify = TRUE)
df <- data.frame(id = my.data$id, m)
# function to apply to each column - select "." or unique "number"
myfun <- function(x) if(all(x == ".")) "." else unique(x[x != "."])
df %>%
# for each id...
group_by(id) %>%
# ...and each column, apply function
summarise_each(funs(myfun)) %>%
# for each row...
rowwise() %>%
#...concatenate strings
do(data.frame(id = .[1], mystring = paste(.[-1], collapse = "")))
# id mystring
# 1 2 11...2...3..........
# 2 5 ....................
# 3 6 ......2.....2...4...
# 4 7 .1...2....3..34.....
# 5 8 ....1.....12....3...
# 6 9 ..................44
# 7 10 .2.......2..........
# 8 11 ...2...2.....2...2..
库(stringi)
图书馆(dplyr)
#分开我的绳子
m%
#对于每一行。。。
行()
#…连接字符串
do(data.frame(id=[1],mystring=paste([1],collapse=”“))
#id mystring
# 1 2 11...2...3..........
# 2 5 ....................
# 3 6 ......2.....2...4...
# 4 7 .1...2....3..34.....
# 5 8 ....1.....12....3...
# 6 9 ..................44
# 7 10 .2.......2..........
# 8 11 ...2...2.....2...2..
<>代码>因为这个代码正在工作,你可能想考虑把这个移到我试图更清楚地理解这一点,如果你的合并,你的第二行的“<代码> 1”/代码>消失在哪里?@ HWND每一行同一个ID都有一个共同的元素。我会把它添加到帖子中。@JasonAizkalns谢谢。我从来没有张贴任何东西,但我确实考虑到它在目前的情况下。我只是在想,也许在R中有一个1行、2行或3行的解决方案,也许有人可以在这里快速发布。我不知道Code Review站点上是否有很多R程序员。@MarkMiller如果代码按预期工作,并且您希望对其进行改进,那么codereview.stackexchange.com将非常适合您的问题。谢谢Mark!我补充了一个解释,如果你有任何问题,请告诉我。