【问题标题】:Create a new column that counts the number of a sub-string in a string column?创建一个新列来计算字符串列中子字符串的数量?
【发布时间】:2016-01-25 00:22:55
【问题描述】:

这里是 R 新手。我有一个问题要解决:如果子字符串在字符串列中出现一次或多次,我需要创建一些计数为 1 的新列。像这样:

Existing Column         New Col (True if apple)    New Col (True if banana)
apple, apple, orange            1                              0
banana, banana, orange          0                              1
apple, banana, orange           1                              1

谁能帮我解决这个问题?非常感谢您。

【问题讨论】:

  • 我尝试插入文本但结果错误...如何在我的问题中插入表格?
  • 复制并粘贴,然后突出显示并点击代码按钮。或者只是缩进 4 个空格。
  • 首先看一下如何在此处创建 df 并将其输出粘贴到此处可能很有用 - 用于示例输入和示例输出。您可以使用“大括号”将其格式化为代码,这样我们都可以以正确的格式查看它。
  • 啊,明白了,谢谢!是的,如帖子中所示,我想创建这些新列。

标签: r dplyr stringr


【解决方案1】:

所以当我第一次阅读问题(上一个编辑)时,我认为您想要计数列(而不是是否包含字符串),但无论如何它都是有用的代码,所以我留下了它。以下是基本 R 和 stringr 包的选项:

首先让我们制作一个具有相似数据的示例 data.frame

# stringsAsFactors = FALSE would be smart here, but let's not assume...
df <- data.frame(x = c('a, b, c, a', 'b, b, c', 'd, a'))   

看起来像

> df
           x
1 a, b, c, a
2    b, b, c
3       d, a

基础 R

使用strsplit 来制作分离字符串的向量列表,使用as.character 将因子强制转换为有用的形式,

list <- strsplit(as.character(df$x), ', ')

然后制作一个唯一字符串列表

lvls <- unique(unlist(list))

制作包含列

使用sapply 循环遍历data.frame/list 的行。 (此答案中的所有sapply 函数都可以替换为for 循环,但出于速度原因,这通常被认为是R 中的不良风格。)测试每个字符串中是否存在唯一字符串,然后更改为整数格式。将结果 (transposed) 设置为 df 的新列,每个唯一字符串对应一个。

df[, lvls] <- t(sapply(1:nrow(df), function(z){as.integer(lvls %in% list[[z]])}))

> df
           x a b c d
1 a, b, c, a 1 1 1 0
2    b, b, c 0 1 1 0
3       d, a 1 0 0 1

要将值保留为布尔值 TRUE/FALSE 而不是整数,只需删除 as.integer

制作计数列

使用外部sapply 循环遍历data.frame/list 的行,而内部循环遍历每个行中的唯一字符串,并通过对TRUE 值求和来计算出现次数。将结果 (transposed) 设置为 df 的新列,每个唯一字符串对应一个。

df[, lvls] <- t(sapply(1:nrow(df), function(z){
    sapply(seq_along(lvls), function(y){sum(lvls[y] == list[[z]])})
}))

> df
           x a b c d
1 a, b, c, a 2 1 1 0
2    b, b, c 0 2 1 0
3       d, a 1 0 0 1

stringr

stringr 可以让这些任务变得更加简单。

首先,在df$x 中找到唯一的字符串。用str_split 拆分字符串(可以取一个因子),用unlist 将它们展平成一个向量,然后找到唯一的:

library(stringr)
lvls <- unique(unlist(str_split(df$x, ', ')))

制作包含列

str_detect 允许我们只遍历唯一的字符串,而不是行:

df[, lvls] <- sapply(lvls, function(y){as.integer(str_detect(df$x, y))})

制作计数列

str_count 大大简化了我们的语法,再次只循环lvls

df[,lvls] <- sapply(lvls, function(y){str_count(df$x, y)})

两者的结果与上述基数 R 中的结果相同。

【讨论】:

    【解决方案2】:

    因此,如果没有完整的详细信息,很难确切地知道您在寻找什么。但是,如果您要查找给定字符串出现的次数并将其作为列添加到原始数据中,那么这是一种可行的方法(复制您的数据输入):

    df <- data.frame(Fruit = c('apple,orange,orange', 'banana,banana,pear', 'apple,banana,orange'), stringsAsFactors = FALSE)
    
    df$appleCount <- lapply(strsplit(df$Fruit, ','), function(x) sum('apple' == x))
    df$bananaCount <- lapply(strsplit(df$Fruit, ','), function(x) sum('banana' == x))
    

    这仅在您知道要添加为列的特定字符串时才有效。但是,应该让您了解如何拆分字符串、计算该拆分列表中有多少给定的字符串等。希望这会有所帮助。

    上述代码的输出应该是这样的:

                    Fruit appleCount bananaCount
    1 apple,orange,orange          1           0
    2  banana,banana,pear          0           2
    3 apple,banana,orange          1           1
    

    如果您不是在寻找给定字符串出现的次数,而只是寻找该字符串是否出现的真/假 (0/1),您可以使用以下稍作修改的代码来获得该结果:

    df <- data.frame(Fruit = c('apple,orange,orange', 'banana,banana,pear', 'apple,banana,orange'), stringsAsFactors = FALSE)
    df$appleCount <- lapply(strsplit(df$Fruit, ','), function(x) 'apple' %in% x)
    df$bananaCount <- lapply(strsplit(df$Fruit, ','), function(x) 'banana' %in% x)
    

    然后输出如下:

                Fruit appleCount bananaCount
    1 apple,orange,orange       TRUE       FALSE
    2  banana,banana,pear      FALSE        TRUE
    3 apple,banana,orange       TRUE        TRUE
    

    如果你真的想要0/1,你可以使用as.integer将逻辑列转换为整数值。

    【讨论】:

    • 投票和/或接受答案是您在 Stackoverflow 上感谢的方式。 :)
    • 我做了...但是因为我的帐户是新帐户,堆栈溢出不会让我这样做。后续问题:如果我想在字符串中同时查找苹果或香蕉怎么办?这意味着香蕉或苹果在字符串中,我希望新列返回 true。
    • 你需要查找'|'和 '&' 运算符进行组合。例如,您可以这样做:sum('apple' == x | 'banana' == x) 作为一种可能性。
    【解决方案3】:

    使用@user3949008 的答案中的“df”,您也可以尝试我的“splitstackshape”包中的cSplit_e

    library(splitstackshape)
    cSplit_e(df, "Fruit", ",", type = "character", fill = 0)
    #                 Fruit Fruit_apple Fruit_banana Fruit_orange Fruit_pear
    # 1 apple,orange,orange           1            0            1          0
    # 2  banana,banana,pear           0            1            0          1
    # 3 apple,banana,orange           1            1            1          0
    

    您以后可以随时删除您不感兴趣的列。

    如果您需要计数,可以尝试来自“qdapTools”的mtabulate

    library(qdapTools)
    mtabulate(strsplit(df$Fruit, ","))
    #   apple banana orange pear
    # 1     1      0      2    0
    # 2     0      2      0    1
    # 3     1      1      1    0
    

    【讨论】:

    • base::strsplit 没有矢量化(令人沮丧);第二个你必须使用stringr::str_split 之类的东西。不过,令人印象深刻的软件包知识。
    • @alistaire ? strsplit 没有矢量化是什么意思?是的。
    • 等等,你完全正确(对不起!),这让我想知道为什么它之前会为我引发错误......嗯。
    • @alistaire,可能是因为您试图拆分因子。当您使用apply 时,它会将它们转换为字符(我想)。
    猜你喜欢
    • 2017-10-20
    • 1970-01-01
    • 2017-05-05
    • 1970-01-01
    • 2016-02-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多