一.实验目的
- 掌握扩展库matplotlib及其依赖库的安装。
- 了解matplotlib的绘图一般过程。
- 熟练掌握折线图、散点图、柱状图、饼状图、雷达图的绘制与常用属性的设置。
- 掌握绘图区域的切分、绘制不同子图的方法。
- 熟悉坐标轴、图像标题、图例等对象的属性设置操作。
二、实验内容
已知学校附近某烧烤店2019年每个月份的营业额如表9-2所示。编写程序绘制折线图对该烧烤店全年营业额进行可视化,使用红色点划线连接每个月份的数据,并在每个月份的数据处使用三角形标记。
月份 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
营业额(万元) | 5.2 | 2.7 | 5.8 | 5.7 | 7.3 | 9.2 | 18.7 | 15.6 | 20.5 | 18.0 | 7.8 | 6.9 |
参考代码:
说明:plot()函数的第一个参数表示横坐标数据,第二个参数表示纵坐标数据,第三个参数表示颜色、线型和标记样式。
颜色常用的值有(r/g/b/c/m/y/k/w);线型常用的值有(-/--/:/-.);
标记样式常用的值有(./,/o/v/^/s/*/D/d/x/</>/h/H/1/2/3/4/_/|)
(1)程序代码:
import matplotlib.pyplot as plt
# 月份和每月营业额
months = list(range(1, 13))
money = [5.2, 2.7, 5.8, 5.7, 7.3, 9.2, 18.7, 15.6, 20.5, 18.0, 7.8, 6.9]
# 使用红色点划线连接每个月份的数据,并在每个月份的数据处使用三角形标记
plt.plot(months, money, 'r-.v')
plt.xlabel('月份', fontproperties='SimHei', fontsize=14)
plt.ylabel('营业额(万元)', fontproperties='SimHei', fontsize=14)
plt.title('烧烤店2019年营业额变化趋势图', fontproperties='SimHei', fontsize=18)
plt.tight_layout() # 紧缩四周空白,扩大绘图区域可用面积
plt.show()
(2)运行结果(截图):
根据上例中烧烤店的数据绘制柱状图,要求设置每个柱的颜色、内部填充符号、描边效果和标注文本。
(1)程序代码:
import matplotlib.pyplot as plt
months = list(range(1, 13))
money = [5.2, 2.7, 5.8, 5.7, 7.3, 9.2, 18.7, 15.6, 20.5, 18.0, 7.8, 6.9]
for x, y in zip(months, money):
# 营业额越高,颜色中的红色分量越大
color = '#%02x' % int(y*10) + '6666'
# 绘制柱状图
plt.bar(x, y, color=color, hatch='*', width=0.6,
edgecolor='black', linewidth=1.5)
# 添加文本标注
plt.text(x-0.3, y+0.2, '%.1f' % y)
plt.xlabel('月份', fontproperties='SimHei')
plt.ylabel('营业额(万元)', fontproperties='SimHei')
plt.title('烧烤店营业额', fontproperties='SimHei', fontsize=14)
plt.xticks(months) # 设置x轴刻度
plt.ylim(0, 22) # 设置y轴跨度
plt.show()
(2)运行结果(截图):
某商场安排工作人员在不同位置对手机信号强度进行测试以便进一步提高服务质量和用户体验,测试数据保存于文件“商场一楼手机信号强度.txt”中,文件中每行使用逗号分隔的三个数字分别表示商场内一个位置的x、y坐标和信号强度,其中x、y坐标值以商场西南角为坐标原点且向东为x正轴(共150米)、向北为y正轴(共30米),信号强度以0表示无信号、100表示最强。
参考代码:
说明:参考代码中,数据文件“商场一楼手机信号强度.txt”是保存在“D:\我的桌面\”,代码测试的时候,要根据实际的存放位置更改路径。轴标签和标题的字体分别是宋体(simsun)和黑体(simhei),可以根据代码实际运行平台所能提供的字体进行修改。
(1)程序代码:
import matplotlib.pyplot as plt
x, y, s, color = [], [], [], []
with open("商场一楼手机信号强度.txt") as fp:
for line in fp:
x_temp, y_temp, s_temp = map(int, line.split(','))
x.append(x_temp)
y.append(y_temp)
s.append(s_temp)
# 根据信号强度设置颜色
if s_temp < 40:
color.append('r') # 红色
elif s_temp < 70:
color.append('b') # 蓝色
else:
color.append('g') # 绿色
plt.scatter(x, y, s=[si * 3 for si in s], c=color, marker='*')
plt.xlabel('长度坐标', fontproperties='SimSun', fontsize=10)
plt.ylabel('宽\n宽\n坐\n标', fontproperties='SimSun', fontsize=14, rotation='horizontal') # 注意这里是正确的字符串 'horizontal'
plt.title('商场内信号强度', fontproperties='SimHei', fontsize=14)
plt.xlim(0, 150)
plt.ylim(0, 30)
plt.show()
(2)运行结果(截图):
对绘图区域进行切分,使用subplot()函数在每个子图中绘制不同图形。
(1)程序代码:
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(0, 2 * np.pi, 100)
y = np.sin(x ** 2)
r = np.arange(1, 6, 1)
theta = (r - 1) * (np.pi / 2)
ax1 = plt.subplot(231, projection='polar')
ax2 = plt.subplot(232, projection='polar')
ax3 = plt.subplot(233, polar=True) # 设置polar=True,等价于设置projection=polar
ax4 = plt.subplot(212, projection='polar')
ax1.plot(theta, r, linewidth=3, color='r')
ax2.scatter(theta, r, marker='*', c='g', s=50)
ax3.bar(theta, r)
ax4.plot(x, y, 'y--')
plt.tight_layout()
plt.savefig('实验4-4.jpg')
plt.show()
(2)运行结果(截图):
编写程序,根据某学生的部分专业核心课程和成绩清单绘制雷达图。
(1)程序代码:
import matplotlib.pyplot as plt
import numpy as np
courses = ['信息资源管理', '专业英语', 'Web开发技术', '数据分析基础', '多元统计分析', '算法设计与分析']
scores = [80, 85, 90, 92, 85, 75]
dataLength = len(scores)
angles = np.linspace(0, 2 * np.pi, dataLength, endpoint=False) # 生成雷达图的角度
scores.append(scores[0])
angles = np.append(angles, angles[0]) # 闭合
plt.polar(angles, scores, 'rD--', linewidth=3) # 绘制成绩雷达图
# 不包括闭合的角度
plt.thetagrids(angles[:-1] * 180/np.pi, courses, fontproperties='SimHei') # 设置雷达图的角度网格标签
plt.fill(angles, scores, facecolor='b', alpha=0.6) # 填充雷达图内部
plt.show()
(2)运行结果(截图):
三、实验任务
- 学习教材第9章内容,根据实验内容练习相关编程知识。
- 独立完成如下编程任务并提交实验报告。(报告撰写要求详见模板文档)
文件score.xlsx中存放了学生的各个科目的考试成绩(如下图),
编程实现:输入任意一个学号,将该学号对应的成绩,通过雷达图显示。
(1)程序代码:
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
df = pd.read_excel('score.xlsx')
student_id = input("请输入学号:")
# 获取指定学号的成绩数据
scores = df[df['学号'] == int(student_id)].iloc[:, 1:].values.flatten().tolist()
# 科目名称
courses = df.columns[1:]
# 绘制雷达图
data_length = len(courses)
angles = np.linspace(0, 2 * np.pi, data_length, endpoint=False)
scores.append(scores[0])
angles = np.append(angles, angles[0])
plt.figure(figsize=(6, 6))
plt.polar(angles, scores, 'rD--', linewidth=2)
plt.thetagrids(angles[:-1] * 180/np.pi, courses, fontproperties='SimHei', fontsize=10)
plt.fill(angles, scores, facecolor='b', alpha=0.6)
plt.title(f"学号为{student_id}的成绩雷达图", fontproperties='SimHei', fontsize=14)
plt.show()
(2)运行结果(截图):
统计所有同学的成绩总分,将总分在前6名的同学总分用柱形图显示出来。(纵坐标标签是“总分”,横坐标标签是“学号”,每个柱形上方显示具体的成绩总分)
(1)程序代码:
import pandas as pd
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['SimHei']# 设置matplotlib支持中文的字体
plt.rcParams['axes.unicode_minus'] = False # 正确显示负号
df = pd.read_excel('score.xlsx')
# 统计所有同学的成绩总分
df['总分'] = df.iloc[:, 1:7].sum(axis=1)
# 对总分进行排序,找出前6名的学生
top6 = df.nlargest(6, '总分')
plt.figure(figsize=(10, 4))
plt.bar(top6['学号'].astype(str), top6['总分'], color='pink')
# 在每个柱形上方显示具体的成绩总分
for i in range(len(top6['总分'])):
plt.text(i, top6['总分'].iloc[i], str(top6['总分'].iloc[i]), ha='center', va='bottom')
plt.xlabel('学号')
plt.ylabel('总分')
plt.show()
(2)运行结果(截图):
编写程序,输入任意课程名称,统计该课程的各个成绩等级的比例,用饼状图显示。(优秀:90~100;良好:80~89;中等:70~79;及格:60~69;不及格:0~59)
(1)程序代码:
import pandas as pd
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['SimHei'] # 设置matplotlib支持中文的字体
plt.rcParams['axes.unicode_minus'] = False # 正确显示负号
df = pd.read_excel('score.xlsx')
course_name = input("请输入课程名称:")
# 根据输入的课程名称获取对应的成绩列
if course_name in df.columns:
scores = df[course_name]
else:
print(f"未找到课程:{course_name}")
exit()
grades = {90: '优秀', 89: '良好', 79: '中等', 69: '及格', 59: '不及格'}
# 统计各个成绩等级的数量
labels = []
sizes = []
for grade in sorted(grades, reverse=True):
if grade == 59: # 因为成绩不可能低于0,所以不及格的下限是0
labels.append(grades[grade])
sizes.append(scores[scores <= grade].count())
else:
labels.append(grades[grade])
sizes.append(scores[(scores >= grade) & (scores < (grade + 10))].count())
plt.pie(sizes, labels=labels, autopct='%1.1f%%', startangle=140, colors=plt.cm.tab20.colors)
plt.show()
(2)运行结果(截图):
四、实验总结
一、知识点小结
在本次实验中,我深入学习了matplotlib库的安装和使用,了解了其绘图的一般流程,并熟练掌握了折线图、散点图、柱状图、饼状图和雷达图的绘制方法。此外,我还学习了如何设置图形的常用属性,如坐标轴、图像标题和图例等。通过实验,我掌握了如何切分绘图区域,并在不同的子图中绘制不同的图形,这对于数据的多角度展示非常有帮助。
二、实验体会
在实验过程中,我遇到了一些挑战,尤其是在设置中文字体和调整图形属性时。我通过查阅文档和在线资源,逐步解决了这些问题。我发现,绘图不仅仅是将数据可视化,更是一种艺术,需要细心地调整每一个细节,以达到最佳的展示效果。此外,我也体会到了编程的乐趣,尤其是在看到自己的代码成功绘制出预期图形时。
三、未解决的问题
在实验中,我注意到在某些情况下,图形的布局和预期的有所偏差。尽管我使用了plt.tight_layout()来自动调整子图参数,但在一些复杂的图形布局中,仍然需要手动进行调整以达到最佳效果。
四、改进的方法
为了改进实验过程和结果,我计划深入学习matplotlib的高级功能,如图形的保存和导出,以及更复杂的图形布局。此外,我还将探索其他数据可视化库,如seaborn,以丰富我的可视化工具箱。我相信,通过不断学习和实践,我能够更有效地将复杂的数据信息以图形的方式直观展示出来。