使用R语言解决半结构化数据向结构化数据的转换

网络数据时代中不仅仅产生结构化数据,同时也会产生半结构化和非结构化数据。对于半结构化和非结构化数据往往需要结构化处理,然后运用处理后的数据进行统计建模分析。本文尝试使用R语言实现半结构化数据向结构化数据的转变。


结构化数据就是常见的二维表数据,具有工整的字段、字段类型、字段大小等信息;半结构化数据常出现在XML、HTML之类及自描述,数据结构和内容混杂在一起的内容;非结构化数据就是无法用简单的数据或文字形式进行记录,常表现为图片、音频、视频等数据。


函数简介

本文将对日志文件进行结构化处理,处理中用到几个重要的函数,即gregexpr()、substring()、strsplit()、grep()和ldply()函数。下面对这几个函数做一下说明:

gregexpr()、strsplit()和grep()函数常用于正则表达式的使用,语法如下:

gregexpr(pattern, text, ignore.case = FALSE,

perl = FALSE,fixed = FALSE,

useBytes = FALSE)


strsplit(x, split, fixed = FALSE, perl = FALSE,

useBytes = FALSE)


grep(pattern, x, ignore.case = FALSE, perl = FALSE,

value = FALSE,fixed = FALSE, useBytes = FALSE,

invert = FALSE)


pattern指定正则表达式;

x指定需要处理的文本或字符;

split指定文本或字符的分隔符;

text指定需要分析的文本;

ignore.case是否忽略正则表达式的大小写,默认区分大小写;

perl使用perl语法下的正则格式,默认不使用perl语法;

fixed指定正则表达式是否作为一个整体进行匹配,默认为FALSE;

value为FALSE时返回符合正则表达式条件的索引,TRUE时返回满足条件的具体值。


substring()函数为字符串函数,用于提取字符串子集,语法如下:

substring(text, first, last = 1000000L)


text指定所要分析的字符串;

first指定从first位置开始截取母字符串text;

last控制返回子字符串的长度,子字符串长度等于:last-first+1。


ldply()函数是数据格式的整合函数,表示列表数据向数据库数据的转换,语法如下:

ldply(.data, .fun = NULL, ..., .progress = "none",

.inform = FALSE,.parallel = FALSE,

.paropts = NULL, .id = NA)


.data为向量或列表格式的数据;

.fun指定为.data中每个元素执行的函数;


应用

假设一条日志文件的格式如下图所示:

0?wx_fmt=png
下面需要从该日志记录中截取TimeStamp、ConsumerID、ProductsCount等红色部分的值,并返回为数据框格式。下面讲解一下本人解决该问题的思路:


1)发现所要获取的数据存在一个明显的结构,即“英文字符+冒号+数字” 或 “英文字符+等号+数字”。可以使用gregexpr()函数和substring()函数返回满足这些结构内容


str <- ‘Visitor=BBStore&TimeStamp=20150701105747350&......’

使用gregexpr()函数返回满足“英文字符+冒号+数字”结构的内容所在str中的索引(下标)

t1 <- gregexpr('[a-zA-Z]+\\:[0-9]+',str)

使用gregexpr()函数返回满足”英文字符+等号+数字“结构的内容所在str中的索引(下标)

t2 <- gregexpr('[a-zA-Z]+\\=[0-9]+',str)

0?wx_fmt=png
第一个红框返回的是:满足正则表达式的首个字符下标;第二个红框返回的是:满足正则表达式的字符长度。


使用substring()函数获取满足条件的子字符

res1 <- substring(str,t1[[1]],t1[[1]]+

attr(t1[[1]],'match.length')-1)

res2 <- substring(str,t2[[1]],t2[[1]]+

attr(t2[[1]],'match.length')-1)

0?wx_fmt=png
第一个结果返回的是:满足“英文字符+冒号+数字”的所有字符串;第二个结果返回的是:满足“英文字符+等号+数字”的所有字符串。


合并res1和res2的结果

res <- c(res1,res2)

0?wx_fmt=png
显示了所以需要满足条件的字符串。


2)抓取想要获取的内容

