创建一个新列,计算字符串列中子字符串的数量?

时间:2016-01-25 00:22:56

标签: r dplyr stringr

这里是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

任何人都可以帮我这个吗?非常感谢你提前。

3 个答案:

答案 0 :(得分:3)

所以当我第一次阅读问题(上一个编辑)时,我认为你想要计数列(不是字符串是否包含在内),但无论如何它都是有用的代码,所以我离开了它。以下是基本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中的不良样式。)测试每个中是否存在唯一字符串,并更改到整数格式。将结果(t ransposed)设置为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值求和来计算出现次数。将结果(t ransposed)设置为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的结果相同。

答案 1 :(得分: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将逻辑列转换为整数值。

答案 2 :(得分:2)

使用来自@ 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