[问题背景]
处理以下文本文件:
grade.txt
ANSI编码格式,共三条数据,每条数据的第一项是姓名,第二项是语文,第三项是数学,第四项是英语
张三,128,136,112
李四,99,106,73
王五,102,148,88
要求将文件改造成适合scikitlearn下KMeans聚类方法处理的形式(生成两个列表,一个是学生姓名,一个是学生成绩)
并计算出每个学生的总分。
[问题分析]
这里的核心是做数据预处理工作,需要把.txt文件处理成Python能处理的格式,例如列表
首先编写一个函数loadData,传参是文件路径filePath,返回两个列表,一个是学生姓名retName,一个是学生成绩retGrade。
def loadPath(fileName):
然后用open函数(可读可写r+模式)打开filePath路径的文件,用文件对象fr承接open()的返回值。
fr = open(filePath, 'r+')
接下来调用fr对象的readlines()方法函数将文件逐行读取,每行存入列表后返回,返回的列表我们用一个列表lines接收。这里每一行都是字符串形式。
lines = fr.readlines()
这时lines列表是这样的:
['张三,128,136,112\n', '李四,99,106,73\n', '王五,102,148,88']
接下来我们生成两个列表retName和retData,一开始它们都是空列表
用retName记录学生姓名,用retData记录分数
retName = []
retData = []
然后用for循环对lines列表的每一个元素进行数据提取的操作:
for line in lines:
例如第一个line就是 '张三,128,136,112\n'
第二个line是 '李四,99,106,73\n'
第三个line是 '王五,102,148,88'
我们发现对于 '张三,128,136,112\n' 和 '李四,99,106,73\n' 都有\n这样的换行符,而line本身是字符串,我们可以用字符串的strip()方法,这个方法设计用于返回被删去开头和结尾的空格或换行符的行字符串。
把上述for循环改写为这样:
for line in lines:
line.strip()
注意line是临时变量,line字符串已经没有换行符\n了,但是lines里的每个元素并没有变。
各个line字符串分别是:
'张三,128,136,112'
'李四,99,106,73'
'王五,102,148,88'
接着我们就可以用字符串的split()函数将字符串进行分割,其中split()的传参用',' 表示按英文逗号分隔,返回存放各个项的列表,我们用item来承接,for循环改写为如下:
for line in lines:
items = line.strip().split(',')
每个items列表依次是:
['张三', '128', '136', '112']
['李四', '99', '106', '73']
['王五', '102', '148', '88']
然后我们就可以把每个items的第一项(姓名)用列表的append()方法追加到retName列表,把每个items的其余项追加到retData列表。其中要注意的是retData里的数据应该是数值型的,具体说是浮点型以用于计算,而这里items每一项却都是字符串型的,所以要先经过强制转换成float的过程。
retData里有三个列表,分别对应三个学生,因此每次循环都要append()一个列表,写成append([...])的形式。
[...]中写 float(item[i]) 并在后面补充说明 for i in range(1, len(items))
for循环改写为如下:
for line in lines:
items = line.strip().split(',')
retName.append(items[0])
retData.append([float(items[i]) for i in range(1, len(items))])
return retName, retData
之后的 retName 为 ['张三', '李四', '王五']
retData 为 [[128.0, 136.0, 112.0], [99.0, 106.0, 73.0], [102.0, 148.0, 88.0]]
然后返回两个列表retName和retData,完整的函数loadData定义如下:
def loadData(filePath):
fr = open('grade.txt')
lines = fr.readlines()
retName = []
retData = []
for line in lines:
items = line.strip().split(',')
retName.append(items[0])
retData.append([float(items[i]) for i in range(1, len(items))])
return retName, retData
到此我们的loadData()函数就能将.txt文本文件改造成适合scikitlearn下KMeans聚类方法处理的形式了。
最后再写一下计算平均分的过程,相当于把loadData()返回的列表用于实际应用:
def loadData(filePath):
fr = open('grade.txt')
lines = fr.readlines()
retName = []
retData = []
for line in lines:
items = line.strip().split(',')
retName.append(items[0])
retData.append([float(items[i]) for i in range(1, len(items))])
return retName, retData
if __name__ == '__main__':
Names, Grades = loadData('grade.txt')
for i in range(len(Names)):
print(Names[i], '的平均分是 %.2f' % (sum(Grades[i])/3))
其中if __name__ == '__main__':的背景是 在cmd中直接运行.py文件时,则__name__的值是'__main__',这是便于在cmd中调用.py文件
注:grade.txt需要以ANSI编码格式保存在与.py文件同一目录下。
可以直接运行.py,也可以通过cmd运行,cmd命令是:(假设grade.txt和grade.py都存放在E盘下:)
E:
grade.py
就可以看到运行结果了。
张三 的平均分是 125.33
李四 的平均分是 92.67
王五 的平均分是 112.67
本文参考了礼欣、嵩天老师课程关于 31省市居民家庭消费调查 的代码。
[拓展延伸]
处理以下股票信息,文件名为stock.txt,ANSI编码,并用matplotlib在IDLE中显示图像:(不用cmd,因为涉及到了matplotlib库)
Monday,3368,3389
Tuesday,3381,3391
Wednesday,3388,3402
Thursday,3391,3411
Friday,3405,3446
Saturday,3431,3429
Sunday,3428,3447
每行第一项是日期,第二项是开盘价,第三项是收盘价。
效果图:
import matplotlib.pyplot as plt
def loadData(filePath):
fr = open(filePath, 'r+')
lines = fr.readlines()
Date = []
Begin = []
End = []
for line in lines:
items = line.strip().split(',')
Date.append(items[0])
Begin.append(float(items[1]))
End.append(float(items[2]))
return Date, Begin, End
if __name__ == '__main__':
Date, Begin, End = loadData('stock.txt')
plt.title('Stock')
plt.xlabel('date')
plt.ylabel('price')
plt.plot(Date, Begin)
plt.plot(Date, End)
plt.show()
提示:新手常犯的错误之一是忘记将items[1]和items[2]由字符串类型强转成float类型。
否则,plot()将会把字符串一字排开,而非按照浮点数大小描绘轴线。
错误效果:
这里的3368~3428,3389~3447就是按照.txt内的顺序原封不动依次排开的,因为它们都是字符串。这样的图显然不是我们想要的。