【问题标题】:Split delimited strings into distinct columns in R dataframe将分隔的字符串拆分为 R 数据框中的不同列
【发布时间】:2014-11-18 16:01:10
【问题描述】:

我需要一种快速简洁的方法将数据帧中的字符串文字拆分为一组列。假设我有这个数据框

data <- data.frame(id=c(1,2,3), tok1=c("a, b, c", "a, a, d", "b, d, e"), tok2=c("alpha|bravo", "alpha|charlie", "tango|tango|delta") )

(请注意列之间的不同分隔符)

通常事先不知道字符串列的数量(尽管如果我没有其他选择,我可以尝试发现整个案例)

我需要两个这样的数据框:

tok1.occurrences:
    +----+---+---+---+---+---+
    | id | a | b | c | d | e | 
    +----+---+---+---+---+---+
    |  1 | 1 | 1 | 1 | 0 | 0 |
    |  2 | 2 | 0 | 0 | 1 | 0 |
    |  3 | 0 | 1 | 0 | 1 | 1 |
    +----+---+---+---+---+---+

tok2.occurrences:
    +----+-------+-------+---------+-------+-------+
    | id | alpha | bravo | charlie | delta | tango | 
    +----+-------+-------+---------+-------+-------+
    |  1 |   1   |   1   |    0    |   0   |   0   |
    |  2 |   1   |   0   |    1    |   0   |   0   |
    |  3 |   0   |   0   |    0    |   1   |   2   |
    +----+-------+-------+---------+-------+-------+

我尝试使用这种语法:

tok1.f = factor(data$tok1)
dummies <- model.matrix(~tok1.f)

这最终导致了一个不完整的解决方案。它正确地创建了我的虚拟变量,但没有(显然)与分隔符分开。

我知道我可以使用“tm”包来查找文档术语矩阵,但对于这种简单的标记化来说似乎太多了。有没有更直接的方法?

【问题讨论】:

  • 还有here(我认为这是一个恰当的问题)。
  • 其实我已经投票重新提出这个问题。尽管它们非常相似,但它们不是精确重复的。但是,我建议您用您尝试过的方法来说明您的问题 - 如果不出意外,它将获得善意。目前,您没有编码错误/问题,您有一项任务希望其他人为您解决。
  • 我没有编码错误,因为我不知道为该任务编写哪个代码。但是,我实际上使用 tm 包做了一些测试。基本上,我使用该包针对来自各种 alpha、bravo、charlie、a、b...的术语词典构建了一个文档术语矩阵。
  • 添加了第一次(不成功)尝试

标签: r substring tokenize tm


【解决方案1】:

如果您不介意使用data.table(暂时),这可能对您有用:

library(data.table)

data <- data.frame(id=c(1,2,3), 
                   tok1=c("a, b, c", "a, a, d", "b, d, e"), 
                   tok2=c("alpha|bravo", "alpha|charlie", "tango|tango|delta"))

splitCols <- function(col_name, data) {

  # strsplit needs strings

  data[, col_name] <- as.character(data[, col_name])

  # make a list of single row data frames from the tabulation
  # of each of items from the split column

  tokens <- lapply(strsplit(data[, col_name], "[^[:alnum:]]+"), function(x) {
    tab <- table(x)
    setNames(rbind.data.frame(as.numeric(tab)), names(tab))
  })

  # use data.table's rbindlist, filling in missing values

  rbl <- rbindlist(tokens, fill=TRUE)

  # 0 out the NA's

  rbl[is.na(rbl)] <- 0

  # add the "id" column

  cbind(id=data$id, rbl)

}

lapply(names(data)[-1], splitCols, data)

## [[1]]
##    id a b c d e
## 1:  1 1 1 1 0 0
## 2:  2 2 0 0 1 0
## 3:  3 0 1 0 1 1
## 
## [[2]]
##    id alpha bravo charlie delta tango
## 1:  1     1     1       0     0     0
## 2:  2     1     0       1     0     0
## 3:  3     0     0       0     1     2

您最终会得到一个数据框列表,然后您可以根据需要对其进行处理。

【讨论】:

  • 这里的“data.table”优势在哪里?似乎大部分仍处于data.frame 操作级别....
  • rbindlist 完成填充部分。我尝试不再使用plyr,而rbindlist 填补了空白。
【解决方案2】:

您可以使用stringr 包如下:

require(stringr)

test_data <- data.frame(id=c(1,2,3), tok1=c("a, b, c", "a, a, d", "b, d, e"), tok2=c("alpha|bravo", "alpha|charlie", "tango|tango|delta") )

#conversion to character class and uniform delimeter as ","
test_data$tok1<-as.character(test_data$tok1)
test_data$tok1<-gsub(" ","",test_data$tok1)
test_data$tok2=gsub("\\|",",",as.character(test_data$tok2))

#Unique list of elements for each column
tok1.uniq=sort(unique(unlist(strsplit(as.character(test_data$tok1),","))))
tok2.uniq=sort(unique(unlist(strsplit(as.character(test_data$tok2),","))))

#Token count for each column

#In each row of token, find the count of characters using str_count from stringr package

第一栏:

tok1.occurances=do.call(cbind,lapply(tok1.uniq,function(x) {

DF=data.frame(do.call(rbind,lapply(test_data$tok1,function(y,z=x) str_count(y,z))))
colnames(DF) = x
return(DF)

}
))

#Add ID number as column
tok1.occurances=data.frame(id=as.numeric(row.names(tok1.occurances)),tok1.occurances,stringsAsFactors=FALSE)


# > tok1.occurances
# id a b c d e
#  1 1 1 1 0 0
#  2 2 0 0 1 0
#  3 0 1 0 1 1

第二栏:

tok2.occurances=do.call(cbind,lapply(tok2.uniq,function(x) {

DF=data.frame(do.call(rbind,lapply(test_data$tok2,function(y,z=x) str_count(y,z))))
colnames(DF) = x
return(DF)

}
))

tok2.occurances=data.frame(id=as.numeric(row.names(tok2.occurances)),tok2.occurances,stringsAsFactors=FALSE)


# > tok2.occurances
# id alpha bravo charlie delta tango
#  1     1     1       0     0     0
#  2     1     0       1     0     0
#  3     0     0       0     1     2

【讨论】:

    【解决方案3】:

    我能想到的最简单的方法就是将my cSplit functiondcast.data.table 结合使用,如下所示:

    library(splitstackshape)
    dcast.data.table(cSplit(data, "tok1", ", ", "long"), 
                     id ~ tok1, value.var = "tok1", 
                     fun.aggregate = length)
    #    id a b c d e
    # 1:  1 1 1 1 0 0
    # 2:  2 2 0 0 1 0
    # 3:  3 0 1 0 1 1
    
    dcast.data.table(cSplit(data, "tok2", "|", "long"), 
                     id ~ tok2, value.var = "tok2", 
                     fun.aggregate = length)
    #    id alpha bravo charlie delta tango
    # 1:  1     1     1       0     0     0
    # 2:  2     1     0       1     0     0
    # 3:  3     0     0       0     1     2
    

    编辑:更新为 library(splitstackshape),因为 cSplit 现在是该软件包的一部分。

    【讨论】:

    • 这非常有效,它就像一个魅力,tnx allot!
    猜你喜欢
    • 1970-01-01
    • 2023-03-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多