疯狂Python讲义学习笔记(含习题)之数据可视化

文章目录

  • 一、使用Matplotlib生成数据图
    • (一)、安装Matplotlib包
    • (二)Matplotlib数据图入门
    • (三)管理图例
    • (四)管理坐标轴
    • (五)管理多个子图
  • 功能丰富的数据图
    • (一)饼图
    • (二)柱状图
    • (三)水平柱状图
    • (四)散点图
    • (五)等高线图
    • (六)3D图形
  • 三、使用Pygal生成数据图
    • (一)安装Pygal包
    • (二)Pygal数据图入门
    • (三)配置Pygal数据图
  • 四、Pygal支持的常见数据图
    • (一)折线图
    • (二)水平柱状图和水平折线图
    • (三)叠加柱状图和叠加折线图
    • (四)饼图
    • (五)点图
    • (六)仪表(Gauge)图
    • (七)雷达图
  • 五、处理数据
    • (一)CSV文件格式
    • (二)JSON数据
    • (三)数据清洗
    • (四)读取网络数据
  • 练习

Python为数据展示提供了大量优秀的功能包,其中Matplotlib和Pygal是两个极具代表性的功能包。

一、使用Matplotlib生成数据图

Matplotlib是一个非常优秀的Python 2D 绘图库,只要给出符合格式的数据,通过Matplotlib就可以方便地制作折线图、柱状图、散点图等各种高质量的数据图。

(一)、安装Matplotlib包

pip安装

pip install matplotlib

(二)Matplotlib数据图入门

Matplotlib的用法非常简单,对于最简单的折线图来说, 程序只需根据需要给出对应的X 轴、Y 轴数据, 调用pyplot 子模块下的plot()函数即可生成简单的折线图。
示例

import matplotlib.pyplot as plt

# 定义两个列表分别作为X轴、Y轴数据
x_data = ['2011', '2012', '2013', '2014', '2015', '2016', '2017']
y_data = [58000, 60200, 63000, 71000, 84000, 90500, 107000]
# 第一个列表代波啊横坐标的值,第二个列表代表纵坐标的值
plt.plot(x_data, y_data)
# 调用show()函数显示图形
plt.show()

运行效果:

疯狂Python讲义学习笔记(含习题)之数据可视化_第1张图片
如果在调用plot()函数时只传入一个list列表,则该列表的数据将作为y轴数据,Matplotlib会自动使用0, 1, 2, 3作为X轴数据。
效果如下图
疯狂Python讲义学习笔记(含习题)之数据可视化_第2张图片

plot()函数除了支持创建具有单挑折线的折线图,也支持创建包含多条折线的复式折线图,只要在调用plot()函数时传入多个分别代表x轴和y轴数据的list列表即可。
示例

import matplotlib.pyplot as plt

x_data = ['2011', '2012', '2013', '2014', '2015', '2016', '2017']
# 定义两个列表分别作为两条折线的Y轴数据
y_data = [58000, 60200, 63000, 71000, 84000, 90500, 107000]
y_data1 = [52000, 54200, 51500, 58300, 56800, 59500, 62700]
# 传入两组分别代表X轴,Y轴的数据的list列表
plt.plot(x_data, y_data, x_data, y_data1)
# 调用show()函数显示图形
plt.show()

运行效果:
疯狂Python讲义学习笔记(含习题)之数据可视化_第3张图片

