R语言列表操作和连接MongoDB经验之谈
在做Teamfight Tactics分析时,我先通过Riot's API收集到了许多场次的对局数据。如何将这些复杂的数据转换成可以直接使用的数据,方便进行分析呢?
RStudio

RStudio

R语言的好处

在做Teamfight Tactics分析时,我先通过Riot’s API收集到了许多场次的对局数据。如何将这些复杂的数据转换成可以直接使用的数据,方便进行分析呢?

之前一直用的是Python,思路是通过pymongo包连接MongoDB提取数据,再通过python内建函数进行转换和分析。这样需要不停使用Table["sub"]['subsub']这样的格式取得内部结构。这部分内容没有代码提示,需要程序员自行记忆每个Key对应的结构,不仅麻烦,而且容易出错。可以预计分析数据的时候会用到大量循环,而python的效率让人缺乏信心。再加上后续还有画图步骤,而matplotlib是一个非常麻烦的东西,我有点害怕。

也考虑过使用Matlab,它的好处是不少循环可以转为向量化运算,速度会快不少,而且画图容易。Matlab的附加功能里有一个Database Toolbox Interface for MongoDB。我甚至已经使用它进行了一段时间分析了,但后面发现默认生成的.mat格式转换为其他格式比较困难。而且运行速度也不是很理想,消耗资源很大,400MB的数据量在运算时常常卡死我的电脑。也可能是因为我不常使用它,所有代码本身编写的也不够好。

最后终于接触到了R语言,这真是个好工具。配合RStudio,顺手又高效。它解决了上面的所有问题,而且内建和社区的函数和包为分析过程提供了很大帮助,为我节省了很多时间。它的一些用法和其他语言显得有些奇怪,理解和熟悉需要一些时间,但R语言的学习绝对物超所值。

李东风老师的开源R语言教程很好,推荐一下:https://www.math.pku.edu.cn/teachers/lidf/docs/Rbook/html/_Rbook/index.html

R语言的数据类型

向量

向量是将若干个基础类型相同的值存储在一起,各个元素可以按序号访问。需要注意的是,R语言各种类型的数据结构的下标都是从1开始的。例如

# 数值型
marks <- c(10, 6, 4, 7, 8)
x <- c(1:3, 10:13)
# 逻辑型
c(1, 3, 5) > 2  # [1] FALSE  TRUE  TRUE
# 字符型
s1 <- c('abc', '', 'a cat', NA, '李明')

内建的大多数函数以向量为参数时,会对向量内的元素逐个运算,然后返回一个和原来一样长的向量。

sqrt(c(1, 4, 6.25)) # [1] 1.0 2.0 2.5

x <- c(33, 55, 11)
order(x)    # [1] 3 1 2
x[order(x)] # [1] 11 33 55

列表

R中列表(list)类型来保存不同类型的数据。一个主要目的是提供R分析结果输出包装:输出一个变量,这个变量包括回归系数、预测值、残差、检验结果等等一系列不能放到规则形状数据结构中的内容。实际上,数据框也是列表的一种,但是数据框要求各列等长,而列表不要求。

列表可以命名,命名后类似字典,可以通过名字访问。

rec <- list(name="李明", age=30,
  scores=c(85, 76, 90))
rec
## $name
## [1] "李明"
## 
## $age
## [1] 30
## 
## $scores
## [1] 85 76 90

rec$age # 或 rec[["age"]]

也可以通过下标访问,但需要两重中括号。如果使用单重方括号对列表取子集,结果还是列表而不是列表元素。

rec[[3]]
## [1] 85 76 90
rec[[3]][2]
## [1] 76

rec[3]
## $scores
## [1] 85 76 90

要删除某个元素,直接将该元素赋值为NULL;添加元素则是直接为该元素赋值。

rec[["age"]] <- NULL
rec[["身高"]] <- 178
## $name
## [1] "李明"
## 
## $三科分数
## [1] 85  0 90
## 
## $身高
## [1] 178

数据框

数据框类似于数据库表或Excel数据表的形式。这样形式的数据在R中叫做数据框(data.frame)。数据框类似于一个矩阵,有$n$个横行、$p$个纵列,但各列允许有不同类型:数值型向量、因子、字符型向量、日期时间向量。同一列的数据类型相同。数据框是一个特殊的列表,其每个列表元素都是一个长度相同的向量。

数据框可以说是处理数据的最佳方式,利用它可以轻松按列或按行取值,并且可以直接用向量化的方式计算。

