Python数据可视化(Pygal、svg、csv、Bar、Line)
将使用Python可视化包Pygal来生成可缩放的矢量图形文件
pygal官方文档:[www.pygal.org/en/stable/](http://www.pygal.org/en/stable/)
1、安装pygal
pip install pygal -i https://pypi.tuna.tsinghua.edu.cn/simple
2、简单的python图表
import pygal pygal.Bar()(1, 3, 3, 7)(1, 6, 6, 4).render()
生成svg图表
pygal.Bar()(1, 3, 3, 7)(1, 6, 6, 4).render_to_file("simple.svg")
需要查看它的源文件,才能显示图片。
3、制作多系列图标(Bar)
import pygal # pygal.Bar()(1, 3, 3, 7)(1, 6, 6, 4)(5,7,8,13)(5,7,4,9).render_to_file("xgp.svg") py_bar = pygal.Bar() py_bar.add("大标题",[1, 3, 3, 7]) py_bar.add("小标题",[1, 6, 6, 4]) py_bar.render_to_file("wsd.svg")
4、堆叠图表(StackedBar)
import pygal # pygal.Bar()(1, 3, 3, 7)(1, 6, 6, 4)(5,7,8,13)(5,7,4,9).render_to_file("xgp.svg") py_bar = pygal.StackedBar() py_bar.add("大标题",[1, 3, 3, 7]) py_bar.add("小标题",[1, 6, 6, 4]) py_bar.render_to_file("wsd.svg")
5、将上面的图表水平(HorizontalStackedBar)
import pygal # pygal.Bar()(1, 3, 3, 7)(1, 6, 6, 4)(5,7,8,13)(5,7,4,9).render_to_file("xgp.svg") py_bar = pygal.HorizontalStackedBar() py_bar.add("大标题",[1, 3, 3, 7]) py_bar.add("小标题",[1, 6, 6, 4]) py_bar.render_to_file("wsd.svg")
二、pygal(各种图表类型)
1、基本的简单线形图(Line)
import pygal # pygal.Bar()(1, 3, 3, 7)(1, 6, 6, 4)(5,7,8,13)(5,7,4,9).render_to_file("xgp.svg") py_bar = pygal.Line() py_bar.add("大标题",[1, 3, 3, 7]) py_bar.add("小标题",[1, 6, 6, 4]) py_bar.render_to_file("wsd.svg")
2、Horizontal Line
相同的图形但水平,范围为0-100。
import pygal # pygal.Bar()(1, 3, 3, 7)(1, 6, 6, 4)(5,7,8,13)(5,7,4,9).render_to_file("xgp.svg") py_bar = pygal.HorizontalLine() py_bar.add("大标题",[1, 3, 3, 7]) py_bar.add("小标题",[1, 6, 6, 4]) py_bar.range = [0, 10] py_bar.render_to_file("wsd.svg")
3、Stacked
相同的图形但具有堆叠值和填充渲染
import pygal # pygal.Bar()(1, 3, 3, 7)(1, 6, 6, 4)(5,7,8,13)(5,7,4,9).render_to_file("xgp.svg") py_bar = pygal.StackedLine(fill=True) py_bar.add("大标题",[1, 3, 3, 7]) py_bar.add("小标题",[1, 6, 6, 4]) py_bar.range = [0, 10] py_bar.render_to_file("wsd.svg")
4、Time
对于与时间相关的图,只需格式化标签或使用xy图表的一种变体
import pygal from datetime import datetime # x_label_rotation=20是指x轴标签右旋转20度,可负数,负数向左旋转 date_chart = pygal.Line(x_label_rotation=-20) date_chart.x_labels = map(lambda d: d.strftime(‘%Y-%m-%d‘), [ datetime(2013, 1, 2), datetime(2013, 1, 12), datetime(2013, 2, 2), datetime(2013, 2, 22)]) date_chart.add("Visits", [300, 412, 823, 672]) date_chart.render_to_file("line-time.svg")
Lambda是一个表达式,也可以是一个匿名函数
def sum(x, y): return x + y
在Lambda中可以这样写
p = lambda x, y: x + y
5、Histogram
Basic
直方图是特殊条形,它为条形图取3个值:纵坐标高度,横坐标开始和横坐标结束。
import pygal hist = pygal.Histogram() hist.add(‘Wide bars‘, [(5, 0, 10), (4, 5, 13), (2, 0, 15)]) hist.add(‘Narrow bars‘, [(10, 1, 2), (12, 4, 4.5), (8, 11, 13)]) hist.render_to_file("histogram-basic.svg")
6、Scatter Plot
禁用点和点之间的连线而获得散点图
import pygal from math import cos xy_chart = pygal.XY() xy_chart.title = ‘XY Cosinus‘ xy_chart.add(‘x = cos(y)‘, [(cos(x / 10.), x / 10.) for x in range(-50, 50, 5)]) xy_chart.add(‘y = cos(x)‘, [(x / 10., cos(x / 10.)) for x in range(-50, 50, 5)]) xy_chart.add(‘x = 1‘, [(1, -5), (1, 5)]) xy_chart.add(‘x = -1‘, [(-1, -5), (-1, 5)]) xy_chart.add(‘y = 1‘, [(-5, 1), (5, 1)]) xy_chart.add(‘y = -1‘, [(-5, -1), (5, -1)]) xy_chart.render_to_file("xy-basic.svg")
7、Pie
简单的饼图
import pygal pie_chart = pygal.Pie() pie_chart.title = ‘Browser usage in February 2012 (in %)‘ pie_chart.add(‘IE‘, 19.5) pie_chart.add(‘Firefox‘, 36.6) pie_chart.add(‘Chrome‘, 36.3) pie_chart.add(‘Safari‘, 4.5) pie_chart.add(‘Opera‘, 2.3) pie_chart.render_to_file("pie-basic.svg")
8、Multi-series pie
相同的饼图,但分为子类别
import pygal pie_chart = pygal.Pie() pie_chart.title = ‘Browser usage by version in February 2012 (in %)‘ pie_chart.add(‘IE‘, [5.7, 10.2, 2.6, 1]) pie_chart.add(‘Firefox‘, [.6, 16.8, 7.4, 2.2, 1.2, 1, 1, 1.1, 4.3, 1]) pie_chart.add(‘Chrome‘, [.3, .9, 17.1, 15.3, .6, .5, 1.6]) pie_chart.add(‘Safari‘, [4.4, .1]) pie_chart.add(‘Opera‘, [.1, 1.6, .1, .5]) pie_chart.render_to_file("pie-multi-series.svg")
9、Radar
简单的Kiviat图
import pygal # pygal.Bar()(1, 3, 3, 7)(1, 6, 6, 4)(5,7,8,13)(5,7,4,9).render_to_file("xgp.svg") py_bar = pygal.Radar() py_bar.add("大标题",[1, 3, 3, 7]) py_bar.add("小标题",[1, 6, 6, 4]) py_bar.range = [0, 10] py_bar.render_to_file("wsd.svg")
10、Box
Extremes (default)
import pygal box_plot = pygal.Box() box_plot.title = ‘V8 benchmark results‘ box_plot.add(‘Chrome‘, [6395, 8212, 7520, 7218, 12464, 1660, 2123, 8607]) box_plot.add(‘Firefox‘, [7473, 8099, 11700, 2651, 6361, 1044, 3797, 9450]) box_plot.add(‘Opera‘, [3472, 2933, 4203, 5229, 5810, 1828, 9013, 4669]) box_plot.add(‘IE‘, [43, 41, 59, 79, 144, 136, 34, 102]) box_plot.render_to_file("box-extremes.svg")
11、Dot
import pygal # pygal.Bar()(1, 3, 3, 7)(1, 6, 6, 4)(5,7,8,13)(5,7,4,9).render_to_file("xgp.svg") py_bar = pygal.Dot(x_label_rotation=30) py_bar.add("大标题",[1, 3, 3, 7]) py_bar.add("小标题",[1, 6, 6, 4]) py_bar.range = [0, 10] py_bar.render_to_file("wsd.svg")
12、Funnel
漏斗图
import pygal funnel_chart = pygal.Funnel() funnel_chart.title = ‘V8 benchmark results‘ funnel_chart.x_labels = [‘Richards‘, ‘DeltaBlue‘, ‘Crypto‘, ‘RayTrace‘, ‘EarleyBoyer‘, ‘RegExp‘, ‘Splay‘, ‘NavierStokes‘] funnel_chart.add(‘Opera‘, [3472, 2933, 4203, 5229, 5810, 1828, 9013, 4669]) funnel_chart.add(‘Firefox‘, [7473, 8099, 11700, 2651, 6361, 1044, 3797, 9450]) funnel_chart.add(‘Chrome‘, [6395, 8212, 7520, 7218, 12464, 1660, 2123, 8607]) funnel_chart.render_to_file(‘funnel-basic.svg‘)
13、SolidGauge
import pygal gauge = pygal.SolidGauge(inner_radius=0.70) # 百分格式 percent_formatter = lambda x: ‘{:.10g}%‘.format(x) # 美元格式 dollar_formatter = lambda x: ‘{:.10g}$‘.format(x) gauge.value_formatter = percent_formatter gauge.add(‘Series 1‘, [{‘value‘: 225000, ‘max_value‘: 1275000}], formatter=dollar_formatter) gauge.add(‘Series 2‘, [{‘value‘: 110, ‘max_value‘: 100}]) gauge.add(‘Series 3‘, [{‘value‘: 3}]) gauge.add( ‘Series 4‘, [ {‘value‘: 51, ‘max_value‘: 100}, {‘value‘: 12, ‘max_value‘: 100}]) gauge.add(‘Series 5‘, [{‘value‘: 79, ‘max_value‘: 100}]) gauge.add(‘Series 6‘, 99) gauge.add(‘Series 7‘, [{‘value‘: 100, ‘max_value‘: 100}]) gauge.render_to_file(‘solidgauge-normal.svg‘)
14、Gauge
仪表图
import pygal gauge_chart = pygal.Gauge(human_readable=True) gauge_chart.title = ‘DeltaBlue V8 benchmark results‘ gauge_chart.range = [0, 10000] gauge_chart.add(‘Chrome‘, 8212) gauge_chart.add(‘Firefox‘, 8099) gauge_chart.add(‘Opera‘, 2933) gauge_chart.add(‘IE‘, 41) gauge_chart.render_to_file(‘gauge-basic.svg‘)
15、Maps
World map
安装
pip install pygal_maps_world
Countries
import pygal worldmap_chart = pygal.maps.world.World() worldmap_chart.title = ‘Some countries‘ worldmap_chart.add(‘C countries‘, [‘cn‘, ‘ca‘, ‘ch‘, ‘cg‘]) worldmap_chart.add(‘F countries‘, [‘fr‘, ‘fi‘]) worldmap_chart.add(‘M countries‘, [‘ma‘, ‘mc‘, ‘md‘, ‘me‘, ‘mg‘, ‘mk‘, ‘ml‘, ‘mm‘, ‘mn‘, ‘mo‘, ‘mr‘, ‘mt‘, ‘mu‘, ‘mv‘, ‘mw‘, ‘mx‘, ‘my‘, ‘mz‘]) worldmap_chart.add(‘U countries‘, [‘ua‘, ‘ug‘, ‘us‘, ‘uy‘, ‘uz‘]) worldmap_chart.render_to_file(‘world-map-countries.svg‘)
16、Continents
访问各大洲
import pygal supra = pygal.maps.world.SupranationalWorld() supra.add(‘Asia‘, [(‘asia‘, 1)]) supra.add(‘Europe‘, [(‘europe‘, 1)]) supra.add(‘Africa‘, [(‘africa‘, 1)]) supra.add(‘North america‘, [(‘north_america‘, 1)]) supra.add(‘South america‘, [(‘south_america‘, 1)]) supra.add(‘Oceania‘, [(‘oceania‘, 1)]) supra.add(‘Antartica‘, [(‘antartica‘, 1)]) supra.render_to_file(‘world-map-continents.svg‘)
三、掷色子
分析点数概率并且绘制直方图
1、创建源文件(引用所需)
from random import randint class Die(): """表示一个色子的类""" def __init__(self,num_sides=6): """色子默认为6面""" self.num_sides=num_sides def roll(self): """返回一个位于1和色子面数之间的随机值""" return randint(1, self.num_sides)
2、创建一个色子
from Pygal.示例.die import Die import pygal # 创建一个色子 die = Die() # 掷几次色子,并且将结果存储在一个列表中 results = [] for roll in range(1000): r = die.roll() results.append(r) print(results) # 分析结果 frequencies = [] for value in range(1, die.num_sides+1): frequency = results.count(value) frequencies.append(frequency) print(frequencies) # 对结果进行可视化 hist = pygal.Bar() hist.title=‘掷色子1000次的结果‘ hist.x_labels = [‘1‘,‘2‘,‘3‘,‘4‘,‘5‘,‘6‘] hist.x_title=‘Result‘ hist.y_title=‘概率‘ hist.add(‘D6‘,frequencies) hist.render_to_file(‘die_visual.svg‘)
使用浏览器打开这个文件,鼠标指向数据,可以看到显示了标题“D6”, x轴的坐标以及y轴坐标。
可以发现,六个数字出现的频次是差不多的(理论上概率是1/6, 随着实验次数的增加,趋势越来越明显)
3、同时掷两个骰子
稍微改下代码就行,再实例化一个骰子
from Pygal.示例.die import Die import pygal # 创建两个色子 die_1 = Die() die_2 = Die() # 掷几次色子,并且将结果存储在一个列表中 results = [] for roll in range(1000): r = die_1.roll() + die_2.roll() results.append(r) print(results) # 分析结果 frequencies = [] max_result= die_1.num_sides + die_2.num_sides for value in range(2, max_result + 1): frequency = results.count(value) frequencies.append(frequency) print(frequencies) # 对结果进行可视化 hist = pygal.Bar() hist.title=‘掷色子1000次的结果‘ hist.x_labels = [‘2‘,‘3‘,‘4‘,‘5‘,‘6‘,‘7‘,‘8‘,‘9‘,‘10‘,‘11‘,‘12‘] hist.x_title=‘Result‘ hist.y_title=‘概率‘ hist.add(‘D6 + D6‘,frequencies) hist.render_to_file(‘die_visualc.svg‘)****
从图中可以看出,两个骰子之和为7的次数最多,和为2的次数最少。因为能掷出2的只有一种情况 -> (1, 1);而掷出7的情况有(1, 6) , (2, 5), (3, 4), (4, 3), (5, 2), (6, 1)共6种情况,其余数字的情况都没有7的多,故掷得7得概率最大。
4、同时掷两个骰子(六和十的)
from Pygal.示例.die import Die import pygal # 创建两个色子 die_1 = Die() die_2 = Die(10) # 掷几次色子,并且将结果存储在一个列表中 results = [] for roll in range(50000): r = die_1.roll() + die_2.roll() results.append(r) print(results) # 分析结果 frequencies = [] max_result= die_1.num_sides + die_2.num_sides for value in range(2, max_result + 1): frequency = results.count(value) frequencies.append(frequency) print(frequencies) # 对结果进行可视化 hist = pygal.Bar() hist.title=‘掷色子1000次的结果‘ # hist.x_labels = [‘2‘,‘3‘,‘4‘,‘5‘,‘6‘,‘7‘,‘8‘,‘9‘,‘10‘,‘11‘,‘12‘,‘13‘,‘14‘,‘15‘,‘16‘] hist.x_labels = [i for i in range(2,max_result+1)] hist.x_title=‘Result‘ hist.y_title=‘概率‘ hist.add(‘D6 + D6‘,frequencies) hist.render_to_file(‘die_visualcc.svg‘)
四、Python处理csv文件
CSV(Comma-Separated Values)即逗号分隔值,可以用Excel打开查看。由于是纯文本,任何编辑器也都可打开。与Excel文件不同,CSV文件中:
- 值没有类型,所有值都是字符串
- 不能指定字体颜色等样式
- 不能指定单元格的宽高,不能合并单元格
- 没有多个工作表
- 不能嵌入图像图表
在CSV文件中,以,
作为分隔符,分隔两个单元格。像这样a,,c
表示单元格a
和单元格c
之间有个空白的单元格。依此类推。
不是每个逗号都表示单元格之间的分界。所以即使CSV是纯文本文件,也坚持使用专门的模块进行处理。Python内置了csv模块。先看看一个简单的例子。
1、从CSV文件中读取数据
import csv filename = ‘F:/Jupyter Notebook/matplotlib_pygal_csv_json/sitka_weather_2014.csv‘ with open(filename) as f: reader = csv.reader(f) print(list(reader))
**data
不能直接打印,list(data)最外层是list,里层的每一行数据都在一个list中,有点像这样**
[[‘name‘, ‘age‘], [‘Bob‘, ‘14‘], [‘Tom‘, ‘23‘], ...]
于是我们可以这样访问到Bob的年龄reader[1][1]
, 在for循环中遍历如下
import csv filename = ‘F:/Jupyter Notebook/matplotlib_pygal_csv_json/sitka_weather_2014.csv‘ with open(filename) as f: reader = csv.reader(f) for row in reader: # 行号从1开始 print(reader.line_num, row)
截取一部分输出
1 [‘AKST‘, ‘Max TemperatureF] 2 [‘2014-1-1‘, ‘46‘, ‘42‘, ‘37‘, ‘40‘, ‘38‘, ‘36‘, ‘97‘, 138‘] ...
前面的数字是行号,从1开始,可以用reader.line_num
获取。
要注意的是,reader只能被遍历一次。由于reader是可迭代对象,可以使用next
方法一次获取一行。
import csv filename = ‘F:/Jupyter Notebook/matplotlib_pygal_csv_json/sitka_weather_2014.csv‘ with open(filename) as f: reader = csv.reader(f) # 读取一行,下面的reader中已经没有该行了 head_row = next(reader) for row in reader: # 行号从2开始 print(reader.line_num, row)
2、写数据到csv文件中
有reader可以读取,当然也有writer可以写入。一次写入一行,一次写入多行都可以。
import csv # 使用数字和字符串的数字都可以 datas = [[‘name‘, ‘age‘], [‘Bob‘, 14], [‘Tom‘, 23], [‘Jerry‘, ‘18‘]] with open(‘example.csv‘, ‘w‘, newline=‘‘) as f: writer = csv.writer(f) for row in datas: writer.writerow(row) # 还可以写入多行 writer.writerows(datas)
如果不指定newline=‘‘
,则每写入一行将有一空行被写入。上面的代码生成如下内容。
name,age Bob,14 Tom,23 Jerry,18 name,age Bob,14 Tom,23 Jerry,18
3、DictReader和DictWriter对象
使用DictReader可以像操作字典那样获取数据,把表的第一行(一般是标头)作为key。可访问每一行中那个某个key对应的数据。
import csv filename = ‘F:/Jupyter Notebook/matplotlib_pygal_csv_json/sitka_weather_2014.csv‘ with open(filename) as f: reader = csv.DictReader(f) for row in reader: # Max TemperatureF是表第一行的某个数据,作为key max_temp = row[‘Max TemperatureF‘] print(max_temp)
使用DictWriter类,可以写入字典形式的数据,同样键也是标头(表格第一行)。
import csv headers = [‘name‘, ‘age‘] datas = [{‘name‘:‘Bob‘, ‘age‘:23}, {‘name‘:‘Jerry‘, ‘age‘:44}, {‘name‘:‘Tom‘, ‘age‘:15} ] with open(‘example.csv‘, ‘w‘, newline=‘‘) as f: # 标头在这里传入,作为第一行数据 writer = csv.DictWriter(f, headers) writer.writeheader() for row in datas: writer.writerow(row) # 还可以写入多行 writer.writerows(datas)
4、统计每月最高温度
import csv from matplotlib import pyplot as plt from datetime import datetime plt.rcParams[‘font.sans-serif‘]=[‘SimHei‘] #用来正常显示中文标签 plt.rcParams[‘axes.unicode_minus‘]=False #用来正常显示负号 filename = ‘Python-sitka_weather_2014.csv‘ with open(filename) as f: # 调用reader()函数,将f对象作为参数传递给它,从而创建一个与该文件相关联的阅读器对象 reader = csv.reader(f) # 返回文件中的下一行 header_row = next(reader) # print(header_row) # for index, column_header in enumerate(header_row): # print(index, column_header) highs = [] for row in reader: # 使用int()将字符串转换为数字,让matplotlib能够读取它们 high = int(row[1]) highs.append(high) print(highs) # 根据数据绘制图形 fig = plt.figure(dpi=128, figsize=(16, 9)) plt.plot(highs, c=‘red‘) # 设置图形格式 plt.title(‘2014年最高气温‘, fontsize=24) plt.xlabel(‘‘, fontsize=16) plt.ylabel(‘最高气温‘, fontsize=16) plt.tick_params(axis=‘both‘, which=‘major‘, labelsize=16) plt.show()
5、统计每月最高温度和最低温度
import csv from matplotlib import pyplot as plt from datetime import datetime plt.rcParams[‘font.sans-serif‘] = [‘SimHei‘] # 用来正常显示中文标签 plt.rcParams[‘axes.unicode_minus‘] = False # 用来正常显示负号 filename = ‘Python-sitka_weather_2014.csv‘ with open(filename) as f: # 调用reader()函数,将f对象作为参数传递给它,从而创建一个与该文件相关联的阅读器对象 reader = csv.reader(f) # 返回文件中的下一行 header_row = next(reader) # print(header_row) dates, highs, lows = [], [], [] for row in reader: current_date = datetime.strptime(row[0], "%Y/%m/%d") dates.append(current_date) # print(current_date) # 使用int()将字符串转换为数字,让matplotlib能够读取它们 high = int(row[1]) highs.append(high) low = int(row[3]) lows.append(low) # print(highs) # 根据数据绘制图形 fig = plt.figure(dpi=128, figsize=(16, 9)) plt.plot(dates, highs, c=‘red‘, alpha=0.5) plt.plot(dates, lows, c=‘blue‘, alpha=0.5) plt.fill_between(dates, highs, lows, facecolor=‘blue‘, alpha=0.1) # 设置图形格式 plt.title(‘2014年最高气温‘, fontsize=24) plt.xlabel(‘‘, fontsize=16) # 绘制斜线标签 fig.autofmt_xdate() plt.ylabel(‘最高气温‘, fontsize=16) plt.tick_params(axis=‘both‘, which=‘major‘, labelsize=16) plt.show() # plt.savefig(‘hish.png‘)