老婆是学经济学的,之前用过R语言,也因此知道了R语言在统计分析中的强大之处,后来也了解到,R语言是做生物信息学的重要分析工具,因此对此颇有兴趣。
正好上周Professor告诉我,罗马一大有个R课程,如果我有兴趣,可以去学习一下,我当然是非常有兴趣,因此跑过去听了两天节课,虽然老师全程用英语授课,但是其语速之快,连其他好几个意大利学生都没能怎么听清楚,我也就只能听懂一部分,不过还好,跟着老师的例子进行,加上之前自学了PHP,算得上有一些编程上的思维,也就勉强能跟得上,后来回来之后又各种百度谷歌搜了一下
由于我平时做网站前段比较多,最近有正好需要更新一个数据库表,因此我就这样挑了个坑分非常多的小项目——操作含中文字符的mysql,开始了我的R之旅,其中的恶心程度,难以言喻
背景:已有一份完整的期刊分区与影响因子的表,含有2008年至2018年的影响因子,以及2017年的中科院分区,当然还包含期刊名、issn、所属分类(中文字符),投稿建议(中文字符),投稿难度(中文字符),审稿周期(中文字符)等必要字段。
目的:将中科院发布的2018年的分区、web of science发布的2019年影响因子更新到上述表中,并添加2019年web of science新增的期刊(以及老的表中之前遗漏期刊)
关键点:将最新数据按照期刊一一对应的关系更新到老的数据库表中
坑:可能老外在设计R语言的时候,就压根没考虑过会有人拿来操作mysql数据库表中的中文字符吧
其他的不多说了,直接上源码(虽然代码只有这么几行,但是作为新手的我,耗费了周末两整天才搞定)
#install.packages("RMySQL") install.packages("RODBC") #library(RMySQL) library(RODBC) conn <- odbcConnect("mydata", uid = "root", pwd = "root", DBMSencoding = "UTF8") #连接数据库 factor <- sqlFetch(conn, "factor", stringsAsFactors = FALSE) #获取factor数据库表内的数据,stringsAsFactors = FALSE 表示不将字符串转换为factor因子,表观体现为:去掉个元素后面的levels fenqu <- read.csv("D:\\软件\\R study\\file\\fenqu.csv", head = T, encoding = "UTF-8", stringsAsFactors = FALSE) #以UTF-8编码读取中科院发表的最新分区数据 if2019 <- read.csv("D:\\软件\\R study\\file\\jcr.csv", head = T, encoding = "UTF-8", stringsAsFactors = FALSE) #以UTF-8编码读web of science发表的最新影响因子数据 if2019 <- subset(if2019, select = -Rank) #删除影响因子中的Rank这一列(多余操作) factor <- subset(factor, select = -fenqu) #删除factor中,fenqu这一列(因为这一列是去年发布的,已经过期了,所以需要删除) factor$fenqu = rep(NA, nrow(factor)) #为factor表加上fenqu这一列,数值为空 factor$if19 = rep(NA, nrow(factor)) #为factor表加上2019年影响因子这一列,数值为空 if19_index <- duplicated(if2019[, "ISSN"]) if19 = if2019[!if19_index,] #以ISSN作为索引,去掉最新影响因子表中重复期刊 for (i in 1:nrow(factor)) { a = fenqu[which(toupper(fenqu$journal) == toupper(factor[i, "name"])), "fenqu"] #因为中科院最新分区表中的期刊没有ISSN,所以只能用期刊名来匹配,R语言是区分大小写的,为了避免因大小写问题导致匹配不正确,所以将期刊名全部转化为大写之后再做比较,a等于分区表中,与factor表中期刊名相同的那一行(或多行)里面的分区值 b = if19[which(if19$ISSN == factor[i,'issn']),"Journal.Impact.Factor"] #web of science公布的数据里面,包含有issn号,所以直接用issn来匹配,可保万无一失,b表示if19这个data.frame中,与factor表中issn相同的那一行数据里面的影响因子值 print(i) if (length(a) != 0) { if (length(a) == 1) { factor[i, "fenqu"] <- a } else { #因为中科院分区中,同一个期刊可能存在大类分区和小类分区,此时选择分区数值小的(分区数值越小,在一定程度上表明期刊越好) factor[i, "fenqu"] <- min(a) } } if (length(b) != 0) { factor[i,"if19"] <- b } } #上述for循环为,将2019年的影响因子及最新分区按正确顺序填充到factor表中 #下面开始获取web of science公布的2019年数据中新增的sci期刊(以及老的factor表中之前遗漏的期刊),共379个 name=rep(NA,379) abname=rep(NA,379) issn=rep(NA,379) aif19 = rep(NA, 379) id = rep(NA,379) a = 0 b = 32426 #因为之前的factor表中,id值已经排到了32425,所以这里从32426开始给id赋值 for (j in 1:11822) { #最新的2019年影响因子表中含有11822个不重复的期刊 c = factor[which(factor$issn == if19[j, "ISSN"]),"issn"] if (length(c) == 0) { a = a + 1 id[a] = b name[a] = if19[j, "Full.Journal.Title"] abname[a] = if19[j, "JCR.Abbreviated.Title"] issn[a] = if19[j, "ISSN"] aif19[a] = if19[j, "Journal.Impact.Factor"] print(a) b = b + 1 #print(if19[j, "ISSN"]) } } newdata = data.frame(id, name, abname, issn, "if19" = aif19, "if18" = rep(NA, 379), "if17" = rep(NA, 379), "if16" = rep(NA, 379), "if15" = rep(NA, 379), "if14" = rep(NA, 379), "if13" = rep(NA, 379), "if12" = rep(NA, 379), "if11" = rep(NA, 379), "if10" = rep(NA, 379), "if09" = rep(NA, 379), "if08" = rep(NA, 379), "sci" = rep(NA, 379), "cat" = rep(NA, 379), "country" = rep(NA, 379), "allcat" = rep(NA, 379), "url" = rep(NA, 379), "tgurl" = rep(NA, 379), "zhouqi" = rep(NA, 379), "num" = rep(NA, 379), "shengao" = rep(NA, 379), "nandu" = rep(NA, 379), "oa" = rep(NA, 379), "fenqu" = rep(NA, 379)) ##newdata为:web of science发布的2019年影响因子表中,比原来的factor表多出的期刊,共379个 #从下面开始解决直接将factor以及newdata写入mysql表时出现乱码的问题,因为newdata这个data.frame中没有中文字符,所以无需对newdata进行处理 for(k in 1:ncol(factor)){ factor[,k] <- as.character(factor[,k]) print(mode(factor[,k])) } #将factor表中的所有数据转换为字符型,因为数值型的数据,在写入csv文件时,无法加上双引号,会导致后面读取失败 write.table(factor, file = "newfactor.csv", sep=";",fileEncoding = "UTF-8",quote = T,na = "NA",col.names = T, row.names = F) #以UTF-8编码将factor数据写入newfactor.csv文件,用分号;表示分隔符,每个数值都加入双引号 newfactor <- read.csv("newfactor.csv", encoding="UTF-8", sep=";", quote="",check.names = F,stringsAsFactors = FALSE) #以UTF-8编码读取newfactor.csv文件为newfactor表,尽管上面导出的时候,quote = T,但此处quote必须为FALSE,因为newfactor.csv文件中含有中文,R读取的时候,无法直接识别到中文之间的分隔符; 但是能够识别到";" 因此,必须做上述处理,才能分割成功。这几步project中最大的坑 #如果不加上check.names = F,则列标题会自动加上X. #不要认为上述先写入再读取是多余操作,一点都不多余,此操作是为了将所有数据集转化为UTF-8编码(个人认为,应该是这个原因),否则写入数据库的时候会乱码 #同样也不要认为上面加入双引号和下面去掉双引号是多余操作,此操作是为了让R语言识别出两个相邻的中文字符之间的分隔符,让R语言知道这是两个中文字段,而不是一个字段,就是苦思冥想了两天,才想到这个办法的 factor1 = as.data.frame(gsub('"', '', as.matrix(newfactor)),stringsAsFactors = FALSE) #由于上述读取文件时,quote = FALSE,所以读取过来的数据中,每个数据都被双引号包裹的,要先将数值中的双引号去掉。 names(factor1) <- gsub('"','',names(factor1)) #去掉列标题中的双引号 factor1 <- factor1[-which(factor1$abname == ""),] #读取的newdata(11740行)比原来的factor(11710行)多了30行,原因未知,因此,删掉多出来额这30行,30行有个特点:abname字段为空(除了id以外,其他所有字段都为空,此处我选择的为abname字段作为判断) sqlSave(conn, factor1, tablename = "factor1", append = FALSE, addPK = "id", rowname = FALSE) #创建factor1表,并将factor1(加上了2019年影响因子以及最新分区之后的表)写入factor1表中 sqlSave(conn, newdata, tablename = "factor1", append = TRUE, addPK = "id", rowname = FALSE) #将比原来的factor表多出的379个期刊数据追加到表中
上述处理完成后,mysql数据库factor1表中,每个字段都是varchar 255类型,需要手动改一下每个字段类型
问题:
1、为什么不用RMySQL
答:不是不想用,是里面的bug没有解决,按照网上各种教程所说,读取中文字符的坑我已经解决了,但是写入的时候的坑始终无法解决
install.packages("RMySQL") library(RMySQL) con2 <- dbConnect(MySQL(), host = "localhost", dbname = "dbname", user = "root", password = "root", DBMSencoding = "GBK") dbSendQuery(con2, 'SET NAMES GBK') dbSendQuery(con2, "SET GLOBAL local_infile =1") factor <- dbGetQuery(con2, "SELECT * FROM factor")
中间对数值进行各种处理转换与上述第一种方法的一样,最后写入数据库的代码如下
dbWriteTable(con2, "factor2", factor, append = F, row.names = F)
报错信息 :
> dbWriteTable(con2, “factor2″, factor, append = F, row.names = F)
Error in .local(conn, statement, …) : could not run statement: Invalid utf8 character string: ”
>
什么?你说我连接数据的时候应该用 DBMSencoding = “UTF-8″?我试过,这样读出来的数据都是乱码,更别说后续操作了
即使我将mysql表的各种字段的字符集以及表的字符集改成utf-8,一样的读出来乱码,反正就是各种乱码,如果用RMySQL的话,就只有用GBK才能保证读出来的不乱码,而且,在后面写入数据库表的时候,那个错误死活解决不了,即使是从表中读出来的原数据立马写入,一样的会有那个报错。
据说dbWriteTable的写入效率要比sqlSave高,我也想用高效率的,奈何这个bug实在解决不了,如果有R高手,欢迎留言解决办法