使用grep()函数抓取带有'ConsumerID:'字样的索引(下标),使用strsplit()函数将抓取出来的内容用指定分割符分割。

grep('ConsumerID:',res)

res[grep('ConsumerID:',res)]

strsplit(res[grep('ConsumerID:',res)],':')

ConsumerID <- strsplit(res[grep('ConsumerID:',res)],':')[[1]][2]

ConsumerID

0?wx_fmt=png


同理获得TimeStamp的内容

grep('TimeStamp=',res)

res[grep('TimeStamp=',res)]

strsplit(res[grep('TimeStamp=',res)],'=')

TimeStamp <- strsplit(res[grep('TimeStamp=',res)],'=')[[1]][2]

#使用as.Date()函数将字符型数据转换为日期型数据

TimeStamp <- as.Date(substring(TimeStamp,1,8),

format = '%Y%m%d')

TimeStamp

0?wx_fmt=png

3)根据以上的思路,自定义函数

自定义函数如下:

test <- function(str){

t1 <- gregexpr('[a-zA-Z]+\\:[0-9]+',str)

t2 <- gregexpr('[a-zA-Z]+\\=[0-9]+',str)

res1 <- substring(str,t1[[1]],t1[[1]]+attr(t1[[1]],

'match.length')-1)

res2 <- substring(str,t2[[1]],t2[[1]]+attr(t2[[1]],

'match.length')-1)

res <- c(res1,res2)


TimeStamp <- as.Date(strsplit(res[grep('TimeStamp=',

res)],'=')[[1]][2],format = '%Y%m%d')

ConsumerID <- strsplit(res[grep('ConsumerID:',res)],

':')[[1]][2]

ProductsCount <- strsplit(res[grep('ProductsCount:',

res)],':')[[1]][2]

ProductID <- matrix(unlist(strsplit(res[grep('ProductID:',

res)],':')),ncol = 2, byrow = TRUE)[,2]

#检验日志文件中是否含有”Barcode:“字样

if(length(grep('Barcode:',res)) == 0){

Barcode <- matrix('',ncol = 2,

nrow = length(ProductID),byrow = TRUE)[,2]

}

else{

Barcode <- matrix(unlist(strsplit(res[grep('Barcode:',

res)],':')),ncol = 2, byrow = TRUE)[,2]

}


#检验日志文件中是否含有”StoreID=“字样

if(length(grep('StoreID=',res)) == 0){

StoreID <- ''

}

else{

StoreID <- unlist(strsplit(res[grep('StoreID=',

res)],'='))[2]

}


#返会函数值

return(data.frame(TimeStamp=TimeStamp,

ConsumerID=ConsumerID,

StoreID=StoreID,

ProductsCount=ProductsCount,

Barcode=Barcode,

ProductID=ProductID))

}


4)将数据转换为数据框格式

library(plyr)

my.data <- ldply(.data = str, .fun = test)

my.data

0?wx_fmt=png
根据自定义函数中的if条件判断,字符串str中没有“StoreID:”的字样时返回空值,结果中也说明了str中确实没有StoreID:。


大数据集中自定义函数性能检测

到目前为止,该自定义函数已经实现了非结构化数据到结构化数据的转换,下面应用到比较大的数据中,看看该函数的性能如何。


读取数据

library(xlsx)

setwd('C:\\Users\\admin\\Desktop')

data <- read.xlsx2('test.xlsx', sheetIndex = 1)

nrow(data)

str <- as.character(data$Parm)

length(str)

0?wx_fmt=png
该数据集中含有14267行日志记录。


使用自定义函数,生成数据框数据

my.data <- ldply(.data = str, .fun = test)

head(my.data)

nrow(my.data)

0?wx_fmt=png
结果将14267条日志数据解析成56031条记录。


函数性能(运行所需时间)

system.time(ldply(.data = str, .fun = test))

0?wx_fmt=png
本电脑为4GB内存,通过运行14267条日志花费88秒时间,也许性能不算高,还望读者能够提供更好的思维方式和方法。


总结:本文所涉及到的R包和函数

xlsx包

read.xlsx2()

stats包

as.Date()

gregexpr()

substring()

strsplit()

grep()

unlist()

matrix()

plyr包

ldply()

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值