Python Vs MATLAB

 

——从一次数模美赛的亲身经历谈python在数学建模竞赛中的应用

 

说起数学建模中的编程软件,大部分人都会想到Matlab。Matlab应该是史上功能最强大的一门编程与数学仿真软件,许多复杂的数学问题对它来说都so easy。有经验的都明白,做数模少了MATLAB是万万不能的,什么画个图像啊,搞个拟合分析矩阵计算啊啥的没了它很难玩得转,但MATLAB也不是万能的,至少,在文本数据处理方面,我认为它远不如python,这个文本数据挖掘领域的神器。

 

说实话,电脑安装Matlab有一年多了,但我至今还是个MATLAB菜鸟,原因一是不太喜欢Matlab的语法,二是日常生活中几乎用不到,三是不能理解复杂的矩阵运算,更不懂啥神经网络之类的算法,所以只能把它当画图工具了。但是我也参加数模比赛,比如前两次参加比赛我用的是java,编程效率不是一般的低,以至于我参加比赛时80%的时间都用在了写程序上,真心浪费了很多时间。不过这次改用python后,效率有所提高。

 

废话不多讲了,现在就让我们见识一下如何用Python处理数学建模问题。

 

一、Matlab中引入数据

 

在介绍Python之前,我们先来看看我们在MATLAB中是怎么引入数据的:

 

首先,如果是用空格,逗号,等分隔符隔开的很有规律的数据文件,引入Matlab是很简单的,像我这种小白不看任何教程也能摸索出来,步骤很简单:

 

数学建模可以用python已有的库吗 数学建模比赛可以用python_数据


 

<!--[endif]-->

 

1、点击右上方workspace框中的Import data按钮

 

数学建模可以用python已有的库吗 数学建模比赛可以用python_数据_02


 

<!--[endif]-->

 

2、选择数据文件

 

 

 

数学建模可以用python已有的库吗 数学建模比赛可以用python_数学建模可以用python已有的库吗_03


 

<!--[endif]-->

 

3、选择数据间的分隔符—>next

 

数学建模可以用python已有的库吗 数学建模比赛可以用python_数据_04


 

<!--[endif]-->

 

4、finish

 

数学建模可以用python已有的库吗 数学建模比赛可以用python_json_05


 

<!--[endif]-->

 

现在,workspace中多了一个名为data的矩阵,说明数据引入成功了。

 

不得不说这种数据引入方式是很赞的,大多数数据文件可以一键引入,很省事。然而这只是对于格式有规律的数据来说的,对于格式没规律的数据,直接import显然行不通了,需要自己写程序来搞定,比如这次美赛中我们遇到的Edros0列表:

 

数学建模可以用python已有的库吗 数学建模比赛可以用python_matlab_06


 

<!--[endif]-->

 

     这组文档有18000行,记录的是著名学者Edros(很NB的)的学术合作关系网络,其中开头没有空格行且全大写的人名与Edros有直接合作的,即所谓的Edros数为1学者,共511位,比如AGOH, TAKASHI;列在他们的后面的则是与这511位学者有合作关系的学者,他们大多数与Edros没有直接合作关系,姓名只有首字母大写。当然,那511位学者之间也有互相合作,比如AGOH, TAKASHI与GRANVILLE, ANDREW JAMES,我们的任务就是,找出所有这511位之间的合作关系,得到511个节点的关系网络。

 

显然,对于这个问题,直接import行不通,需要自己写程序进行简单的文本分析。但是,我个人认为用Matlab的程序处理这个文本一点也不轻松,看看Matlab的文件处理函数你就知道了:fopen、fsacnf。。。——和C语言没有区别啊(不是说C语言不好而是在比赛中太浪费时间了),而且结果保存为矩阵也不方便啊,所以我想到了用python处理这个文本。

 

二、python中的数据处理

 

       Python没有matlab中import data这样给力的功能,但相比Matlab,Python中的文件操作简单太多了。举个例子,python中只需2句话:

 

Data = open(‘XXX’)
For each_line in data:
。。。

  

 

 

你就可以打开并遍历文件的每一行。再配合python灵活的字符串与数据操作方式,从文本中提取信息并不难,具体只要三步:

 

1、数据读取

 

用程序解决这个问题,无论用什么,第一步无疑都是文件的读取,所以我先替去除了着18000行文本,保存为data.txt,在同一目录下新建data_processer.py。然后:

 

Data = open(‘data.txt’)
For each_line in data:

  

 

遍历文本每一行。

 

       2、节点获取

 

第二步,我们要找到这511位学者所在的行并获得结点。对此,我们先分析一下文本的特点,发现除了姓名全部大写外,这些都含有四位数的年份,于是,我们可以根据数字的个数来得到这些人名的所在行,对此我写了一个函数来获取字符串中的数字个数:

 

 

def num_count(s):
    num_count = len(filter(lambda x:x.isdigit(),s))
    return num_count

 

 

为了得到511个姓名,我们先把数字个数大于4的行根据空格分割成很多片段,记为flagments列表:

 

flagments = each_line.strip().split(' ')

 

 

再把年份之前的字符串都拼起来,就得到了姓名: 

      

#找到年份对应字符串,并得到名字的函数
def find_name(flagments):
    index =  0
    for each in flagments:
        if  num_count(each) < 4:
            index+=1
        else:
            name = ''
            for i in range(index):
                if(cmp('',name) == -1):
                    name += ' '
                name += flagments[i]
            return name

  

 

