迈出脚步
体验世界

【R学习笔记】可用作统计分析的R语言操作mysql —— 中文字符集乱码的坑

老婆是学经济学的,之前用过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高手,欢迎留言解决办法

赞(0) 喜欢我
未经允许不得转载:王威 » 【R学习笔记】可用作统计分析的R语言操作mysql —— 中文字符集乱码的坑
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