[問題背景]
處理以下文本文件:
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內的順序原封不動依次排開的,因爲它們都是字符串。這樣的圖顯然不是我們想要的。