d<-data.frame(TraitsComb=character(0),Top4=double(0),Winner=double(0),AvgPlacement=double(0),PickTimes=integer(0))
print(d)
## [1] TraitsComb   Top4         Winner       AvgPlacement PickTimes   
## <0 行> (或0-长度的row.names)

d[1,]<-list("a", 1.2, 2.3, 3.4, 5)
print(d)
##   TraitsComb Top4 Winner AvgPlacement PickTimes
## 1          a  1.2    2.3          3.4         5

colnames(d)
## [1] "TraitsComb"   "Top4"         "Winner"       "AvgPlacement" "PickTimes"

d$Winner
## [1] 2.3
d[3]
##   Winner
## 1    2.3
d[3][1]
##   Winner
## 1    2.3

R语言函数

字符串

观察下面的例子,只用了1行代码就实现了我想要的字符串拼接。

tiers<-rev(c("DIAMOND","PLATINUM","GOLD","SILVER","BRONZE","IRON"))
ranks<-rev(c("I","II","III","IV","V"))
tire_order<-lapply(tiers,function(x) paste(x,ranks,sep="-")) %>% unlist

## [1] "IRON-V"          "IRON-IV"         "IRON-III"        "IRON-II"         "IRON-I"          "BRONZE-V"       
## [7] "BRONZE-IV"       "BRONZE-III"      "BRONZE-II"       "BRONZE-I"        "SILVER-V"        "SILVER-IV"      
## [13] "SILVER-III"      "SILVER-II"       "SILVER-I"        "GOLD-V"          "GOLD-IV"         "GOLD-III"       
## [19] "GOLD-II"         "GOLD-I"          "PLATINUM-V"      "PLATINUM-IV"     "PLATINUM-III"    "PLATINUM-II"    
## [25] "PLATINUM-I"      "DIAMOND-V"       "DIAMOND-IV"      "DIAMOND-III"     "DIAMOND-II"      "DIAMOND-I"      

这里lapply是对列表遍历,将每个元素先赋值为x,然后应用function(x)函数。paste函数是将两个参数连接起来,sep是连接符。

%>%是一个pipe,它将结果传递给另一个函数作为第一个参数。unlist是将列表转换为向量。

排序

对一张以下格式的数据框排序,可以使用order函数。倒序只需要在排序的列名前加-号。

head(traits_comp_discribe)
##                                  TraitsComb  Top4 Winner AvgPlacement PickTimes
## 1                               Transformer 0.253  0.025        5.696        79
## 2 Bruiser,Cuddly,Glutton,Sister,Transformer 0.922  0.314        2.373        51
## 3                           Arcanist,Cuddly 0.662  0.166        3.593       145
## 4                                 Socialite 0.217  0.024        6.133        83
## 5                         Chemtech,Twinshot 0.462  0.154        4.231        13
## 6              Clockwork,Cuddly,Transformer 0.750  0.250        3.875         8

head(traits_comp_discribe[order(-traits_comp_discribe$Top4),])
##                    TraitsComb  Top4 Winner AvgPlacement PickTimes
## 7              Academy,Cuddly,Imperial    1  0.500        3.000         4
## 11            Bruiser,Sister,Socialite    1  0.400        6.400         5
## 32     Cuddly,Glutton,Sister,Socialite    1  0.333        2.000         3
## 34                           Mercenary    1  0.071        7.286        14
## 37 Bruiser,Sister,Transformer,Twinshot    1  0.333        3.000         3
## 38          Bodyguard,Cuddly,Syndicate    1  0.250        3.500         4

MongoDB

MongoDB是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富却最像关系数据库的。它提供了一个非常简单的接口,可以让你在简单的程序中操作数据库。

在R语言中,连接MongoDB可以使用mongolite

install.packages("mongolite")
library(mongolite)

url <- "mongodb://user:pass@site:port"
db<- "tft" # 数据库名

# 连接数据库
s_con <- mongo(collection='summoner',
               db=db,
               url=url)

# 查询数据 (查询等级低于100的召唤师)
summoners <- s_con$find('{"summonerlevel": { "$lt": 100 }}')
head(summoners)
##   id      puuid     summonerlevel summonertire summonerrank
## 1  4    ********            66     PLATINUM            I
## 2  5    ********             1      DIAMOND           II
## 3  6    ********            85       MASTER            I
## 4  8    ********             1     PLATINUM           IV
## 5  9    ********            20     PLATINUM           II
## 6 12    ********            19     PLATINUM           II

最后修改于 2022-05-09