繁体   English   中英

将一列分成两列并保留分隔符

[英]Split one column into two columns and retaining the seperator

我有一个非常大的数据数组:

'data.frame':   40525992 obs. of  14 variables:    
 $ INSTNM     : Factor w/ 7050 levels "A   W Healthcare Educators"     
 $ Total      : Factor w/ 3212 levels "1","10","100",    
 $ Crime_Type : Factor w/ 72 levels "MURD11","NEG_M11",    
 $ Count      : num  0 0 0 0 0 0 0 0 0 0 ...

Crime_Type列包含犯罪类型和年份,因此“ MURD11”是2011年的谋杀案。这些是我的孩子正在为她的学校项目分析的大学校园犯罪统计数据,当她被困时,我会提供帮助。 我目前只能创建一个可以分析的干净数据文件

一旦我使用“收集”将宽文件(列中的所有犯罪类型为“ 9”,都转换为长文件)的文件大小从300MB变为8 GB。 我正在处理的文件是8GB。 那是问题吗? 如何将其转换为data.table以进行更快的处理?

我想要做的是split这个“Crime_Type”列到两列“Crime_Type”和“年”。 数据包含字母数字和数字。 还有一些特殊字符,例如NEG_M,即“过失杀人狂”。

稍后我们将替换全名,但有人可以建议我如何分开

MURD11-> MURD和11(两列)NEG_M10-> NEG_M和10(两列)

等等...

我尝试使用

df <- separate(totallong, Crime_Type, into = c("Crime", "Year"), sep = "[:digit:]", extra = "merge")
df <- separate(totallong, Crime_Type, into = c("Year", "Temp"), sep = "[:alpha:]", extra = "merge")

第一个在寻找数字时将犯罪分开。 第二个根本不起作用。

我也试过

df$Crime_Type<- apply (strsplit(as.character(df$Crime_Type), split="[:digit:]"))

那根本不起作用。 我已经阅读了很多有关堆栈溢出的文章,这就是我得到这些命令的地方,但是现在我真的很受困扰,感谢您的帮助。

由于您已经在使用tidyr (如separate ),请尝试extract函数,给定一个正则表达式,它将每个捕获的组放入一个新列中。 “ Crime_Type”是所有非数字内容,“ Year”是数字内容。 相应地调整正则表达式。

library(tidyr)
extract(df, 'Crime_Type', into=c('Crime', 'Year'), regex='^([^0-9]+)([0-9]+)$')

base R ,一种选择是在非数字部分和数字部分之间创建唯一的分隔符。 通过将非数字( [^0-9]+ )和数字( [0-9]+ )字符括在括号( (..) )中,可以将它们捕获为一组,在替换中,我们使用\\\\1对于第一捕获基团,接着一个,并且所述第二组( \\\\2 )。 可以将其用作带有sep=',' read.table输入向量sep=','以读取为两列。

 df1 <- read.table(text=gsub('([^0-9]+)([0-9]+)', '\\1,\\2', 
                   totallong$Crime_Type),sep=",", col.names=c('Crime', 'Year'))
 df1
 #   Crime Year
 #1  MURD   11
 #2 NEG_M   11

如果需要,可以与原始数据集cbind

cbind(totallong, df1)

或在base R ,我们可以将strsplitsplit一起使用,以指定非数字( (?<=[^0-9]) )和数字( (?=[0-9]) )之间的边界。 在这里,我们使用lookarounds来匹配边界。 输出将是一个list ,我们可以rbindlist的元素do.call(rbind并将其转换为data.frame

as.data.frame(do.call(rbind, strsplit(as.character(totallong$Crime_Type), 
                        split="(?<=[^0-9])(?=[0-9])", perl=TRUE)))
#     V1 V2
#1  MURD 11
#2 NEG_M 11

或者从tstrsplit的开发版本中data.table即。 v1.9.5 同样在这里,我们使用相同的regex 此外,还可以选择将输出列转换为不同的class

library(data.table)#v1.9.5+
setDT(totallong)[, c('Crime', 'Year') := tstrsplit(Crime_Type, 
    "(?<=[^0-9])(?=[0-9])",  perl=TRUE, type.convert=TRUE)]
#   Crime_Type Crime Year
#1:     MURD11  MURD   11
#2:    NEG_M11 NEG_M   11

如果我们在输出中不需要'Crime_Type'列,则可以将其分配为NULL

totallong[, Crime_Type:= NULL]

注意:安装说明版本的说明在here


或更快的办法是stri_extract_alllibrary(stringi)折叠行一个字符串(“V2”)后。 可以通过使用seq索引来提取'v3'中的备用元素以创建新data.frame

library(stringi)
v2 <- paste(totallong$Crime_Type, collapse='')
v3 <- stri_extract_all(v2, regex='\\d+|\\D+')[[1]]
ind1 <- seq(1, length(v3), by=2)
ind2 <- seq(2, length(v3), by=2)
d1 <- data.frame(Crime=v3[ind1], Year= v3[ind2])

基准测试

v1 <- do.call(paste, c(expand.grid(c('MURD', 'NEG_M'), 11:15), sep=''))
set.seed(24)
test <- data.frame(v1= sample(v1, 40525992, replace=TRUE ))

system.time({
   v2 <- paste(test$v1, collapse='')
   v3 <- stri_extract_all(v2, regex='\\d+|\\D+')[[1]]
   ind1 <- seq(1, length(v3), by=2)
   ind2 <- seq(2, length(v3), by=2)
   d1 <- data.frame(Crime=v3[ind1], Year= v3[ind2])
 })
 #user  system elapsed 
 #56.019   1.709  57.838 

数据

totallong <- data.frame(Crime_Type= c('MURD11', 'NEG_M11'))

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM