在R中删除字符串中的重复元素

3

我需要删除字符串中的重复元素,但是遇到了一些问题。 我的数据看起来像这样:


idvisit     path
1           1,16,23,59
2           2,14,14,19
3           5,19,23,19
4           10,10
5           23,23,27,29,23

我有一列包含唯一ID的内容,还有一列用于网页导航路径。右侧的列中包含一些情况,其中页面被重载并且页面被跟踪了两次甚至更多。这些页面用逗号分隔,并保存为因子。我的问题是,我不想在一行中出现多个页面,所以数据应该如下所示。

idvisit     path
1           1,16,23,59
2           2,14,19
3           5,19,23,19
4           10
5           23,27,29,23

需要删除相邻的多个页面。我知道如何使用正则表达式删除特定的多个数字,但我有大约20,000个不同的页面,无法为所有页面执行此操作。 是否有人能够提供解决方案或提示,来解决我的问题?

谢谢 Sebastian

2个回答

5
我们可以使用 tidyverse。使用 separate_rows 函数按分隔符(,)将 'path' 变量拆分为长格式,然后根据 'idvisit' 进行分组,我们使用 paste 函数将值合并。
library(tidyverse)
separate_rows(df1, path) %>%
       group_by(idvisit) %>%
       summarise(path = paste(rle(path)$values, collapse=","))
# A tibble: 5 × 2
#  idvisit        path
#    <int>       <chr>
#1       1  1,16,23,59
#2       2     2,14,19
#3       3  5,19,23,19
#4       4          10
#5       5 23,27,29,23

或者使用基础的 R 选项:

df1$path <- sapply(strsplit(df1$path, ","), function(x) paste(rle(x)$values, collapse=","))

注意:如果“路径(path)”列是“factor”类型,请在传递到“strsplit”函数之前将其转换为“character”,即:strsplit(as.character(df1$path), ",")


我正在尝试验证您的R-base解决方案,对于第三行:x <- "5,19,23,19"x <- strsplit(x, ","),然后:paste(rle(x)$values, collapse = ","),我得到了:[1] "c(\"5\", \"19\", \"23\", \"19\")" - David Leal
使用 rle 函数,在这种情况下,首先需要对值进行排序,我不知道这是否与 @SebastianEttner 的问题相关。例如:x <- c(2,2,1,2,3,2,1,3,3,2,1),然后:rle(x)$value 将是:[1] 2 1 2 3 2 1 3 2 1,但使用:rle(x[order(x)]) 我们得到: rle(x[order(x)])$value 将为: [1] 1 2 3 - David Leal
@DavidLeal 这里的整个想法是对值进行排序。期望的输出表明仅应删除连续的重复项。否则,他可以使用 unique。您获得的输出原因是因为您尚未首先探索 strsplit(x, ",") 的输出。 - David Arenburg
@DavidArenburg 我重复了这些步骤,现在使用:x <- strsplit(x, ",")[[1]],现在我得到的是:[1] "5,19,23,19" 重复的值(19)仍然存在。 - David Leal
@DavidArenburg,问题中的短语“我的问题是,我不想在一行中有多个页面”让我感到困惑。现在我明白它只适用于连续值,然后是行:2、4,那么第二个提议就可行了。 - David Leal

1
使用stringr包中的函数str_replace_all,我认为可以通过以下正则表达式([0-9]+),\\1来得到您想要的内容,然后用\\1替换它(我们需要转义\特殊字符):
library(stringr)
> str_replace_all("5,19,23,19", "([0-9]+),\\1", "\\1")
[1] "5,19,23,19"
> str_replace_all("10,10", "([0-9]+),\\1", "\\1")
[1] "10"
> str_replace_all("2,14,14,19", "([0-9]+),\\1", "\\1")
[1] "2,14,19"

您可以使用数组形式:x <- c("5,19,23,19", "10,10", "2,14,14,19"),然后:
str_replace_all(x, "([0-9]+),\\1", "\\1")
[1] "5,19,23,19" "10"         "2,14,19"

或者使用 sapply:
result <- sapply(x, function(x) str_replace_all(x, "([0-9]+),\\1", "\\1"))

然后:

> result
  5,19,23,19        10,10   2,14,14,19 
"5,19,23,19"         "10"    "2,14,19" 

注意:

第一行是属性信息:

> str(result)
Named chr [1:3] "5,19,23,19" "10" "2,14,19"
- attr(*, "names")= chr [1:3] "5,19,23,19" "10,10" "2,14,14,19"

如果您不想看到它们(这不会影响结果),只需执行以下操作:
attributes(result) <- NULL

然后,
> result
[1] "5,19,23,19" "10"         "2,14,19"   

关于使用的正则表达式的解释:([0-9]+),\\1

  1. ([0-9]+):以第一组开始,由()分隔,并查找任何数字(至少一个)
  2. ,:然后是一个标点符号:,(我们可以在这里包括空格,但原始示例仅使用此字符作为分隔符)
  3. \\1:接下来是与组1相同的字符串,即重复的数字。如果不是这样,则模式不匹配。

然后,如果模式匹配,则将其替换为变量\\1的值,即模式中第一次出现的数字。

如何处理多个重复数字,例如2,14,14,14,19?:

只需使用这个正则表达式:([0-9]+)(,\\1)+,那么它会在分隔符(右侧)和数字至少重复一次时匹配。您可以尝试使用regex101.com进行其他可能性的匹配(在我看来,它比其他在线正则表达式检查器更加用户友好)。我希望这对您有用,它是一个灵活的解决方案,您只需要根据需要调整模式即可。

1
不错的想法。虽然supply的添加是不必要的。在R中,循环中运行向量化操作没有任何理由-这是非常糟糕的做法。但是,对于重复>2的情况,例如:"5,19,19,19,20",这将失败。 - David Arenburg

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