python数学建模数据导入 初学python数学建模之数据导入(小白篇)
youcans 人气:01. 数据导入是所有数模编程的第一步
编程求解一个数模问题,问题总会涉及一些数据。
有些数据是在题目的文字描述中给出的,有些数据是通过题目的附件文件下载或指定网址提供的,还有些数据是需要自己搜集的。不论是哪种方式获得的数据,也不论哪种类型的问题和算法,首先都是要把这些数据以适当的方式和格式导入到程序中。
- 如果数据格式有问题
轻则读取数据时发生错误,要浪费时间去查找和解决,在数模竞赛中就会让人非常焦躁。
数据错误还是轻的吗?对,重则读取数据有错误,程序却在继续运行,得到了错误的结果,这在数模竞赛中就更糟糕了。
你可能都不知道发生了错误,就算感觉有问题也不会把错误直接锁定到数据导入部分。
结果不停地去修改其它模块,直到把正确的模块也搞错了,最后无可救药。
因此,确保数模编程第一步“数据导入”的顺利完成,远远比你想象的更重要。
- Python 语言中数据导入的方法很多
对于数学建模问题编程来说,选择什么方法最好呢?答案是:没有最好的,只有最合适的。
对于不同的问题,不同的算法,以及所调用工具包的不同实现方法,对于数据就会有不同的要求。
另外,赛题所给数据文件中的数据组织方式不同,也需要使用不同的方法来导入数据。
那么好了,既然是要具体问题具体分析,这不跟没说一样吗?这正是本文希望回答的问题,虽然针对不同问题的最佳的数据导入方法也不同,但我们先要学会一种未必最佳,但是通用、安全、简单、好学的方法。
2. 在程序中直接向变量赋值
直接在程序中向变量赋值,是虽然笨拙但最简单的方法,也许还是最可靠的方法——如果你没有敲错键盘的话。
确实,把直接赋值作为数据导入方法来介绍,实在是不好意思说出口。
但是,对于数模竞赛这种特殊的需求,直接赋值的方法还是十分常用的,而且完全符合简单、实用、可靠的要求。
不过,直接赋值也并非我们想的那么简单,还是值得认真地谈一谈。
2.1 为什么直接赋值?
绝大部分数学建模教材中的例程,都是使用直接赋值的方法导入数据。
很大比例的博客例程,包括本系列的大多数案例,也都是在程序中直接赋值的。
- 其原因在于
一是为了保证程序的完整性,复制粘贴回车就能得到运行结果,不需要复制数据文件等操作,就避免了由此引起的各种错误;
二是为了把读者的注意力聚焦在主要的知识点,避免干扰;
三是使例程更加直观易懂,便于理解例程的算法。
这些原因也都是直接赋值的优点。那么,这些优点不也正是数模竞赛编程活动的痛点吗?
没错,这就是直接赋值方法在数学建模培训和数模竞赛编程的实践中广泛流行的原因。
2.2 直接赋值的问题与注意事项
但是,即使在数模竞赛编程中,直接赋值也会有几个问题。
- 一是某些问题不能使用直接赋值方法。这主要是大数据的问题,数据量或数据文件的数量极大,已经不能使用直接赋值实现了。
- 二是一些问题虽然可以直接赋值,但很容易出错。这主要是数据量很大,或者数据结构、类型比较复杂的问题。例如,多元分析、时间序列、数据统计类的题目可能都有很大的数据量,在附件中提供数据文件。
这时如果在使用直接赋值导入数据,不再是敲键盘了,而是从文件中把数据复制粘贴到程序中。这时要特别注意的问题是:文件中的数据分隔符是什么,空格还是逗号,与变量赋值的格式要求是否一致?
即使文件中的数据分隔符看上去是空格,也需要检查到底是空格还是制表符,是一个空格还是几个空格?
文件中的数据有没有错漏等异常?
这在读取文件中可以通过程序检查、识别和处理,在复制粘贴时就要人工处理了。 - 三是数据量不大的问题,完全可以用直接赋值导入数据,但也会由于疏忽大意而出错。这倒不是说敲错键盘了,而是由于例程不一定是把数据赋值作为独立模块处理的,而是分散在算法的过程中进行赋值。
同学在使用和修改例程时时,就很容易忘记修改算法过程中的变量赋值。
这种情况屡见不鲜,有时是因为对程序没有搞明白,忽略了算法步骤中的某个变量;
更多时候是忙中出错,在反复调试和更换数据时晕头转向,只顾了修改开始的数据而疏忽了后面的数据。
养成数据导入模块化的习惯,才能避免这一类的疏忽:
- 将数据导入模块作为单独的函数。
- 如果不愿意使用数据导入函数,则要把数据导入部分集中写成一段,放在程序的起始部分。
- 不要把问题本身的数据导入与算法所需的参数赋值混淆,分为两个独立的函数或段落。
例程 1:将数据导入作为单独的函数
# 子程序:定义优化问题的目标函数 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
例程 2:将数据导入集中写成一段,放在程序的起始部分
# 主程序 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)
3. Pandas 导入数据
虽然很多数模竞赛的问题可以通过直接赋值获取数据,但主流的数据导入方法还是读取数据文件。
- 数学建模中常用的数据文件格式有文本文件(.txt)、Excel 文件(.xls, .xlsx)和 csv 文件(.csv)。
- 在读取文本文件时,会遇到逗号、空格、制表符等不同的数据分割符。
- 读取 Excel 文件时,首先 .xls 与 .xlsx 的格式不同,其次要考虑数据表带不带标题行,有时文件中还有多个工作表。
- 读取文件时还会遇到数据缺失,非法字符。
对于小白来说,特别在竞赛时,处理这些问题时都会心神不宁。
Python 中读取数据文件的方法也很多。本文非常不推荐使用 Python 自身的文件操作如打开(open)、关闭(close)、读写(read、readline)函数,而是推荐使用 Pandas 读取数据文件。
原因在于:
- Pandas 提供了多种常用文件格式的读写函数,以上各种情况都能一行代码搞定。
- Pandas 是基于 NumPy 构建的数据分析工具包,便于进行数据整理与清洗,操作方便灵活。
- Pandas 提供了与其它各种数据结构的转换工具,使用简单灵活。
- 很多数学建模算法的例程就是使用 Pandas 的 Series、DataFrame 数据结构,无需进行转换。
3.1 Pandas 读取 Excel 文件
Pandas 使用 read_excel() 函数读取 Excel文件。
pd.read_excel(io, sheetname=0,header=0,index_col=None,names=None)
pd.read_excel() 的主要参数:
- io : 文件路径(包括文件名)。
- header :指定作为列名的行。默认为 0,即首行为标题行。设置 header=None,表示无标题行,首行就是数据行。
- sheetname:指定工作表。默认为 sheetname=0。设置 sheetname=None 返回全表, 设置 sheetname=[0,1] 返回多表 。
- index_col :指定作为行索引的列编号或列名。
- names:指定列名, 类型为 list。
pd.read_excel() 使用实例:
# sheetname 表示读取指定的工作表,header=0 表示首行为标题行,header=None 表示首行为数据行 df = pd.read_excel("data/youcans1.xls", sheetname='Sheet1', header=0)
3.2 Pandas 读取 csv 文件
Pandas 使用 pandas.read_csv() 函数读取 Excel文件。
pd.read_csv( filepath ,sep=',', header=‘infer', names=None, index_col=None)
pd.read_csv() 的主要参数:
- filepath : 文件路径(包括文件名)。
- sep:指定分隔符。默认为逗号 ‘,',可根据需要设置其它分隔符。
- header :指定作为列名的行。**如果文件没有列名则默认为 0,表示首行就是数据行;设置 header=None,表示无标题行,首行就是数据行。
- index_col :指定作为行索引的列编号或列名。
- names:指定列名, 类型为 list。
pd.read_csv() 使用实例:
# sep=','表示间隔符为逗号,header=0表示首行为标题行,header=None 表示首行为数据行 df = pd.read_csv("data/youcans2.csv", header=0, sep=',')
3.3 Pandas 读取文本文件
对于文本文件 .txt 和 .dat,可以使用 pandas.read_table() 函数读取 。
pd.read_table( filepath ,sep='\t', header=‘infer', names=None, index_col=None)
pd.read_table() 的主要参数:
- filepath : 文件路径(包括文件名)。
- sep:指定分隔符。默认为 tab 制表符,可根据需要设置其它分隔符。
- header :指定作为列名的行。**如果文件没有列名则默认为 0,表示首行就是数据行;设置 header=None,表示无标题行,首行就是数据行。
- index_col :指定作为行索引的列编号或列名。
- names:指定列名, 类型为 list
pd.read_table() 使用实例:
# sep='\t'表示分隔符为制表符,header=None 表示无标题行,第一行是数据 df = pd.read_table("data/youcans3.dat", sep="\t", header=None)
3.4 Pandas 读取其它文件格式
Pandas 还提供了读取多种文件格式的函数
使用方法也都类似,都是一行代码搞定。例如:
- pandas.read_sql,读取 SQL 数据库
- pandas.read_html,抓取网页中的表格数据
- pandas.read_json,读取 JSON 数据文件
- pandas.read_clipboard,读取剪贴板内容
由于这些文件格式中数模竞赛中很少用到,本文就不进行详细介绍了。有需要的同学可以根据函数名通过搜索引擎搜索参考资料,也可以查阅官方文档:
- Pandas 输入输出函数的说明文档 Input/output — pandas 1.2.4 documentation (pydata.org)
- http://pandas.pydata.org/pandas-docs/stable/reference/io.html
此外,对于大数据类的问题,所需处理的数据量可能非常大,必要时需对文件进行拆分或合并,也可以用 pandas 进行处理,这将在后续文章结合具体问题进行讲解。
4. 数据导入例程
【重要说明】以上章节的内容虽然介绍了数据导入的基本方法,但恐怕还是难以达到消化吸收,为我所用。
为了解决这个问题,本文将相关内容整合为例程,以便于读者学习收藏,也便于使用修改。
例程01:读取数据文件
# 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()
例程01 运行结果:
<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 语句进行简单的异常处理。如果读取失败,可以根据抛出的异常类型查找错误。
加载全部内容