首先,新建一个叫all_nodes的列表,来储存所有的节点。

 

 

all_nodes = []

 

 

然后,调用函数get_nodes得到所有节点: 

       

def get_nodes:
    for each_line in data:
        #如果这一行中含有年份,那么提取节点名称
        if num_count(each_line) >= 4:
            #把字符串按空格分割
            flagments = each_line.strip().split(' ')
            #得到名字
            name = find_name(flagments)
            #添加新节点
            new_node = {'id':id,'line_index':line_index,'name':name,'in_link':[],'out_link':[],'in_link_node_count':0,'out_link_node_count':0}
            all_nodes.append(new_node)
            line_of_nodes.append(line_index)
            id+=1
        lines.append(each_line)
        line_index += 1#记录行号

   

这里,我把每个节点定义为一个字典,赋予了节点id编号、姓名name,同时新建了列表link来储存它连接的节点的id,用。另外,我还记录了每个节点的首行号,方便后面的工作。

 

      下一步,就是得到节点间的连接关系了

 

3、得到节点间的连接关系

       这步工作也很简单,刚才我们记录了每个节点所在行的行号,相邻行号之间的部分就是这个节点的合著者名单,把名单中的人与511个节点中的姓名一一对比,就可以得到它所连接的节点,代码如下:

 

#1 找到每个节点的区间,逐行匹配,找到其他的人就加一
for i in range(id):
    for j in range(line_of_nodes[i]+1,line_of_nodes[i+1]):
        for k in range(id):
            if cmp(all_nodes[k]['name']+'\n',lines[j])==0:
                all_nodes[i]['in_link'].append(k)
                all_nodes[i]['in_link_node_count'] += 1

OK,完成了以上几步工作,我们已经得到了构建网络所需的所有的信息——511个节点与每个节点连接信息,就可以得到关系网络了,根据all_nodes列表中的信息,配合pajek软件,我们得到这样一张网络图:

数学建模可以用python已有的库吗 数学建模比赛可以用python_数学建模可以用python已有的库吗_07


 

<!--[endif]-->

       怎么样,是不是很有成就感,嘿嘿。

三、用Python处理JSON格式数据

       在上面的例子中,我们借助Python处理文本成功构建了Edros网络,下面我们继续用Python构建另一个网络。顺便认识一下一种常见的数据格式——JSON格式。

题目的第四问,要求我们自己基于另一组数据建立另一个的网络,重新评估自己的算法。不得不说这一问不好做,因为在短时间内找到类似的数据真心很难,自己写爬虫爬数据我也不会。不过我的运气实在是太好,没费多大功夫就在数据堂网站上找到了一组名为“十三万条微博互粉记录”的数据。不是我自夸,这么合适的数据简直就像是为本题量身打造的啊。微博关注网络,和Edros网络如出一辙啊有木有,更巧的是节点个数——521个,和511个很相近有木有!!!

废话不多讲,就让我们先来看看这组数据的真面目。

数学建模可以用python已有的库吗 数学建模比赛可以用python_数学建模可以用python已有的库吗_08


 

<!--[endif]-->

这个4兆多的文档,我截取了一小部分,说实话,一看到这组数据我就笑了——这不就是python中的字典打印出来的格式吗!后来我查了资料,知道原来这种格式叫JSON格式,源于JavaScript。详见百度百科:

http://baike.baidu.com/link?url=IWjHSgBSGudp0w6sKwrRRbiIIL2tXL4wfzu-uTC052mzvA4BKdHO2BoYRLajAFc0

这组数据中同样用字典来定义节点,其中“node”为节点对应的微博号码,“count”为该用户的关注数,“biFriends”为用户关注的用户id列表。

由于本来就是Python默认的数据输出格式,所以使用Python来处理这组数据,出奇的简单,直接调用python内置的JSON库函数,只要几行代码:

 

data = open('相互关注.txt')
for each_line in data:
    sss = json.loads(each_line)
    all_infos.append(sss)
    sss['id'] = id
    sss['links'] = []
    id += 1

 

 

这样就把文档直接存为字典列表了。

用同样的方法,我们可以得到521个节点的微博关注网络,画出来是这个样:

 


数学建模可以用python已有的库吗 数学建模比赛可以用python_c/c++_09


 

 

<!--[endif]-->

额,乌压压的一片TOT。。。好吧,我承认,数据确实太多,以至于我自己写的这个小程序要跑整整5分钟!

我抽取的其中连接数最多的30个节点,图好看多了:



数学建模可以用python已有的库吗 数学建模比赛可以用python_c/c++_10


 

  

<!--[endif]-->

从图中可能看不出来,但是从统计数据中我们可以清楚地看到,id为1197161814的用户与所有其他520的节点用户都有连接,显然,这是这13万条数据的爬行起点,1197161814节点扮演的角色和Edros一样,所有的关系网络都是从他辐射出来的。

当然,除了引入这些数据,python程序进行简单的数据计算也so easy,和C语言啥的差不多,鄙人对数学模型方面也只能算个菜鸟,建模没有太多亮点,在此我就不多介绍了,所有的代码和数据文件都打包上传了,有兴趣的可以参考一下,希望能给大家一起启发,也欢迎各位提出建议,我们共同进步!