当前位置 博文首页 > 初学python数学建模之数据导入(小白篇)
编程求解一个数模问题,问题总会涉及一些数据。
有些数据是在题目的文字描述中给出的,有些数据是通过题目的附件文件下载或指定网址提供的,还有些数据是需要自己搜集的。不论是哪种方式获得的数据,也不论哪种类型的问题和算法,首先都是要把这些数据以适当的方式和格式导入到程序中。
轻则读取数据时发生错误,要浪费时间去查找和解决,在数模竞赛中就会让人非常焦躁。
数据错误还是轻的吗?对,重则读取数据有错误,程序却在继续运行,得到了错误的结果,这在数模竞赛中就更糟糕了。
你可能都不知道发生了错误,就算感觉有问题也不会把错误直接锁定到数据导入部分。
结果不停地去修改其它模块,直到把正确的模块也搞错了,最后无可救药。
因此,确保数模编程第一步“数据导入”的顺利完成,远远比你想象的更重要。
对于数学建模问题编程来说,选择什么方法最好呢?答案是:没有最好的,只有最合适的。
对于不同的问题,不同的算法,以及所调用工具包的不同实现方法,对于数据就会有不同的要求。
另外,赛题所给数据文件中的数据组织方式不同,也需要使用不同的方法来导入数据。
那么好了,既然是要具体问题具体分析,这不跟没说一样吗?这正是本文希望回答的问题,虽然针对不同问题的最佳的数据导入方法也不同,但我们先要学会一种未必最佳,但是通用、安全、简单、好学的方法。
直接在程序中向变量赋值,是虽然笨拙但最简单的方法,也许还是最可靠的方法——如果你没有敲错键盘的话。
确实,把直接赋值作为数据导入方法来介绍,实在是不好意思说出口。
但是,对于数模竞赛这种特殊的需求,直接赋值的方法还是十分常用的,而且完全符合简单、实用、可靠的要求。
不过,直接赋值也并非我们想的那么简单,还是值得认真地谈一谈。
绝大部分数学建模教材中的例程,都是使用直接赋值的方法导入数据。
很大比例的博客例程,包括本系列的大多数案例,也都是在程序中直接赋值的。
一是为了保证程序的完整性,复制粘贴回车就能得到运行结果,不需要复制数据文件等操作,就避免了由此引起的各种错误;
二是为了把读者的注意力聚焦在主要的知识点,避免干扰;
三是使例程更加直观易懂,便于理解例程的算法。
这些原因也都是直接赋值的优点。那么,这些优点不也正是数模竞赛编程活动的痛点吗?
没错,这就是直接赋值方法在数学建模培训和数模竞赛编程的实践中广泛流行的原因。
但是,即使在数模竞赛编程中,直接赋值也会有几个问题。
养成数据导入模块化的习惯,才能避免这一类的疏忽:
# 子程序:定义优化问题的目标函数 def cal_Energy(X, nVar, mk): # m(k):惩罚因子 p1 = (max(0, 6*X[0]+5*X[1]-320))**2 p2 = (max(0, 10*X[0]+20*X[1]-7027)**2 fx = -(10*X[0]+9*X[1]) return fx+mk*(p1+p2) # 子程序:模拟退火算法的参数设置 def ParameterSetting(): tInitial = 100.0 # 设定初始退火温度(initial temperature) tFinal = 1 # 设定终止退火温度(stop temperature) alfa = 0.98 # 设定降温参数,T(k)=alfa*T(k-1) nMarkov = 100 # Markov链长度,也即内循环运行次数 youcans = 0.5 # 定义搜索步长,可以设为固定值或逐渐缩小 return tInitial, tFinal, alfa, nMarkov, youcans
# 主程序 def main(): # 模型数据导入 p1 = [6, 5, -320] p2 = [10, 20, -7027] p3 = [10, 9] print(p1,p2,p3) # 算法参数设置 tInitial = 100.0 # 设定初始退火温度(initial temperature) tFinal = 1 # 设定终止退火温度(stop temperature) alfa = 0.98 # 设定降温参数,T(k)=alfa*T(k-1) nMarkov = 100 # Markov链长度,也即内循环运行次数 youcans = 0.5 # 定义搜索步长,可以设为固定值或逐渐缩小 print(tInitial, tFinal, alfa, nMarkov, youcans)
虽然很多数模竞赛的问题可以通过直接赋值获取数据,但主流的数据导入方法还是读取数据文件。
对于小白来说,特别在竞赛时,处理这些问题时都会心神不宁。
Python 中读取数据文件的方法也很多。本文非常不推荐使用 Python 自身的文件操作如打开(open)、关闭(close)、读写(read、readline)函数,而是推荐使用 Pandas 读取数据文件。
原因在于:
Pandas 使用 read_excel() 函数读取 Excel文件。
pd.read_excel(io, sheetname=0,header=0,index_col=None,names=None)
# sheetname 表示读取指定的工作表,header=0 表示首行为标题行,header=None 表示首行为数据行 df = pd.read_excel("data/youcans1.xls", sheetname='Sheet1', header=0)
pd.read_csv( filepath ,sep=',', header=‘infer', names=None, index_col=None)
# sep=','表示间隔符为逗号,header=0表示首行为标题行,header=None 表示首行为数据行 df = pd.read_csv("data/youcans2.csv", header=0, sep=',')
pd.read_table( filepath ,sep='\t', header=‘infer', names=None, index_col=None)
# sep='\t'表示分隔符为制表符,header=None 表示无标题行,第一行是数据 df = pd.read_table("data/youcans3.dat", sep="\t", header=None)
使用方法也都类似,都是一行代码搞定。例如:
由于这些文件格式中数模竞赛中很少用到,本文就不进行详细介绍了。有需要的同学可以根据函数名通过搜索引擎搜索参考资料,也可以查阅官方文档:
此外,对于大数据类的问题,所需处理的数据量可能非常大,必要时需对文件进行拆分或合并,也可以用 pandas 进行处理,这将在后续文章结合具体问题进行讲解。
【重要说明】以上章节的内容虽然介绍了数据导入的基本方法,但恐怕还是难以达到消化吸收,为我所用。
为了解决这个问题,本文将相关内容整合为例程,以便于读者学习收藏,也便于使用修改。
# mathmodel01_v1.py # Demo01 of mathematical modeling algorithm # Read data files into DataFrame. # Copyright 2021 Youcans, XUPT # Crated:2021-05-27 import pandas as pd # 读取数据文件 def readDataFile(readPath): # readPath: 数据文件的地址和文件名 # readPath = "../data/youcansxupt.csv" # 文件路径也可以直接在此输入 try: if (readPath[-4:] == ".csv"): dfFile = pd.read_csv(readPath, header=0, sep=",") # 间隔符为逗号,首行为标题行 # dfFile = pd.read_csv(filePath, header=None, sep=",") # sep: 间隔符,无标题行 elif (readPath[-4:] == ".xls") or (readPath[-5:] == ".xlsx"): # sheet_name 默认为 0 dfFile = pd.read_excel(readPath, header=0) # 首行为标题行 # dfFile = pd.read_excel(filePath, header=None) # 无标题行 elif (readPath[-4:] == ".dat"): # sep: 间隔符,header:首行是否为标题行 dfFile = pd.read_table(readPath, sep=" ", header=0) # 间隔符为空格,首行为标题行 # dfFile = pd.read_table(filePath,sep=",",header=None) # 间隔符为逗号,无标题行 else: print("不支持的文件格式。") except Exception as e: print("读取数据文件失败:{}".format(str(e))) return return dfFile # 主程序 def main(): # 读取数据文件 # Youcans, XUPT readPath = "../data/toothpaste.csv" # 数据文件的地址和文件名 dfFile = readDataFile(readPath) # 调用读取文件子程序 print(type(dfFile)) # 查看 dfFile 数据类型 print(dfFile.shape) # 查看 dfFile 形状(行数,列数) print(dfFile.head()) # 显示 dfFile 前 5 行数据 return if __name__ == '__main__': # Youcans, XUPT main()
<class 'pandas.core.frame.DataFrame'>
(30, 6)
period price average advertise difference sales
0 1 3.85 3.80 5.50 -0.05 7.38
1 2 3.75 4.00 6.75 0.25 8.51
2 3 3.70 4.30 7.25 0.60 9.52
3 4 3.70 3.70 5.50 0.00 7.50
4 5 3.60 3.85 7.00 0.25 9.33
1.本例程需要读取数据文件 “…/data/toothpaste.csv”,该文件保存在 …/data/ 目录下。读者需要修改该数据文件的文件路径和文件名,以便读取自己需要的本地文件。
2.本例程可以根据文件名的后缀自动识别文件类型,调用相应的函数读取文件。
3.本例程中读取文件模块使用 try…except 语句进行简单的异常处理。如果读取失败,可以根据抛出的异常类型查找错误。
jsjbwy