也可以将以上代码中的 `` plt.plot(x_data, y_data, x_data, y_data1) ```改为多次调用plot()的方式:

plt.plot(x_data, y_data)
plt.plot(x_data, y_data1)

同样可以在调用plot()函数时传入额外的参数来指定折线的样子、如线宽、颜色、样式等。
示例

import matplotlib.pyplot as plt

x_data = ['2011', '2012', '2013', '2014', '2015', '2016', '2017']
# 定义两个列表分别作为两条折线的Y轴数据
y_data = [58000, 60200, 63000, 71000, 84000, 90500, 107000]
y_data1 = [52000, 54200, 51500, 58300, 56800, 59500, 62700]
# 指定折线的颜色、线宽和样式
plt.plot(x_data, y_data, color='red', linewidth=2.0, linestyle='--')
plt.plot(x_data, y_data1, color='blue', linewidth=3.0, linestyle='-.')
# 调用show()函数显示图形
plt.show()

运行效果:

疯狂Python讲义学习笔记(含习题)之数据可视化_第4张图片
样式字符串支持如下参数值:

参数值 描述
- 代表实线,默认值
代表虚线
: 代表点线
-. 代表短线、点相间的虚线

(三)管理图例

可以通过legend()函数来为折线添加图例,为该函数传入两个list参数,其中第一个list参数(handles参数)用于引用折线图上的每条折线,第二个list参数(labels)代表为每条折线所添加的图例。
示例

import matplotlib.pyplot as plt

x_data = ['2011', '2012', '2013', '2014', '2015', '2016', '2017']
# 定义两个列表分别作为两条折线的Y轴数据
y_data = [58000, 60200, 63000, 71000, 84000, 90500, 107000]
y_data1 = [52000, 54200, 51500, 58300, 56800, 59500, 62700]
# 指定折线的颜色、线宽和样式
ln1, = plt.plot(x_data, y_data, color='red', linewidth=2.0, linestyle='--')
ln2, = plt.plot(x_data, y_data1, color='blue', linewidth=3.0, linestyle='-.')
# 调用legend()函数设置图例
plt.legend(handles=[ln2, ln1],
           labels=['示例2年销售量', '示例1年销售量'],
           loc='lower right')
# 调用show()函数显示图形
plt.show()

运行效果:

疯狂Python讲义学习笔记(含习题)之数据可视化_第5张图片
以上代码中的loc参数指定图例添加位置,该参数支持如下参数值:

参数值 描述
best 自动选择最佳位置
upper right 将图例放在右上角
upper left 将图例放在左上角
lower left 将图例放在左下角
lower right 将图例放在右下角
right 将图例放在右边
center left 将图例放在左边居中的位置
center right 将图例放在右边居中的位置
lower center 将图例放在底部居中的位置
upper center 将图例放在顶部居中的位置
center 将图例放在中心

中文乱码解决方案
① 使用matplotlib.font_manager子模块下的FontProperties类加载中文字体。
② 在调用legend()函数时通过prop属性指定使用中文字体。

示例

import matplotlib.pyplot as plt
import matplotlib.font_manager as fm

x_data = ['2011', '2012', '2013', '2014', '2015', '2016', '2017']
# 定义两个列表分别作为两条折线的Y轴数据
y_data = [58000, 60200, 63000, 71000, 84000, 90500, 107000]
y_data1 = [52000, 54200, 51500, 58300, 56800, 59500, 62700]
# 指定折线的颜色、线宽和样式
ln1, = plt.plot(x_data, y_data, color='red', linewidth=2.0, linestyle='--')
ln2, = plt.plot(x_data, y_data1, color='blue', linewidth=3.0, linestyle='-.')
# 使用Matplotlib的字体管理器加载中文字体
my_font = fm.FontProperties(fname="C:\Windows\Fonts\msyh.ttc")
# 调用legend()函数设置图例
plt.legend(handles=[ln2, ln1],
           labels=['示例2年销售量', '示例1年销售量'],
           loc='lower right',
           prop=my_font)
# 调用show()函数显示图形
plt.show()

运行效果:
疯狂Python讲义学习笔记(含习题)之数据可视化_第6张图片

以上程序使用FontProperties类加载c:\windows\Fonts\msyh.ttc文件所对应的中文字体。
legend()函数可以不指定handles参数,只传入labels参数,这样该labels参数将按顺序为直线图中的多条折线添加图例。
Matplotlib也允许在调用plot()函数时为每条折线分别传入label参数,这样程序在调用legend()函数时就无须传入labels、handles参数了。

永久改变Matplotlib的默认字体
在Python的交互解释器中输入如下命令:
疯狂Python讲义学习笔记(含习题)之数据可视化_第7张图片
编辑配置文件,找到如下行:
在这里插入图片描述
将此行代码修改为如下形式,并去掉前面的#号:
在这里插入图片描述

(四)管理坐标轴

可以调用xlable()和ylabel()函数分别设置X轴、Y轴的名称,也可以通过title()函数设置整个数据图的标题,还可以调用xticks()、yticks()函数分别改变X轴、Y轴的刻度值(允许使用文本作为刻度值)。
示例

import matplotlib.pyplot as plt

x_data = ['2011', '2012', '2013', '2014', '2015', '2016', '2017']
# 定义两个列表分别作为两条折线的Y轴数据
y_data = [58000, 60200, 63000, 71000, 84000, 90500, 107000]
y_data1 = [52000, 54200, 51500, 58300, 56800, 59500, 62700]
# 指定折线的颜色、线宽和样式
plt.plot(x_data,
         y_data,
         color='red',
         linewidth=2.0,
         linestyle='--',
         label='图例1年销量')
plt.plot(x_data,
         y_data1,
         color='blue',
         linewidth=3.0,
         linestyle='-.',
         label='图例2年销量')
# 调用legend()函数设置图例
plt.legend(loc='best')
# 设置两个坐标轴的名称
plt.xlabel("年份")
plt.ylabel("销量")
# 设置数据图的标题
plt.title("历年销量图")
# 设置Y轴上的数值文本
# 第一个参数是点的位置,第二个参数是点的文字提示
plt.yticks([50000, 70000, 100000], [r'挺好', r'优秀', r'火爆'])
# 调用show()函数显示图形
plt.show()

运行效果:
疯狂Python讲义学习笔记(含习题)之数据可视化_第8张图片

若要对X轴、Y轴进行更细致的控制,可以调用gca()函数来获取坐标轴信息对象,然后对坐标轴进行控制。

示例

import matplotlib.pyplot as plt

x_data = ['2011', '2012', '2013', '2014', '2015', '2016', '2017']
# 定义两个列表分别作为两条折线的Y轴数据
y_data = [58000, 60200, 63000, 71000, 84000, 90500, 107000]
y_data1 = [52000, 54200, 51500, 58300, 56800, 59500, 62700]
# 指定折线的颜色、线宽和样式
plt.plot(x_data,
         y_data,
         color='red',
         linewidth=2.0,
         linestyle='--',
         label='图例1年销量')
plt.plot(x_data,
         y_data1,
         color='blue',
         linewidth=3.0,
         linestyle='-.',
         label='图例2年销量')
# 调用legend()函数设置图例
plt.legend(loc='best')
# 设置两个坐标轴的名称
plt.xlabel("年份")
plt.ylabel("销量")
# 设置数据图的标题
plt.title("历年销量图")
# 设置Y轴上的数值文本
# 第一个参数是点的位置,第二个参数是点的文字提示
plt.yticks([50000, 70000, 100000], [r'挺好', r'优秀', r'火爆'])
ax = plt.gca()
# 设置将x轴的刻度值放在底部X轴上
ax.xaxis.set_ticks_position('bottom')
# 设置将Y轴的刻度值放在左边Y轴上
ax.yaxis.set_ticks_position('left')
# 设置右边坐标轴线的颜色(设置为none表示不显示)
ax.spines['right'].set_color('none')
# 设置顶部坐标轴线的颜色(设置为none表示不显示)
ax.spines['top'].set_color('none')
# 定义底部坐标轴线的位置(放在70000数值处)
ax.spines['bottom'].set_position(('data', 70000))
# 调用show()函数显示图形
plt.show()

运行效果:
疯狂Python讲义学习笔记(含习题)之数据可视化_第9张图片

以上代码中使用plt.gca()获取了一个AxesSubplot对象,然后调用该对象的xaxis属性的set_ticks_position()方法设置X轴刻度值的位置。通过spines属性可以访问数据图四周的坐标轴线(Spine对象)

(五)管理多个子图

使用Matplotlib 除可以生成包含多条折线的复式折线图之外,它还允许在一张数据图上包含多个子图。
调用subplot()函数可以创建一个子图,然后程序就可以在子图上进行绘制。subplot(nrows, ncols, mdex, **kwargs)函数的时ows 参数指定将数据图区域分成多少行; ncols 参数指定将数据图区域分成多少列: index参数指定获取第几个区域。

示例

import matplotlib.pyplot as plt
import numpy as np

plt.figure()
# 定义从-pi到pi之间的数据,平均去64个数据点
x_data = np.linspace(-np.pi, np.pi, 64, endpoint=True)
# 将整个figure分成两行两列,第三个参数表示将该图形放在第1个网格中
plt.subplot(2, 2, 1)
# 绘制正弦曲线
plt.plot(x_data, np.sin(x_data))
plt.gca().spines['right'].set_color('none')
plt.gca().spines['top'].set_color('none')
plt.gca().spines['bottom'].set_position(('data', 0))
plt.gca().spines['left'].set_position(('data', 0))
plt.title('正弦曲线')

# 将整个figure分成两行两列,并将该图形房子啊第2个网格中
plt.subplot(222)
# 绘制余弦曲线
plt.plot(x_data, np.cos(x_data))
plt.gca().spines['right'].set_color('none')
plt.gca().spines['top'].set_color('none')
plt.gca().spines['bottom'].set_position(('data', 0))
plt.gca().spines['left'].set_position(('data', 0))
plt.title('余弦曲线')

# 将整个figure分成两行两列,并将该图形放在第3个网格中
plt.subplot(223)
# 绘制正切曲线
plt.plot(x_data, np.tan(x_data))
plt.gca().spines['right'].set_color('none')
plt.gca().spines['top'].set_color('none')
plt.gca().spines['bottom'].set_position(('data', 0))
plt.gca().spines['left'].set_position(('data', 0))
plt.title('正切曲线')

plt.show()

运行效果:
疯狂Python讲义学习笔记(含习题)之数据可视化_第10张图片

还可以让某个子图占用多个网格,例如将以上代码中的plt.subplot(2, 2, 1)改为 plt.subplot(2, 1, 1),然后依次修改另外两个子图的位置。
还可以使用GridSpec对绘图区域进行分割。
示例

import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
import numpy as np

plt.figure()
# 定义从-pi到pi之间的数据,平均去64个数据点
x_data = np.linspace(-np.pi, np.pi, 64, endpoint=True)

# 将绘图区域分成两行三列
gs = gridspec.GridSpec(2, 3)
# 指定ax1占用第一行(0)整行
ax1 = plt.subplot(gs[0, :])
# 指定ax2占用第二行(1)的第一格(第二个参数0代表)
ax2 = plt.subplot(gs[1, 0])
# 指定ax3占用第二行(1)的第二、三格(第二个参数用1:3代表)
ax3 = plt.subplot(gs[1, 1:3])

# 绘制正弦曲线
ax1.plot(x_data, np.sin(x_data))
ax1.spines['right'].set_color('none')
ax1.spines['top'].set_color('none')
ax1.spines['bottom'].set_position(('data', 0))
ax1.spines['left'].set_position(('data', 0))
ax1.set_title('正弦曲线')

# 绘制余弦曲线
ax2.plot(x_data, np.cos(x_data))
ax2.spines['right'].set_color('none')
ax2.spines['top'].set_color('none')
ax2.spines['bottom'].set_position(('data', 0))
ax2.spines['left'].set_position(('data', 0))
ax2.set_title('余弦曲线')

# 绘制正切曲线
ax3.plot(x_data, np.tan(x_data))
ax3.spines['right'].set_color('none')
ax3.spines['top'].set_color('none')
ax3.spines['bottom'].set_position(('data', 0))
ax3.spines['left'].set_position(('data', 0))
ax3.set_title('正切曲线')

plt.show()

运行效果:
疯狂Python讲义学习笔记(含习题)之数据可视化_第11张图片

功能丰富的数据图

(一)饼图

使用Matplotlib提供的pie() 函数来绘制饼图。
示例数据表 TIOBE2019年6月编程语言指数排行榜前10名

语言 排名
Java 15.004%
C 13.300%
Python 8.530%
C++ 7.384%
Visual Basic.NET 4.624%
C# 4.483%
JavaScript 2.716%
PHP 2.567%
SQL 2.224%
Assembly language 1.479%

饼图示例

import matplotlib.pyplot as plt

# 准备数据
data = [
    0.15004, 0.13300, 0.08530, 0.07384, 0.04624, 0.04483, 0.02716, 0.02567,
    0.02224, 0.01479, 0.37689
]
# 准备标签
labels = [
    'Java', 'C', 'Python', 'C++', 'Visual Basic.NET', 'C#', 'JavaScript',
    'PHP', 'SQL', 'Assembly language', '其他'
]
# 将排在第3位的语言(Python)分离出来
explode = [0, 0, 0.3, 0, 0, 0, 0, 0, 0, 0, 0]
# 使用自定义颜色
colors = ['red', 'pink', 'magenta', 'purple', 'orange']
# 将横、纵坐标轴标准化处理,保证饼图是一个正圆,否则为椭圆
plt.axes(aspect='equal')
# 控制X轴和Y轴的范围(用于控制饼图的圆心、半径)
plt.xlim(0, 8)
plt.ylim(0, 8)

# 绘制饼图
plt.pie(x=data, # 绘图数据
        labels=labels, # 添加编程语言标签
        explode=explode, # 突出显示Python
        colors=colors, # 设置饼图的自定义填充色
        autopct='%.3f%%', # 设置百分比的格式,此处保留3位小数
        pctdistance=0.8, # 设置百分比标签与圆心的距离
        labeldistance=1.05, # 设置标签与圆心的距离
        startangle=180, # 设置饼图的初始角度
        center=(4, 4), # 设置饼图的圆心(相当于X轴和Y轴的范围)
        radius=3.8, # 设置饼图的半径(相当于X轴和Y轴的范围
        counterclock=False, # 是否为逆时针方向,这里为顺时针
        wedgeprops={
            'linewidth': 1,
            'edgecolor': 'green'
        }, # 设置饼图内外边界的属性值
        textprops={
            'fontsize': 12,
            'color': 'black'
        }, # 设置文本标签的属性值
        frame=1) # 是否显示饼图的圆圈,此处设置为显示
# 不显示X轴和Y轴的刻度值
plt.xticks(())
plt.yticks(())
# 添加图形标题
plt.title('TIOBE2019年6月编程语言指数排行榜前10名')
# 显示图形
plt.show()

运行效果:
疯狂Python讲义学习笔记(含习题)之数据可视化_第12张图片

(二)柱状图

使用Matplotlib提供的bar()函数来绘制柱状图。与前面介绍的plot()函数类似, 程序每次调用bar()函数时都会生成一组柱状图, 如果希望生成多组柱状图,则可通过多次调用bar()函数来实现。
示例

import matplotlib.pyplot as plt

# 构建数据
x_data = ['2011', '2012', '2013', '2014', '2015', '2016', '2017']
y_data = [58000, 60200, 63000, 71000, 84000, 90500, 107000]
y_data2 = [52000, 54200, 51500, 58300, 56800, 59500, 62700]
# 绘图
plt.bar(x=x_data, height=y_data, label='产品1', color='steelblue', alpha=0.8)
plt.bar(x=x_data, height=y_data2, label='产品2', color='indianred', alpha=0.8)
# 在柱状图上显示具体数值,ha参数控制水平对齐方式,va参数控制垂直对齐方式
for x, y in enumerate(y_data):
    plt.text(x, y + 100, '%s' % y, ha='center', va='bottom')
for x, y in enumerate(y_data2):
    plt.text(x, y + 100, '%s' % y, ha='center', va='top')
# 设置标题
plt.title("产品对比")
# 为两个坐标轴设置名称
plt.xlabel("年份")
plt.ylabel("销量")
# 显示图例
plt.legend()
plt.show()

运行效果:
疯狂Python讲义学习笔记(含习题)之数据可视化_第13张图片

使用bar()函数绘制柱状图时,默认不会在柱状图上显示具体的数值。程序可以通过调用text()函数在数据图上输出文字。text()函数前两个参数控制输出文字的X、Y坐标,第三个参数控制输出的内容,va控制文字的垂直对齐方式,ha控制文字的水平对齐方式。
如果不希望两组数据重叠在一起,那么就需要对X轴进行设置,因为两组柱状图使用的是同一组list列表数据,为了将多个柱状图的条柱并列显示,程序需要为这些柱状图重新计算不同的X轴数据。为了精确控制条柱的宽度,程序可以在调用bar()函数时传入width参数,这样可以更好地计算条柱的并列方式。
示例

import matplotlib.pyplot as plt
import numpy as np

# 构建数据
x_data = ['2011', '2012', '2013', '2014', '2015', '2016', '2017']
y_data = [58000, 60200, 63000, 71000, 84000, 90500, 107000]
y_data2 = [52000, 54200, 51500, 58300, 56800, 59500, 62700]
bar_width = 0.3
# 将X轴数据改为使用range(len(x_data)),就是0、1、2……
plt.bar(x=range(len(x_data)),
        height=y_data,
        label='产品1',
        color='steelblue',
        alpha=0.8,
        width=bar_width)
# 将X轴数据改为使用np.arange(len(x_data)) + bar_width
# 就是bar_width、1 + bar_width、2 + bar_width……这样就和第一个图并列了
plt.bar(x=np.arange(len(x_data)) + bar_width,
        height=y_data2,
        label='产品2',
        color='indianred',
        alpha=0.8,
        width=bar_width)
# 在柱状图上显示具体的数值,ha参数控制水平对齐方式,va参数控制垂直对齐方式
for x, y in enumerate(y_data):
    plt.text(x, y + 100, '%s' % y, ha='center', va='bottom')
for x, y in enumerate(y_data2):
    plt.text(x + bar_width, y + 100, '%s' % y, ha='center', va='top')
# 设置标题
plt.title('产品对比')
# 为两个坐标轴设置名称
plt.xlabel("年份")
plt.ylabel("销量")
# 显示图例
plt.legend()
plt.show()

运行效果:
疯狂Python讲义学习笔记(含习题)之数据可视化_第14张图片

以上程序有一个不足之处,就是X轴的刻度值变成了0,1,2……而不再显示年份,为了让柱状图的X轴刻度显示年份,可以调用xticks()函数重新设置X轴的刻度值。如:
plt.xticks(np.arange(len(x_data)) + bar_width/2, x_data)

(三)水平柱状图

调用Matplotlib的barh()函数可以生成水平柱状图。barh()函数的用法与bar()函数的用法基本一样,只是在调用barh()函数时使用y 参数传入Y 轴数据,使用width 参数传入代表条柱宽度的数据。
示例

import matplotlib.pyplot as plt
import numpy as np

# 构建数据
x_data = ['2011', '2012', '2013', '2014', '2015', '2016', '2017']
y_data = [58000, 60200, 63000, 71000, 84000, 90500, 107000]
y_data2 = [52000, 54200, 51500, 58300, 56800, 59500, 62700]
bar_width = 0.3
# Y轴数据使用range(len(x_data)),也就是0、1、2、……
plt.barh(y=range(len(x_data)),
         width=y_data,
         label='产品1',
         color='steelblue',
         alpha=0.8,
         height=bar_width)
# Y轴数据使用np.arange(len(x_data)) + bar_width,也就是bar_width、1+bar_width、2+bar_width……
plt.barh(y=np.arange(len(x_data)) + bar_width,
         width=y_data2,
         label='产品2',
         color='indianred',
         alpha=0.8,
         height=bar_width)
# 在柱状图上显示具体的数值,ha参数控制水平对齐方式,va参数控制垂直对齐方式
for y, x in enumerate(y_data):
    plt.text(x + 5000,
             y - bar_width / 2,
             '%s' % x,
             ha='center',
             va='bottom')
for y, x in enumerate(y_data2):
    plt.text(x + 5000,
             y + bar_width / 2,
             '%s' % x,
             ha='center',
             va='bottom')
# 设置Y轴刻度
plt.yticks(np.arange(len(x_data)) + bar_width / 2, x_data)
# 设置标题
plt.title("产品比较")
# 为两个坐标轴设置名称
plt.xlabel("销量")
plt.ylabel("年份")
# 显示图例
plt.legend()
plt.show()

运行效果
疯狂Python讲义学习笔记(含习题)之数据可视化_第15张图片

(四)散点图

实际上,散点图和折线图类似,区别是折线图会将各个点用线连接起来,而散点图不会。调用Matplotlib的scatter()函数可以绘制散点图,该函数接收如下参数:

  • x:指定X轴数据。
  • y:指定Y轴数据。
  • s:指定散点的大小。
  • c:指定散点的颜色。
  • alpha:指定散点的透明度。
  • linewidths:指定散点边框线的宽度。
  • edgecolors:指定散点边框的颜色。
  • marker:指定散点的图形样式。
  • cmap:指定散点的颜色映射,会使用不同的颜色来区分散点的值。

散点样式表

样式字符 样式
‘.’ 点标记
‘,’ 像素标记
‘o’ 圆形标记
‘v’ 向下三角形标记
‘^’ 向上三角形标记
‘<’ 向左三角形标记
‘>’ 向右三角形标记
‘1’ 向下三叉标记
‘2’ 向上三叉标记
‘3’ 向左三叉标记
‘4’ 向右三叉标记
‘s’ 正方形标记
‘p’ 五边形标记
‘*’ 星号标记
‘h’ 八边形标记
‘H’ 另一种八边形标记
‘+’ 加号标记
‘x’ x标记
‘D’ 菱形标记
‘d’ 尖菱形标记
‘|’ 竖线标记
‘_’ 横线标记

示例

import matplotlib.pyplot as plt
import numpy as np

plt.figure()
# 定义从-pi到pi之间的数据,平均取64个数据点
x_data = np.linspace(-np.pi, np.pi, 64, endpoint=True)
# 将整个figure分成两行两列,第三个参数表示将图形放在第1个网格中
# 沿着正弦曲线绘制散点图
plt.scatter(x_data,
            np.sin(x_data),
            c='purple',
            s=50,
            alpha=0.5,
            marker='p',
            linewidths=1,
            edgecolors=['green', 'yellow'])
# 绘制第二个散点图(只包含一个起点),突出起点
plt.scatter(x_data[0], np.sin(x_data)[0], c='red', s=150, alpha=1)
# 绘制第三个散点图(只包含一个结束点),突出结束点
plt.scatter(x_data[63], np.sin(x_data)[63], c='black', s=150, alpha=1)
plt.gca().spines['right'].set_color('none')
plt.gca().spines['top'].set_color('none')
plt.gca().spines['bottom'].set_position(('data', 0))
plt.gca().spines['left'].set_position(('data', 0))
plt.title('正弦曲线的散点图')
plt.show()

运行效果:
疯狂Python讲义学习笔记(含习题)之数据可视化_第16张图片

(五)等高线图

等高线图需要三维数据,其中X、Y轴数据决定坐标点,还需要对应的高度数据(相当于Z轴)来决定不同坐标点的高度。程序调用contour()函数绘制等高线,调用contourf()函数为等高线图填充颜色。
contour()、contourf()函数常用参数如下:

  • X:指定X轴数据
  • Y:指定Y轴数据
  • Z:指定X、Y坐标对应点的高度数据
  • colors:指定不同高度的等高线的颜色
  • alpha:指定等高线的透明度
  • cmap:指定登高线的颜色映射,即自动使用不同的颜色来区分不同的高度区域
  • linewidths:指定等高线的宽度
  • linestyles:指定等高线的样式

示例

import matplotlib.pyplot as plt
import numpy as np

delta = 0.025
# 生成代表x轴数据的列表
x = np.arange(-3.0, 3.0, delta)
# 生成代表y轴数据的列表
y = np.arange(-2.0, 2.0, delta)
# 对X、y数据进行网格化
X, Y = np.meshgrid(x, y)
Z1 = np.exp(-X**2 - Y**2)
Z2 = np.exp(-(X - 1)**2 - (Y - 1)**2)
# 计算Z轴数据(高度数据)
Z = (Z1 - Z2) * 2
# 为等高线图填充颜色,16指定将等高线分为几部分
plt.contourf(x, y, Z, 16, alpha=0.75, cmap='rainbow') # 使用颜色映射来区分不同高度的区域
# 绘制等高线
c = plt.contour(x, y, Z, 16, colors='black', linewidths=0.5)
# 绘制等高线数据
plt.clabel(c, inline=True, fontsize=10)
# 去除坐标轴
plt.xticks(())
plt.yticks(())
# 设置标题
plt.title('等高线图')
# 为两个坐标轴设置名称
plt.xlabel("纬度")
plt.ylabel("经度")
plt.show()

运行效果:
疯狂Python讲义学习笔记(含习题)之数据可视化_第17张图片

以上代码中要注意一个地方,很多书里都将c = plt.contour(x, y, Z, 16, colors='black', linewidths=0.5)中的linewidths写成了linewidth。

(六)3D图形

调用Axes3D对象的plot_surface()方法来绘制3D图形。
示例

import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.mplot3d import Axes3D

fig = plt.figure(figsize=(12, 8))
ax = Axes3D(fig)

delta = 0.125
# 生成代表X轴数据的列表
x = np.arange(-3.0, 3.0, delta)
# 生成代表Y轴数据的列表
y = np.arange(-2.0, 2.0, delta)
# 对x、y数据进行网格化
X, Y = np.meshgrid(x, y)
Z1 = np.exp(-X**2 - Y**2)
Z2 = np.exp(-(X - 1)**2 - (Y - 1)**2)
# 计算Z轴数据(高度数据)
Z = (Z1 - Z2) * 2
# 绘制3D图形
ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap=plt.get_cmap('rainbow'))
# 设置Z轴范围
ax.set_zlim(-2, 2)
# 设置标题
plt.title("3D图")
plt.show()

运行效果:
疯狂Python讲义学习笔记(含习题)之数据可视化_第18张图片

三、使用Pygal生成数据图

Pygal是另一个简单易用的数据图库,它以面向对象的方式来创建各种数据图,而且使用Pygal可以非常方便地生成各种格式的数据图,包括PNG 、SVG 等。使用Pygal也可以生成XML etree、HTML 表格(这些都需要安装其他包)。

(一)安装Pygal包

pip安装
pip install pygal

查看文档

python -m pydoc -p 8899

(二)Pygal数据图入门

使用Pygal生成数据图步骤如下:
① 创建Pygal数据图对象。有pygal.Bar,pygal.Pie,Pygal.Line等对象类型。
② 调用数据图对象的add()方法添加数据。
③ 调用Config对象的属性配置数据图。
④ 调用数据图对象的render_to_xxx()方法将数据图渲染到指定的输出节点——可以是PNG图片、SVG文件等。

示例

import pygal

x_data = ['2011', '2012', '2013', '2014', '2015', '2016', '2017']
# 定义两个列表分别作为两组柱状图的Y轴数据
y_data = [5800, 60200, 63000, 71000, 84000, 90500, 107000]
y_data2 = [52000, 54200, 51500, 58300, 56800, 59500, 62700]
# 创建pygal.Bar对象
bar = pygal.Bar()
# 添加两组代表条柱的数据
bar.add('产品1', y_data)
bar.add('产品2', y_data2)
# 设置X轴的刻度值
bar.x_labels = x_data
bar._title = '产品历年销售量'
# 设置X,Y轴的标题
bar._x_title = '年份'
bar._y_title = '销量'
# 指定将数据图输出到SVG文件中
bar.render_to_file('pygal_bar_test.svg')

运行后生成pygal_bar_test.svg文件,用浏览器打开效果如下:
疯狂Python讲义学习笔记(含习题)之数据可视化_第19张图片

(三)配置Pygal数据图

Pygal模块下有一个config模块,该模块包含了BaseConfig、CommonConfig、Config、SerieConfig等配置类。
部分配置属性示例

import pygal

x_data = ['2011', '2012', '2013', '2014', '2015', '2016', '2017']
# 定义两个列表分别作为两组柱状图的Y轴数据
y_data = [5800, 60200, 63000, 71000, 84000, 90500, 107000]
y_data2 = [52000, 54200, 51500, 58300, 56800, 59500, 62700]
# 创建pygal.Bar对象(柱状图)
bar = pygal.Bar()
# 添加两组代表条柱的数据
bar.add('产品1', y_data)
bar.add('产品2', y_data2)
# 设置X轴的刻度值
bar.x_labels = x_data
bar.title = '产品历年销量'
# 设置X、Y轴的标题
bar.x_title = '年份'
bar.y_title = '销量'
# 设置X轴的刻度值旋转45°
bar.x_label_rotation = 45
# 设置将图例放在底部
bar.legend_at_bottom = True
# 设置数据图四周的页边距
# 也可通过margin_bottom、margin_left、margin_right、margin_top只设置单独一边的页边距
bar.margin = 35
# 隐藏Y轴上的网络线
bar.show_y_guides = False
# 显示X轴上的网格线
bar.show_x_guides = True
# 指定将数据图输出到SVG文件中
bar.render_to_file('pygal_bar_config.svg')

pygal_bar_config.svg文件效果:
疯狂Python讲义学习笔记(含习题)之数据可视化_第20张图片

四、Pygal支持的常见数据图

(一)折线图

使用pygal.Line 类来表示折线图,程序创建pygal.Line对象就是创建折线图。
示例

import pygal

x_data = ['2011', '2012', '2013', '2014', '2015', '2016', '2017']
# 定义两个列表分别作为两组柱状图的Y轴数据
y_data = [5800, 60200, 63000, 71000, 84000, 90500, 107000]
y_data2 = [52000, 54200, 51500, 58300, 56800, 59500, 62700]
# 创建pygal.Line对象(折线图)
line = pygal.Line()
# 添加两组代表折线的数据
line.add('产品1', y_data)
line.add('产品2', y_data2)
# 设置X轴的刻度值
line.x_labels = x_data
# 重新设置Y轴的刻度值
line.y_labels = [20000, 40000, 60000, 80000, 100000]
line._title = '产品历年销量'
# 设置X,Y轴的标题
line.x_title = '年份'
line.y_title = '销量'
# 设置将图例放在底部
line.legend_at_bottom = True
# 指定将数据图输出到SVG文件中
line.render_to_file('pygal_line_test.svg')

SVG图形效果:
疯狂Python讲义学习笔记(含习题)之数据可视化_第21张图片

(二)水平柱状图和水平折线图

使用pygal.HorizontalBar类来表示水平柱状图。使用pygal.HorizontalBar生成水平柱状图的步骤与创建普通柱状图的步骤基本相同。
示例

import pygal

x_data = ['2011', '2012', '2013', '2014', '2015', '2016', '2017']
# 定义两个列表分别作为两组柱状图的Y轴数据
y_data = [5800, 60200, 63000, 71000, 84000, 90500, 107000]
y_data2 = [52000, 54200, 51500, 58300, 56800, 59500, 62700]
# 创建pygal.HorizontalBar对象
horizontal_bar = pygal.HorizontalBar()
# 添加两组数据
horizontal_bar.add('产品1', y_data)
horizontal_bar.add('产品2', y_data2)
# 设置Y轴的刻度值
horizontal_bar.x_labels = x_data
# 重新设置X轴的刻度值
horizontal_bar.y_labels = [20000, 40000, 60000, 80000, 100000]
horizontal_bar._title = '产品历年销量'
# 设置X、Y轴的标题
horizontal_bar.x_title = '销量'
horizontal_bar.y_title = '年份'
# 设置将图例放在底部
horizontal_bar.legend_at_bottom = True
# 指定将数据图输出到SVG文件中
horizontal_bar.render_to_file('pygal_horizontal_bar_test.svg')

SVG效果图:
疯狂Python讲义学习笔记(含习题)之数据可视化_第22张图片

与水平柱状图类似的还有水平折线图,水平折线图使用pygal.HorizontalLine 类来表示,水平折线图的X 轴刻度值同样使用y_labels属性来设置,而Y轴刻度值才使用x_labels属性来设置。

(三)叠加柱状图和叠加折线图

叠加柱状图使用pygal.StackedBar 类来表示,程序使用pygal.StackedBar 创建叠加柱状图的步骤与创建普通柱状图的步骤基本相同。
示例

import pygal

x_data = ['2011', '2012', '2013', '2014', '2015', '2016', '2017']
# 定义两个列表分别作为两组柱状图的Y轴数据
y_data = [5800, 60200, 63000, 71000, 84000, 90500, 107000]
y_data2 = [52000, 54200, 51500, 58300, 56800, 59500, 62700]
# 创建pygal.StackedBar对象
stacked_bar = pygal.StackedBar()
# 添加两组数据
stacked_bar.add('产品1', y_data)
stacked_bar.add('产品2', y_data2)
# 设置X轴的刻度值
stacked_bar.x_labels = x_data
# 重新设置Y轴的刻度值
stacked_bar.y_labels = [20000, 40000, 60000, 80000, 100000]
stacked_bar.title = '产品历年销量'
# 设置X、Y轴的标题
stacked_bar.y_title = '年份'
stacked_bar.x_title = '销量'
# 设置将图例放在底部
stacked_bar.legend_at_bottom = True
# 指定将数据图输出到SVG文件中
stacked_bar.render_to_file('pygal_stacked_bar_test.svg')

SVG效果图:
疯狂Python讲义学习笔记(含习题)之数据可视化_第23张图片

(四)饼图

Pygal提供了pygal.Pie 类来支持饼图,支持如下两个特有的属性。

  • inner_radius:设置饼图内圈的半径。通过设置该属性可以实现环形数据图。
  • half_pie:将该属性设置为True,可以实现半圆的饼图。
    示例
import pygal

# 准备数据
data = [
    0.16881, 0.14966, 0.07471, 0.06992, 0.04762, 0.03541, 0.02925, 0.02411,
    0.02316, 0.01409, 0.36326
]
# 准备标签
labels = [
    'Java', 'C', 'C++', 'Python', 'Visual Basic.NET', 'c#', 'PHP',
    'JavaScript', 'SQL', 'Assembly langugage', '其它'
]
# 创建pygal.Pie对象
pie = pygal.Pie()
# 采用循环为饼图添加数据
for i, per in enumerate(data):
    pie.add(labels[i], per)
pie.title = '2018年8月编程语言排行'
# 设置将图例放在底部
pie.legend_at_bottom = True
# 设置内圈的半径长度
pie.inner_radius = 0.4
# 创建班员数据图
pie.half_pie = True
# 指定将数据图输出到SVG文件中
pie.render_to_file('language_percent.svg')

SVG效果图:
疯狂Python讲义学习笔记(含习题)之数据可视化_第24张图片

(五)点图

点图使用点( 圆) 的大小来表示数值的大小。Pygal 使用pygal.Dot 类表示点图,创建点图的方式与创建柱状图的方式基本相同。
示例

import pygal

x_data = ['2011', '2012', '2013', '2014', '2015', '2016', '2017']
# 定义两个列表分别作为两组柱状图的Y轴数据
y_data = [5800, 60200, 63000, 71000, 84000, 90500, 107000]
y_data2 = [52000, 54200, 51500, 58300, 56800, 59500, 62700]
# 创建pygal.Dot对象
dot = pygal.Dot()
dot.dots_size = 5
# 添加两组数据
dot.add('产品1', y_data)
dot.add('产品2', y_data2)
# 设置X轴的刻度值
dot.x_labels = x_data
# 重新设置Y轴的刻度值
dot.y_labels = ['产品1', '产品2']
# 设置Y轴刻度值的旋转角度
dot.y_label_rotation = 45
dot.title = '产品历年销量'
# 设置X轴的标题
dot.x_title = '年份'
# 设置将图例放在底部
dot.legend_at_bottom = True
# 指定将数据图输出的SVG文件中
dot.render_to_file('pygal_dot_test.svg')

SVG效果图:
疯狂Python讲义学习笔记(含习题)之数据可视化_第25张图片

(六)仪表(Gauge)图

仪表图类似于一个仪表盘,在仪表盘内使用不同的指针代表不同的数据。Pygal使用pygal.Gauge类表示仪表图。程序在创建pygal.Gauge对象之后,为pygal.Gauge 对象添加数据的方式与为pygal.Pie对象添加数据的方式相似。pygal.Gauge对象有一个特别的属性:range ,该属性用于指定仪表图的最小值和最大值。
示例

import pygal

# 准备数据
data = [
    0.16881, 0.14966, 0.07471, 0.06992, 0.04762, 0.03541, 0.02925, 0.02411,
    0.02316, 0.01409, 0.36326
]
# 准备标签
labels = [
    'Java', 'C', 'C++', 'Python', 'Visual Basic.NET', 'c#', 'PHP',
    'JavaScript', 'SQL', 'Assembly langugage', '其它'
]
# 创建pygal.Gauge对象
gauge = pygal.Gauge()
gauge.range = [0, 1]
# 采用循环为仪表图添加数据
for i, per in enumerate(data):
    gauge.add(labels[i], per)
gauge.title = '2018年8月编程语言'
# 设置将图例放在底部
gauge.legend_at_bottom = True
# 指定将数据图输出到SVG文件中
gauge.render_to_file('pygal_gauge_test.svg')

SVG效果图:
疯狂Python讲义学习笔记(含习题)之数据可视化_第26张图片

(七)雷达图

雷达图适合用于分析各对象在不同维度的优势和劣势,通过雷达图可对比每个对象在不同维度的得分。
假如我们从下表所示的5个方面(平台健壮性、语法易用性、社区活跃度、市场份额和未来趋势)的得分来评价各编程语言的优势。

平台健壮性 语法易用性 社区活跃度 市场份额 未来趋势
Java 5 4.0 5 5 5
C 4.8 2.8 4.8 4.8 4.9
C++ 4.5 2.9 4.6 4.0 4.9
Python 4.0 4.8 4.9 4.0 5
C# 3.0 4.2 2.3 3.5 2
PHP 4.8 4.3 3.9 3.0 4.5

雷达图示例

import pygal

# 准备数据
data = [[5, 4.0, 5, 5, 5], [4.8, 2.8, 4.8, 4.8, 4.9],
        [4.5, 2.9, 4.6, 4.0, 4.9], [4.0, 4.8, 4.9, 4.0, 5],
        [3.0, 4.2, 2.3, 3.5, 2], [4.8, 4.3, 3.9, 3.0, 4.5]]
# 准备标签
labels = ['Java', 'C', 'C++', 'Python', 'C#', 'PHP']
# 创建pygal.Radar对象
rader = pygal.Radar()
# 采用循环为雷达图添加数据
for i, per in enumerate(labels):
    rader.add(labels[i], data[i])
rader.x_labels = ['平台健壮性', '语法易用性', '社区活跃度', '市场份额', '未来趋势']
rader.title = '编程语言对比图'
# 控制各得分点的大小
rader.dots_size = 8
# 设置将图例放在底部
rader.legend_at_bottom = True
# 指定将数据图输出的SVG文件中
rader.render_to_file('language_compare.svg')

SVG效果图:
疯狂Python讲义学习笔记(含习题)之数据可视化_第27张图片

五、处理数据

(一)CSV文件格式

csv文件格式的本质是一种以文本存储的表格数据(使用Excel工具即可读写csv 文件) 。csv文件的每行代表一行数据,每行数据中每个单元格内的数据以逗号隔开。
Python提供了csv模块来读写csv文件。由于csv 文件的格式本身比较简单( 通常第一行是表头,用于说明每列数据的含义, 接下来每行代表一行数据) , 因此使用csv模块读取csv文件也非常简单。
① 创建CSV模块的读取器
② 循环调用CSV读取器的next()方法逐行读取CSV文件内容即可。next()方法返回一个list列表代表一行数据,list列表的每个元素代表一个单元格数据。
CSV数据读取示例

import csv

filename = 'guangzhou-2017.csv'
# 打开文件
with open(filename) as f:
    # 创建CSV读取器
    reader = csv.reader(f)
    # 读取第一行,这行是表头数据
    header_row = next(reader)
    print(header_row)
    # 读取第二行,这行是真正的数据
    first_row = next(reader)
    print(first_row)

使用Matplotlib结合CSV文件读取展示图表示例

import csv
from datetime import datetime
from matplotlib import pyplot as plt

filename = 'guangzhou-2017.csv'
# 打开文件
with open(filename) as f:
    # 创建CSV读取器
    reader = csv.reader(f)
    # 读取第一行,这行是表头数据
    header_row = next(reader)
    print(header_row)
    # 定义读取起始日期
    start_date = datetime(2017, 6, 30)
    # 定义读取结束日期
    end_date = datetime(2017, 8, 1)
    # 定义三个list列表作为展示的数据
    dates, highs, lows = [], [], []
    for row in reader:
        # 将第一列的值格式化为日期
        d = datetime.strptime(row[0], '%Y-%m-%d')
        # 只展示2017年7月的数据
        if start_date < d < end_date:
            dates.append(d)
            highs.append(int(row[1]))
            lows.append(int(row[2]))
# 配置图形
fig = plt.figure(dpi=128, figsize=(12, 9))
# 绘制最高气温的折线
plt.plot(dates,
         highs,
         c='red',
         label='最高气温',
         alpha=0.5,
         linewidth=2.0,
         linestyle='-',
         marker='v')
# 绘制最低气温的折线
plt.plot(dates,
         lows,
         c='blue',
         label='最低气温',
         alpha=0.5,
         linewidth=3.0,
         linestyle='-.',
         marker='o')
# 为两个数据的绘图区填充颜色
plt.fill_between(dates, highs, lows, facecolor='blue', alpha=0.1)
# 设置标题
plt.title("2017年7月广州最高气温和最低气温")
# 为两个坐标轴设置名称
plt.xlabel("日期")
# 该方法绘制歇着的日期标签
fig.autofmt_xdate()
plt.ylabel("气温(℃)")
# 显示图例
plt.legend()
ax = plt.gca()
# 设置右边坐标轴线的颜色
ax.spines['right'].set_color('none')
# 设置顶部坐标轴线的颜色
ax.spines['top'].set_color('none')
plt.show()

运行效果:
疯狂Python讲义学习笔记(含习题)之数据可视化_第28张图片
使用Pygal结合CSV读取展示数据图示例

import csv
import pygal

filename = 'guangzhou-2017.csv'
# 打开文件
with open(filename) as f:
    # 创建CSV读取器
    reader = csv.reader(f)
    # 读取第一行数据的表头信息
    header_row = next(reader)
    print(header_row)
    # 准备展示的数据
    shades, sunys, cloudys, rainys = 0, 0, 0, 0
    for row in reader:
        if '阴' in row[3]:
            shades += 1
        elif '晴' in row[3]:
            sunys += 1
        elif '云' in row[3]:
            cloudys += 1
        elif '雨' in row[3]:
            rainys += 1
        else:
            print(row[3])
# 创建pygal.pei对象
pie = pygal.Pie()
# 为饼图添加数据
pie.add("阴", shades)
pie.add("晴", sunys)
pie.add("多云", cloudys)
pie.add("雨", rainys)
pie.title = '2017年广州天气汇总'
# 设置图例放在底部
pie.legend_at_bottom = True
# 指定将数据图输出到SVG文件中
pie.render_to_file('pygal_guangzhou_weather.svg')

SVG效果:
疯狂Python讲义学习笔记(含习题)之数据可视化_第29张图片

(二)JSON数据

示例1

import json
from matplotlib import pyplot as plt
import numpy as np

filename = 'gdp_json.json'
# 读取JSON格式的GDP数据
with open(filename) as f:
    gdp_list = json.load(f)
# 使用list列表依次保存中国、美国、日本、俄罗斯、加拿大的GDP值
country_gdps = [{}, {}, {}, {}, {}]
country_codes = ['CHN', 'USA', 'JPN', 'RUS', 'CAN']
# 遍历列表的每个元素,每个元素都是一个GDP数据项
for gdp_dict in gdp_list:
    for i, country_code in enumerate(country_codes):
        # 只读取指定国家的数据
        if gdp_dict['Country Code'] == country_code:
            year = gdp_dict['Year']
            # 值读取从2001年到2016年的数据
            if 2017 > year > 2000:
                country_gdps[i][year] = gdp_dict['Value']
country_gdp_list = [[], [], [], [], []]
# 构建时间数据
x_data = range(2001, 2017)
for i in range(len(country_gdp_list)):
    for year in x_data:
        # 除以1e8,让数值变成以亿为单位
        country_gdp_list[i].append(country_gdps[i][year] / 1e8)
bar_width = 0.15
fig = plt.figure(dpi=128, figsize=(15, 9))
colors = ['indianred', 'steelblue', 'gold', 'lightpink', 'seagreen']
# 定义国家名称列表
countries = ['中国', '美国', '日本', '俄罗斯', '加拿大']
# 采用循环绘制5组柱状图
for i in range(len(colors)):
    # 使用自定义的X坐标将数据分开
    plt.bar(x=np.arange(len(x_data)) + bar_width * i,
            height=country_gdp_list[i],
            label=countries[i],
            color=colors[i],
            alpha=0.8,
            width=bar_width)
    # 仅在中国、美国的条柱上绘制GDP值
    if i < 2:
        for x, y in enumerate(country_gdp_list[i]):
            plt.text(x, y + 100, '%.0f' % y, ha='center', va='bottom')
# 为X轴设置刻度值
plt.xticks(np.arange(len(x_data)) + bar_width * 2, x_data)
# 设置标题
plt.title("从2001年到2016年各国GDP对比")
# 为两个坐标轴设置名称
plt.xlabel("年份")
plt.ylabel("GDP(亿美元)")
# 显示图例
plt.legend()
plt.show()

运行效果:
疯狂Python讲义学习笔记(含习题)之数据可视化_第30张图片

(三)数据清洗

当程序使用Python 进行数据展示时,经常发现数据存在以下两种情况。

  • 数据丢失
  • 数据格式错误
    对于数据丢失的情况,程序应i亥生成报告:对于数据格式发生错误的情况, 程序应该能略过发生错误的数据,继续处理后面的程序,并报告发生错误的数据。

(四)读取网络数据

示例 爬取并展示http://lishi.tianqi.com站点的数据

import re
from datetime import datetime
from datetime import timedelta
from matplotlib import pyplot as plt
from urllib.request import *


# 定义一个函数读取http://lishi.tianqi.com站点的数据
def get_html(city, year, month):
    url = 'http://lishi.tianqi.com/' + city + '/' + str(year) + str(
        month) + '.html'
    # 创建请求
    request = Request(url)
    # 添加请求头
    request.add_header(
        'User-Agent', 'Mozilla/5.0 (Windows NT 10.0; WOW64)' +
        'AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36'
    )
    response = urlopen(request)
    # 获取服务器响应
    return response.read().decode('utf-8')


# 定义三个list列表作为展示的数据
dates, highs, lows = [], [], []
city = 'chongqing'
year = '2018'
months = [
    '01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12'
]
prev_day = datetime(2017, 12, 31)
# 循环读取每个月的天气数据
for month in months:
    html = get_html(city, year, month)
    # 将HTML响应拼接起来
    text = "".join(html.split())
    # 定义包含天气信息的div的正则表达式
    patten = re.compile(r'(.*?)')
    table = re.findall(patten, text)
    patten1 = re.compile(r'(.*?)')
    uls = re.findall(patten1, table[0])
    # 去除最后一个包含“查看更多”的
  • uls = uls[:-1] for ul in uls: # 定义解析天气信息的正则表达式 patten2 = re.compile(r'(.*?)
  • ') lis = re.findall(patten2, ul) print(lis) # 解析得到日期数据 d_str = re.findall(r'">(.*?)', lis[0])[0] try: # 将日期字符串格式化为日期 cur_day = datetime.strptime(d_str, '%Y-%m-%d') # 解析得到最高气温和最低气温 high = int(lis[1]) low = int(lis[2]) except ValueError: print(cur_day, '数据出现错误') else: # 计算前、后两天数据的时间差 diff = cur_day - prev_day # 如果前、后两天数据的时间差不是相差一天,则说明数据有问题 if diff != timedelta(days=1): print('%s之间少了%d天的数据' % (cur_day, diff.days - 1)) dates.append(cur_day) highs.append(high) lows.append(low) prev_day = cur_day # 配置图形 fig = plt.figure(dpi=128, figsize=(12, 9)) # 绘制最高气温的折线 plt.plot(dates, highs, c='red', label='最高气温', alpha=0.5, linewidth=2.0) # 绘制最低气温的折线 plt.plot(dates, lows, c='blue', label='最低气温', alpha=0.5, linewidth=2.0) # 为两个数据的绘图区域填充颜色 plt.fill_between(dates, highs, lows, facecolor='blue', alpha=0.1) # 设置标题 plt.title("%s年重庆最高气温和最低气温" % year) # 为两个坐标轴设置名称 plt.xlabel("日期") # 该方法绘制斜着的日期标签 fig.autofmt_xdate() plt.ylabel("气温(℃)") # 显示图例 plt.legend() ax = plt.gca() # 设置右边坐标线不显示 ax.spines['right'].set_color('none') # 设置顶部坐标线不显示 ax.spines['top'].set_color('none') plt.show()

    运行效果:
    疯狂Python讲义学习笔记(含习题)之数据可视化_第31张图片

    练习

    1.使用Matplotlib生成折线图,分析自己在两个月内体重变化与运动时间之间的关系。

    import matplotlib.pyplot as plt
    
    # 定义两个列表分别作为X轴、Y轴数据
    # Y轴代表运动时间,X轴代表体重
    y_data = [
        '60分钟', '30分钟', '45分钟', '60分钟', '30分钟', '45分钟', '60分钟', '30分钟', '45分钟',
        '60分钟', '30分钟', '45分钟', '60分钟', '30分钟', '45分钟', '60分钟', '30分钟', '45分钟',
        '60分钟', '30分钟', '45分钟', '60分钟', '30分钟', '45分钟', '60分钟', '30分钟', '45分钟',
        '60分钟', '30分钟', '45分钟'
    ]
    x_data = [
        68, 67.5, 67, 66, 66.5, 66, 65.5, 65, 64.5, 64, 64.5, 64, 63.5, 63, 64, 63,
        62.5, 62, 61.5, 61, 61.5, 60, 59.5, 59, 58.5, 58, 57.5, 57, 56.5, 56
    ]
    # 第一个列表代表横坐标的值,第二个列表代表纵坐标的值
    plt.plot(x_data, y_data)
    # 调用show()函数显示图形
    plt.show()
    
    

    2.使用Pygal生成饼图,分析本年度自己在生活、教育学习、健身、旅游、娱乐各方面的时间投入和金钱投入。

    import pygal
    
    # 准备数据
    data = [191400, 50000, 1000, 30000, 110000]
    data1 = [365, 100, 125, 30, 200]
    # 准备标签
    labels = ['生活', '教育学习', '健身', '旅游', '娱乐']
    # 创建pygal.Pie对象
    pie = pygal.Pie()
    # 采用循环为饼图添加数据
    for i, per in enumerate(data):
        pie.add(labels[i], per)
    pie.title = '2019年消费投入图'
    # 设置将图例放在底部
    pie.legend_at_bottom = True
    # 设置内圈的半径长度
    pie.inner_radius = 0.4
    # 指定将数据图输出到SVG文件中
    pie.render_to_file('my.svg')
    
    # 创建pygal.Pie对象
    pie1 = pygal.Pie()
    # 采用循环为饼图添加数据
    for i, per in enumerate(data1):
        pie1.add(labels[i], per)
    pie1.title = '2019年时间投入图'
    # 设置将图例放在底部
    pie1.legend_at_bottom = True
    # 设置内圈的半径长度
    pie1.inner_radius = 0.5
    # 指定将数据图输出到SVG文件中
    pie.render_to_file('my1.svg')
    
    

    3.使用随机数生成5000 个(-3, -3 )~(3,3 )范围的点,并使用散点图绘制它们。

    import matplotlib.pyplot as plt
    import random
    
    plt.figure()
    x_data, y_data = [], []
    for i in range(5000):
        x_data.append(random.uniform(-3, 3))
    for i in range(5000):
        y_data.append(random.uniform(-3, 3))
    plt.rcParams['font.sans-serif'] = ['SimHei']
    plt.scatter(x_data,
                y_data,
                c='purple',
                s=50,
                alpha=0.5,
                marker='p',
                linewidths=1,
                edgecolors=['green', 'yellow'])
    # 绘制第二个散点图(只包含一个起点),突出起点
    plt.scatter(x_data[0], y_data[0], c='red', s=150, alpha=1)
    # 绘制第三个散点图(只包含一个结束点),突出结束点
    plt.scatter(x_data[4999], y_data[4999], c='black', s=150, alpha=1)
    plt.gca().spines['right'].set_color('none')
    plt.gca().spines['top'].set_color('none')
    plt.gca().spines['bottom'].set_position(('data', 0))
    plt.gca().spines['left'].set_position(('data', 0))
    plt.title('随机数的散点图')
    plt.show()
    
    

    4.从https://www.tiobe.com/tiobe-index/网站查找2018年各月Python和Java两门语言的市场份额,并绘制它们的柱状图进行对比。

    import csv, pygal
    
    filename = 'exercise\language.csv'
    x_data = ['0' + str(i + 1) for i in range(12)]
    java_data = [0] * 12
    python_data = [0] * 12
    # 打开文件
    with open(filename) as f:
        reader = csv.reader(f)
        header_row = next(reader)
        for row in reader:
            # Java的数据
            if row[0] == 'java':
                java_data[int(row[2][5:]) - 1] = float(row[1])
            # Python的数据
            elif row[0] == 'python':
                python_data[int(row[2][5:]) - 1] = float(row[1])
    bar = pygal.Bar()
    bar.add('Java', java_data)
    bar.add('python', python_data)
    bar.x_labels = x_data
    bar.title = '2018年各月Java与Python市场份额对比图'
    bar.x_title = '月份'
    bar.y_title = '份额'
    bar.legend_at_bottom = True
    bar.render_to_file('language.svg')
    
    

    5.从https://datahub.io网站下载世界各国的人口数据,并绘制中国、印度历年人口变化的折线图。

    import json
    import pygal
    
    pop_filename = 'exercise\\population-figures-by-country.json'
    # 读取JSON格式的人口数据
    with open(pop_filename) as f:
        pop_list = json.load(f)
    
    # 构建时间数据
    x_data = range(1970, 2017)
    # 使用list列表依次保存中国、印度的人口
    country_pops_list = [[], []]
    for pop_dict in pop_list:
        # 获取中国的人口数据
        if pop_dict['Country_Code'] == 'CHN':
            for year in x_data:
                country_pops_list[0].append(pop_dict['Population_in_%d' % year] /
                                            10000)
        # 获取印度的人口数据
        if pop_dict['Country_Code'] == 'IND':
            for year in x_data:
                country_pops_list[1].append(pop_dict['Population_in_%d' % year] /
                                            10000)
    # 定义国家名称列表
    countries = ['中国', '印度']
    # 创建pygal.Bar对象
    bar = pygal.Bar()
    # 采用循环添加代表条柱的数据
    for i in range(len(countries)):
        bar.add(countries[i], country_pops_list[i])
    bar.width = 1100
    # 设置X轴的刻度值
    bar.x_labels = x_data
    bar.title = '1970-2016年中国印度人口对比'
    bar.x_title = '年份'
    bar.y_title = '人口(万)'
    # 设置X轴的刻度值旋转45度
    bar.x_label_rotation = 45
    # 设置将图例放在底部
    bar.legend_at_bottom = True
    # 将数据图输出到文件
    bar.render_to_file('population.svg')
    
    

    6.从http://lishi.tianqi.com网站读取深圳的2017年历史天气数据,并绘制最高气温、最低气温的折线图。

    import re
    from datetime import datetime
    from datetime import timedelta
    from matplotlib import pyplot as plt
    from urllib.request import *
    
    
    # 定义一个函数读取http://lishi.tianqi.com站点的数据
    def get_html(city, year, month):
        url = 'http://lishi.tianqi.com/' + city + '/' + str(year) + str(
            month) + '.html'
        # 创建请求
        request = Request(url)
        # 添加请求头
        request.add_header(
            'User-Agent', 'Mozilla/5.0 (Windows NT 10.0; WOW64)' +
            'AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36'
        )
        response = urlopen(request)
        # 获取服务器响应
        return response.read().decode('utf-8')
    
    
    # 定义三个list列表作为展示的数据
    dates, highs, lows = [], [], []
    city = 'shenzhen'
    year = '2017'
    months = [
        '01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12'
    ]
    prev_day = datetime(2016, 12, 31)
    # 循环读取每个月的天气数据
    for month in months:
        html = get_html(city, year, month)
        # 将HTML响应拼接起来
        text = "".join(html.split())
        # 定义包含天气信息的div的正则表达式
        patten = re.compile(r'(.*?)')
        table = re.findall(patten, text)
        patten1 = re.compile(r'(.*?)')
        uls = re.findall(patten1, table[0])
        # 去除最后一个包含“查看更多”的
  • uls = uls[:-1] for ul in uls: # 定义解析天气信息的正则表达式 patten2 = re.compile(r'(.*?)
  • ') lis = re.findall(patten2, ul) print(lis) # 解析得到日期数据 d_str = re.findall(r'">(.*?)', lis[0])[0] try: # 将日期字符串格式化为日期 cur_day = datetime.strptime(d_str, '%Y-%m-%d') # 解析得到最高气温和最低气温 high = int(lis[1]) low = int(lis[2]) except ValueError: print(cur_day, '数据出现错误') else: # 计算前、后两天数据的时间差 diff = cur_day - prev_day # 如果前、后两天数据的时间差不是相差一天,则说明数据有问题 if diff != timedelta(days=1): print('%s之间少了%d天的数据' % (cur_day, diff.days - 1)) dates.append(cur_day) highs.append(high) lows.append(low) prev_day = cur_day # 配置图形 fig = plt.figure(dpi=128, figsize=(12, 9)) # 绘制最高气温的折线 plt.plot(dates, highs, c='red', label='最高气温', alpha=0.5, linewidth=2.0) # 绘制最低气温的折线 plt.plot(dates, lows, c='blue', label='最低气温', alpha=0.5, linewidth=2.0) # 为两个数据的绘图区域填充颜色 plt.fill_between(dates, highs, lows, facecolor='blue', alpha=0.1) # 设置标题 plt.title("%s年重庆最高气温和最低气温" % year) # 为两个坐标轴设置名称 plt.xlabel("日期") # 该方法绘制斜着的日期标签 fig.autofmt_xdate() plt.ylabel("气温(℃)") # 显示图例 plt.legend() ax = plt.gca() # 设置右边坐标线不显示 ax.spines['right'].set_color('none') # 设置顶部坐标线不显示 ax.spines['top'].set_color('none') plt.show()

    你可能感兴趣的:(Python3学习)