上篇:数据采集

说到数据采集,那最大名鼎鼎的方式就是“爬虫”啦,让我们来看看百媚生带给我们的“爬虫”利器吧,是不是真如传言的“见血封喉”呢?

Requests

啥?为什么 requests 是“爬虫”?
可不要小瞧了它!虽说 requests 是网络请求库,但它却如高手手中的「木剑」一般,用好了,一样招招致命。
使用 requests 发起攻击(请求),犹如疾风般迅速,犹如落叶般轻盈。

  1. >>> r = requests.get('https://api.github.com/user', auth=('user', 'pass'))
  2. >>> r.status_code
  3. 200
  4. >>> r.headers['content-type']
  5. 'application/json; charset=utf8'
  6. >>> r.encoding
  7. 'utf-8'
  8. >>> r.text
  9. '{"type":"User"...'
  10. >>> r.json()
  11. {'private_gists': 419, 'total_private_repos': 77, ...}
  12. 复制代码

这就完了?
如果对方是返回 Json 格式的 API 服务,是的,这就完了。我们已经拿到数据了。
如果对方是返回 XML 格式的 API 服务,那么,我们再搭配上原生的 xml 或者 lxml 解析器,灭敌于百步之外。

  1. """
  2. content 是 xml 格式的字符串,即 r.text
  3. 例如
  4. <?xml version="1.0"?>
  5. <data>
  6. <country name="a"></country>
  7. <country name="b"></country>
  8. <country name="c"></country>
  9. </data>
  10. """
  11. import xml.etree.ElementTree as ET
  12. tree = ET.parse(content)
  13. root = tree.getroot()
  14. # 遍历节点
  15. for child in root:
  16. print(child.tag, child.attrib)
  17. 复制代码

lxml 更快更凶残。

  1. from lxml import etree
  2. root = etree.XML(content)
  3. for element in root.iter():
  4. print("%s - %s" % (element.tag, element.text))
  5. 复制代码

lxml 更是支持强大的 xpathxlst 语法(语法文档详见参考)。

  1. # 使用 xpath 语法快速定位节点,提取数据
  2. r = root.xpath('country')
  3. text = root.xpath('country/text()')
  4. 复制代码

xlst 进行快速转换。

  1. xslt_root = etree.XML('''\
  2. <xsl:stylesheet version="1.0"
  3. xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  4. <xsl:template match="/">
  5. <foo><xsl:value-of select="/a/b/text()" /></foo>
  6. </xsl:template>
  7. </xsl:stylesheet>''')
  8. transform = etree.XSLT(xslt_root)
  9. f = StringIO('<a><b>Text</b></a>')
  10. doc = etree.parse(f)
  11. result_tree = transform(doc)
  12. 复制代码

对手更凶残了,是 HTML 文档!这下就需要 BeautifulSouplxml 解析器出马了。
BeautifulSoup 虽然速度不快,好在利于理解。

  1. from bs4 import BeautifulSoup
  2. # content 即 html 字符串, requests 返回的文本 text
  3. soup = BeautifulSoup(content, 'html.parser')
  4. print(soup.title)
  5. print(soup.title.name)
  6. print(soup.find_all('a'))
  7. print(soup.find(id="link3"))
  8. for link in soup.find_all('a'):
  9. print(link.get('href'))
  10. 复制代码

上房揭瓦(解析网页),那是手到擒来。
而用 lxml 还是那么干净利落。

  1. html = etree.HTML(content)
  2. result = etree.tostring(html, pretty_print=True, method="html")
  3. print(result)
  4. # 接下来就是 xpath 的表演时间
  5. 复制代码

可见,木剑虽朴实,在高手手中,也能变化无穷。如果是“接骨木”,那更是了不得。最快速便捷的数据采集神兵,非 requests 莫属!

Scrapy

接下来让我们看看数据采集的百变神兵 —— Scrapy,分分钟让我们全副武装。

  1. # 创建一个项目
  2. scrapy startproject tutorial
  3. cd tutorial
  4. # 创建一个爬虫
  5. scrapy genspider quotes quotes.toscrape.com
  6. 复制代码

然后编辑项目下 spiders/quotes.py 爬虫文件。

  1. import scrapy
  2. class QuotesSpider(scrapy.Spider):
  3. name = "quotes"
  4. def start_requests(self):
  5. """
  6. 生成初始请求。
  7. """
  8. urls = [
  9. 'http://quotes.toscrape.com/page/1/',
  10. 'http://quotes.toscrape.com/page/2/',
  11. ]
  12. for url in urls:
  13. yield scrapy.Request(url=url, callback=self.parse)
  14. def parse(self, response):
  15. """
  16. 处理请求返回的响应。
  17. """
  18. page = response.url.split("/")[-2]
  19. filename = 'quotes-%s.html' % page
  20. with open(filename, 'wb') as f:
  21. f.write(response.body)
  22. self.log('Saved file %s' % filename)
  23. 复制代码

然后就是启动爬虫。

  1. scrapy crawl quotes
  2. 复制代码

这还没有发挥 Scrapy 的能力呢!
解析网页

  1. # CSS 解析
  2. response.css('title::text').getall()
  3. # xpath 解析
  4. response.css('//title/text()').getall()
  5. 复制代码

自动生成结果文件

  1. import scrapy
  2. class QuotesSpider(scrapy.Spider):
  3. name = "quotes"
  4. start_urls = [
  5. 'http://quotes.toscrape.com/page/1/',
  6. 'http://quotes.toscrape.com/page/2/',
  7. ]
  8. def parse(self, response):
  9. # parse 函数直接返回字典或者 Item 对象。
  10. for quote in response.css('div.quote'):
  11. yield {
  12. 'text': quote.css('span.text::text').get(),
  13. 'author': quote.css('small.author::text').get(),
  14. 'tags': quote.css('div.tags a.tag::text').getall(),
  15. }
  16. 复制代码

在爬取的命令上加上 -o 参数,即可快速将结果保存到文件,支持多种格式(csv,json,json lines,xml),也可方便地扩展自己的格式。

  1. scrapy crawl quotes -o quotes.json
  2. 复制代码

数据分页了,还有下一页怎么办?抛出请求,让 Scrapy 自己去处理。

  1. class QuotesSpider(scrapy.Spider):
  2. name = "quotes"
  3. start_urls = [
  4. 'http://quotes.toscrape.com/page/1/',
  5. ]
  6. def parse(self, response):
  7. """
  8. parse 函数 yield 字典或者 Item 对象,则视为结果,
  9. yield 请求对象(follow 方法即是跟随链接,快速生成对应的请求对象)即继续爬取。
  10. """
  11. for quote in response.css('div.quote'):
  12. yield {
  13. 'text': quote.css('span.text::text').get(),
  14. 'author': quote.css('span small::text').get(),
  15. 'tags': quote.css('div.tags a.tag::text').getall(),
  16. }
  17. next_page = response.css('li.next a').get()
  18. if next_page is not None:
  19. yield response.follow(next_page, callback=self.parse)
  20. 复制代码

这就完了吗?当然不会,Scrapy 还提供了多种数据采集需要用到的功能。

  • 强大的扩展能力,快速编写扩展和中间件。
  • 灵活的配置,并发控制,限速控制等。
  • 自定义的爬取对象处理流水线。
  • 自定义的爬取对象存储。
  • 自动统计数据。
  • 整合邮件。
  • Telnet 控制台等等。

这只是核心功能,还没见到它的社区能力呢!

这些就不再展开了。
快速而又强大的数据采集利器,当属 Scrapy

Pyspider

强大的瑞士军刀 —— Pyspider。
Pyspider 可不得了,它提供了一整套完整的数据采集解决方案,堪称爬虫界的“瑞士军刀”。

  • 原生提供 Web 管理界面,支持任务监控、项目管理、结果查看等等。
  • 原生支持众多的数据库后端,如 MySQL、MongoDB、SQLite、Elasticsearch、Postgresql。
  • 原生支持多种消息队列,如 RabbitMQ,Beanstalk、Redis、Kombu。
  • 支持任务优先级、自动重试、定时任务、支持 JS 渲染等功能。
  • 分布式架构。

爬虫,就是这么简单!

  1. from pyspider.libs.base_handler import *
  2. class Handler(BaseHandler):
  3. crawl_config = {
  4. }
  5. @every(minutes=24 * 60)
  6. def on_start(self):
  7. self.crawl('http://scrapy.org/', callback=self.index_page)
  8. @config(age=10 * 24 * 60 * 60)
  9. def index_page(self, response):
  10. for each in response.doc('a[href^="http"]').items():
  11. self.crawl(each.attr.href, callback=self.detail_page)
  12. def detail_page(self, response):
  13. return {
  14. "url": response.url,
  15. "title": response.doc('title').text(),
  16. }
  17. 复制代码

启动爬虫框架。

  1. pyspider
  2. 复制代码

然后,我们就可以通过 http://localhost:5000/ 进行爬虫的管理和运行了。
我们可以使用 css 选择器快速提取网页信息。

  1. def index_page(self, response):
  2. for each in response.doc('a[href^="http"]').items():
  3. if re.match("http://www.imdb.com/title/tt\d+/$", each.attr.href):
  4. self.crawl(each.attr.href, callback=self.detail_page)
  5. self.crawl(response.doc('#right a').attr.href, callback=self.index_page)
  6. def detail_page(self, response):
  7. return {
  8. "url": response.url,
  9. "title": response.doc('.header > [itemprop="name"]').text(),
  10. "rating": response.doc('.star-box-giga-star').text(),
  11. "director": [x.text() for x in response.doc('[itemprop="director"] span').items()],
  12. }
  13. 复制代码

启用 PhantomJS 来渲染网页上的 JS。

  1. pyspider phantomjs
  2. 复制代码

使用 fetch_type='js'

  1. class Handler(BaseHandler):
  2. def on_start(self):
  3. self.crawl('http://www.twitch.tv/directory/game/Dota%202',
  4. fetch_type='js', callback=self.index_page)
  5. def index_page(self, response):
  6. return {
  7. "url": response.url,
  8. "channels": [{
  9. "title": x('.title').text(),
  10. "viewers": x('.info').contents()[2],
  11. "name": x('.info a').text(),
  12. } for x in response.doc('.stream.item').items()]
  13. }
  14. 复制代码

还能执行一段 JS 代码,来获取那些动态生成的网页内容。

  1. class Handler(BaseHandler):
  2. def on_start(self):
  3. self.crawl('http://www.pinterest.com/categories/popular/',
  4. fetch_type='js', js_script="""
  5. function() {
  6. window.scrollTo(0,document.body.scrollHeight);
  7. }
  8. """, callback=self.index_page)
  9. def index_page(self, response):
  10. return {
  11. "url": response.url,
  12. "images": [{
  13. "title": x('.richPinGridTitle').text(),
  14. "img": x('.pinImg').attr('src'),
  15. "author": x('.creditName').text(),
  16. } for x in response.doc('.item').items() if x('.pinImg')]
  17. }
  18. 复制代码

好了,接下来我知道,问题就是 PyspiderScrapy 选哪个?
简单说下它们的对比。
Scrapy 有更强大的扩展能力,社区更活跃,周边更丰富。而 Pyspider 本身功能更全,但扩展能力较弱。许多 Scrapy 需要扩展实现的功能,如 Web 界面、JS 渲染等,Pyspider 原生都提供了。
Pyspider 的整套生态上手更容易,实现更快速。Scrapy 对复杂的场景有更多的选择余地,更灵活。
所以,诸位选哪款?
成年人需要做选择吗?

参考

中篇:数据处理

说到数据处理,最大名鼎鼎的应该就是“Excel”了,它的大名,可谓无人不知,无人不晓。有了 Excel,即使毫无编程经验的人,都能对数据耍上两把。而我们今天就来看看,仅仅一点点代码,就能实现的更为强大的功能。

Numpy

第一把交椅,必须是“Numpy”了。不少人可能听说过,但是可能从未用过,因为 Numpy 是 Python 数据处理的基石,是其他不少相关工具的依赖。
Numpy 的功能非常纯粹,它为 Python 提供了一个非常强大且灵活的“数组”数据结构。

但是 Python 不是有列表(list)了吗?为什么还要其他的“数组”结构?

Numpy 主要有以下几个特点:

  • list 仅仅是一个“链表”,而 Numpy 提供了真正的多维数组
  • 提供了众多的数组相关操作
  • 提供了许多线性代数等数学函数
  • 提供了广播功能,像操作标量一样操作多维数组
  • 底层使用 C 实现,非常快,适合大量数据处理

Numpy 提供了非常方便的方法生成多维数组,例如从 list 转换而来。

  1. import numpy as np
  2. a = np.array([1,2,3,4])
  3. 复制代码

第一行引入 Numpy,这是一般推荐的做法,用 as 别名为 np。不是必须的,但建议遵循。
还有众多的生成函数。

  1. # 生成一个 3 * 4 的矩阵,每个元素都是 0
  2. np.zeros((3,4))
  3. # 生成一个 2 * 3 的矩阵,每个元素都是 1
  4. np.ones( (2,3) )
  5. # 等差数列生成,从 10 到 30,5 递增
  6. np.arange(10, 30, 5)
  7. 复制代码

不同于 list,数组的每个元素必须有一致的数据类型。

  1. >>> a.dtype.name
  2. 'int64'
  3. 复制代码

可以在创建时通过 dtype 参数指定,例如 int32,int64,float64 等。
Numpy 的数组有几个比较重要的属性。

  • ndim:轴的数量,也就是维度
  • shape:数组大小的元祖
  • size:总数据个数
  • dtype:数据类型
  • itemsize:每个数组元素占的字节数

    1. # 创建一个 1 到 14 到数组,并改成 3 * 5 的矩阵
    2. >>> a = np.arange(15).reshape(3, 5)
    3. >>> a
    4. array([[ 0, 1, 2, 3, 4],
    5. [ 5, 6, 7, 8, 9],
    6. [10, 11, 12, 13, 14]])
    7. >>> a.ndim
    8. 2
    9. >>> a.shape
    10. (3, 5)
    11. >>> a.size
    12. 15
    13. >>> a.dtype.name
    14. 'int64'
    15. >>> a.itemsize
    16. 8
    17. 复制代码

    这还是牛刀小试,让我们来见识 Numpy 的实力吧,像操作普通标量一样操作数组。

    1. # 数组每个元素乘以 2
    2. >>> b = a * 2
    3. >>> b
    4. array([[ 0, 2, 4, 6, 8],
    5. [10, 12, 14, 16, 18],
    6. [20, 22, 24, 26, 28]])
    7. # 数组对应元素相加
    8. >>> a + b
    9. array([[ 0, 3, 6, 9, 12],
    10. [15, 18, 21, 24, 27],
    11. [30, 33, 36, 39, 42]])
    12. # 数组对应元素相乘
    13. >>> a * b
    14. array([[ 0, 2, 8, 18, 32],
    15. [ 50, 72, 98, 128, 162],
    16. [200, 242, 288, 338, 392]])
    17. # 逻辑判断,每个元素是否小于 8
    18. >>> a < 8
    19. array([[ True, True, True, True, True],
    20. [ True, True, True, False, False],
    21. [False, False, False, False, False]], dtype=bool)
    22. 复制代码

    基本的函数操作

    1. >>> a
    2. array([[ 0, 1, 2, 3, 4],
    3. [ 5, 6, 7, 8, 9],
    4. [10, 11, 12, 13, 14]])
    5. # 纵向求和(轴索引为 0)
    6. >>> a.sum(axis=0)
    7. array([15, 18, 21, 24, 27])
    8. # 横向求和(轴索引为 1)
    9. >>> a.sum(axis=1)
    10. array([10, 35, 60])
    11. # 每行的最小值(轴索引为 1)
    12. >>> a.min(axis=1)
    13. array([ 0, 5, 10])
    14. 复制代码

    取数据

    1. # 取索引 0,0 处的元素,也就是第一行第一个
    2. >>> a[0,0]
    3. 0
    4. # 取索引 2,3 处的元素
    5. >>> a[2,3]
    6. 13
    7. 复制代码

    当然也可以切片操作

    1. # 取索引为 1 到 2 (不含)的行,也就是第 2 行
    2. >>> a[1:2]
    3. array([[5, 6, 7, 8, 9]])
    4. # 取索引为 1 到 3 (不含)的行,索引为 2 到 5(不含)的列
    5. >>> a[1:3,2:5]
    6. array([[ 7, 8, 9],
    7. [12, 13, 14]]))
    8. 复制代码

    还有其他的数组转换、线性代数计算等函数,就不详细展开了。Numpy 就是我们进行数据处理的基本容器,有了 Numpy 提供的数组结构,后面的操作就游刃有余了。

    Scipy

    Scipy 是数学、工程、科学计算相关的函数库,本身是基于 Numpy 的数组的,也是和 Numpy 师出同门。
    Scipy 有许多子模块,每个子模块提供各个领域特定的功能。

  • cluster:聚类算法

  • constants:物理和数学的常量
  • fftpack:快速傅立叶变换
  • integrate:微积分方程求解
  • interpolate:插值计算
  • io:输入输出
  • linalg:线性代数
  • ndimage:n 维图像处理
  • ord:正交距离回归
  • optimize:优化
  • signal:信号处理
  • sparse:稀疏矩阵
  • spatial:空间数据结构和算法
  • special:特殊函数
  • stats:统计分布函数

各个函数的使用需要一定的理论基础,scipy 已经帮助我们实现,只需要调用即可。
例如,我们计算如下的积分方程
Python数据相关模块 - 图1

  1. >>> import scipy.integrate as integrate
  2. >>> import scipy.special as special
  3. >>> result = integrate.quad(lambda x: special.jv(2.5,x), 0, 4.5)
  4. >>> result
  5. (1.1178179380783249, 7.8663172481899801e-09)
  6. 复制代码

是不是再也不怕高数作业了?

Pandas

Pandas 不是一只普通的熊猫。Pandas 是基于 Numpy 的高级的数据处理工具库。Pandas 最擅长的就是处理表格数据,也就是我们熟悉的 Excel 类型的数据,Pandas 提供的方法能让我们简单的几行代码快速处理类 Excel 或者关系型数据表的数据。
一个最简单的例子。

  1. >>> import pandas as pd # 和 nunpy 一样的约定,一般引入后别名为 pd
  2. >>> df = pd.read_csv('test.csv') # 读取 csv 文件
  3. >>> df # 显示读取的内容,为两列三行的数据
  4. name price
  5. 0 apple 5
  6. 1 banana 3
  7. 2 pear 2
  8. >>> df.describe() # 查看数据统计,自动找出数据列(price),计算相关统计量
  9. price
  10. count 3.000000
  11. mean 3.333333
  12. std 1.527525
  13. min 2.000000
  14. 25% 2.500000
  15. 50% 3.000000
  16. 75% 4.000000
  17. max 5.000000
  18. >>> df.price = df.price * 2 # 所有价格都乘以 2
  19. >>> df
  20. name price
  21. 0 apple 10
  22. 1 banana 6
  23. 2 pear 4
  24. >>> df.to_csv('out.csv', index=False) # 导出数据到 csv 文件
  25. 复制代码

Pandas 可以用来做数据清洗、转换、聚合等操作,为下一步数据可视化及机器学习准备数据。
Pandas 有多种数据类型,其中就常见的两种就是 Series 和 DataFrame。
DataFrame 可以理解为一个数据表格,行有行的标签(索引),列有列的标签(索引),是不是和 Excel 很像?
Python数据相关模块 - 图2
而 Series 即是这个数据表格的一列或者一行。所以一个 DataFrame 也可看作是一个 Series 数组。
Pandas 提供了一系列的方法快速的从其他数据源导入导出数据。
Python数据相关模块 - 图3
Pandas 也能非常方便的“挑选”特定的数据。
Python数据相关模块 - 图4
通过 Matplotlib 库,Pandas 可以方便的绘制可视化图表。
Python数据相关模块 - 图5
对于基本的数据抽取、转换、清洗等工作,Pandas 完全能胜任,掌握了 Pandas 就有了应对各种敌人的百变战甲。

Scikit-Learn

Scikit-Learn 是 Python 的机器学习库,也是在 Numpy 和 Scipy 的基础上,提供了强大的机器学习算法的实现。例如分类算法(SVM,K 近邻,随机森林等),回归算法(线性回归、SVR 等),聚类算法(K-means、谱聚类等)、降维、模型选择、数据预处理等等。
Scikit-Learn 通过对算法的高度抽象,对于一个基本的算法调用过程,基本上就简化为了:初始化->数据预处理->训练->预测,流程中的几个步骤。
一个简单的线性回归的例子。

  1. >>> from sklearn import linear_model
  2. >>> reg = linear_model.LinearRegression()
  3. >>> reg.fit([[0, 0], [1, 1], [2, 2]], [0, 1, 2])
  4. LinearRegression()
  5. >>> reg.coef_
  6. array([0.5, 0.5])
  7. 复制代码

除了基本的算法实现,scikit-learn 提供的数据预处理也极其强大,基本能涵盖数据预处理的许多方面。
例如,数据归一化,快速的 Z-score 归一化,使数据满足正态分布。

  1. >>> from sklearn import preprocessing
  2. >>> import numpy as np
  3. >>> X_train = np.array([[ 1., -1., 2.],
  4. ... [ 2., 0., 0.],
  5. ... [ 0., 1., -1.]])
  6. >>> X_scaled = preprocessing.scale(X_train)
  7. >>> X_scaled
  8. array([[ 0. ..., -1.22..., 1.33...],
  9. [ 1.22..., 0. ..., -0.26...],
  10. [-1.22..., 1.22..., -1.06...]])
  11. 复制代码

数据编码,可以将枚举的字符串数据编码为数值型,方便后续计算。

  1. >>> X = [['male', 'from US', 'uses Safari'], ['female', 'from Europe', 'uses Firefox']]
  2. >>> enc.fit(X)
  3. OrdinalEncoder()
  4. >>> enc.transform([['female', 'from US', 'uses Safari']])
  5. array([[0., 1., 1.]])
  6. 复制代码

One Hot 编码,将枚举字符串数据转换为多列的布尔数据(0 / 1)。

  1. >>> X = [['male', 'from US', 'uses Safari'], ['female', 'from Europe', 'uses Firefox']]
  2. >>> enc.fit(X)
  3. OneHotEncoder()
  4. >>> enc.transform([['female', 'from US', 'uses Safari'],
  5. ... ['male', 'from Europe', 'uses Safari']]).toarray()
  6. array([[1., 0., 0., 1., 0., 1.],
  7. [0., 1., 1., 0., 0., 1.]])
  8. 复制代码

缺失值处理。

  1. >>> import numpy as np
  2. >>> from sklearn.impute import SimpleImputer
  3. >>> imp = SimpleImputer(missing_values=np.nan, strategy='mean')
  4. >>> imp.fit([[1, 2], [np.nan, 3], [7, 6]])
  5. SimpleImputer()
  6. >>> X = [[np.nan, 2], [6, np.nan], [7, 6]]
  7. >>> print(imp.transform(X))
  8. [[4. 2. ]
  9. [6. 3.666...]
  10. [7. 6. ]]
  11. 复制代码

如果想要看一个实战的项目,这里有一个基因型预测身高的项目例子。该项目根据用户在多个基因位点的基因型和身高数据进行模型训练,然后可根据训练的模型预测其他用户的身高。我们先不管这个预测的理论可行性和准确性,不过整个数据预处理和模型训练的操作是一个学习 scikit-learn 的过程。

Statsmodels

statsmodels 是一个 Python 的统计模型库,提供了很多统计相关的函数和操作,例如线性回归、统计检验、时间序列分析等。statsmodels 也是构建在 Numpy、SciPy 和 Pandas 之上的。
线性回归模型

  1. import statsmodels.api as sm
  2. data = sm.datasets.scotland.load(as_pandas=False) # 载入测试数据
  3. data.exog = sm.add_constant(data.exog)
  4. # 实例化伽马模型
  5. >>> gamma_model = sm.GLM(data.endog, data.exog, family=sm.families.Gamma())
  6. >>> gamma_results = gamma_model.fit()
  7. >>> print(gamma_results.summary())
  8. Generalized Linear Model Regression Results
  9. ==============================================================================
  10. Dep. Variable: y No. Observations: 32
  11. Model: GLM Df Residuals: 24
  12. Model Family: Gamma Df Model: 7
  13. Link Function: inverse_power Scale: 0.0035843
  14. Method: IRLS Log-Likelihood: -83.017
  15. Date: Fri, 21 Feb 2020 Deviance: 0.087389
  16. Time: 13:59:13 Pearson chi2: 0.0860
  17. No. Iterations: 6
  18. Covariance Type: nonrobust
  19. ==============================================================================
  20. coef std err z P>|z| [0.025 0.975]
  21. ------------------------------------------------------------------------------
  22. const -0.0178 0.011 -1.548 0.122 -0.040 0.005
  23. x1 4.962e-05 1.62e-05 3.060 0.002 1.78e-05 8.14e-05
  24. x2 0.0020 0.001 3.824 0.000 0.001 0.003
  25. x3 -7.181e-05 2.71e-05 -2.648 0.008 -0.000 -1.87e-05
  26. x4 0.0001 4.06e-05 2.757 0.006 3.23e-05 0.000
  27. x5 -1.468e-07 1.24e-07 -1.187 0.235 -3.89e-07 9.56e-08
  28. x6 -0.0005 0.000 -2.159 0.031 -0.001 -4.78e-05
  29. x7 -2.427e-06 7.46e-07 -3.253 0.001 -3.89e-06 -9.65e-07
  30. ==============================================================================
  31. 复制代码

方差分析

  1. >>> import statsmodels.api as sm
  2. >>> from statsmodels.formula.api import ols
  3. >>> moore = sm.datasets.get_rdataset("Moore", "carData", cache=True) # 载入测试数据
  4. >>> data = moore.data
  5. >>> data = data.rename(columns={"partner.status": "partner_status"}) # 重命名
  6. >>> moore_lm = ols('conformity ~ C(fcategory, Sum)*C(partner_status, Sum)', data=data).fit()
  7. >>> table = sm.stats.anova_lm(moore_lm, typ=2) # Type 2 ANOVA DataFrame
  8. >>> print(table)
  9. sum_sq df F PR(>F)
  10. C(fcategory, Sum) 11.614700 2.0 0.276958 0.759564
  11. C(partner_status, Sum) 212.213778 1.0 10.120692 0.002874
  12. C(fcategory, Sum):C(partner_status, Sum) 175.488928 2.0 4.184623 0.022572
  13. Residual 817.763961 39.0 NaN NaN
  14. 复制代码

XGBoost

XGBoost 是一个改进的梯度增强算法(gradient boosting),基本的梯度增强算法(GBDT)已经在 scikit-learn 中有了实现。而 XGBoost 改进了该算法,提供了分布式的能力,更快速,更灵活,更便携。由于 XGBoost 的简单易学,性能高效和表现优异,常常被用于各种数据科学算法大赛。例如,著名的 Kaggle 大赛,XGBoost 是许多竞赛的夺冠热门算法。
XGBoost 是一个算法的名称,但是提供众多语言的实现,例如 Python, R, Java, Scala, C++ 等,同时也支持 Hadoop,Spark 等大数据生态。
如果需要详细了解 GBDT 和 XGBoost 的前世今生,可以戳这篇文章
这里还有几个 Kaggle 竞赛项目的具体例子,可以供大家直接学习。

参考

下篇:数据可视化

数据可视化,顾名思义就是将繁多的数据转换成利于展示,便于人理解的图表。而这其中,最为常见的就是几种图:饼图、折线图、柱状图、雷达图、箱线图等等。下面我们来看看 Python 中众多的绘图库,如何来绘制这些图表吧。

Matplotlib

Matplotlib 是 Python 非常著名的绘图库,受到了 Matlab 的启发,使用方式和接口都非常像 Matlab。先来看看用 Matplotlib 绘制的图,
Python数据相关模块 - 图6
比较中规中矩,非常适合科学计算领域。 Matplotlib 一般以如下的方式引用。

  1. import matplotlib.pyplot as plt
  2. 复制代码

Matplotlib 中有几个核心概念。

  • Figure:面板,所有图像都是位于 figure 对象中,一个图像只能有一个 figure 对象。
  • Subplot:子图,figure 对象下创建一个或多个 subplot 对象(即坐标系)用于绘制图像。
  • Axis:坐标轴,即每个子图或坐标系中的一条坐标轴。

绘制柱状图的例子。

  1. import matplotlib.pyplot as plt
  2. labels = ['G1', 'G2', 'G3', 'G4', 'G5']
  3. men_means = [20, 35, 30, 35, 27]
  4. women_means = [25, 32, 34, 20, 25]
  5. men_std = [2, 3, 4, 1, 2]
  6. women_std = [3, 5, 2, 3, 3]
  7. width = 0.35
  8. fig, ax = plt.subplots() # 即 Figure,subplot 对象
  9. # 绘制柱状图
  10. ax.bar(labels, men_means, width, yerr=men_std, label='Men')
  11. ax.bar(labels, women_means, width, yerr=women_std, bottom=men_means, label='Women')
  12. # 设置标签、标题、图例
  13. ax.set_ylabel('Scores')
  14. ax.set_title('Scores by group and gender')
  15. ax.legend()
  16. # 显示图表
  17. plt.show()
  18. 复制代码

会显示如下所示的图形。
Python数据相关模块 - 图7
Matplotlib 配置的方式比较直观,同时也支持 Numpy 数组作为输入。

  1. fig = plt.figure(2) # 新开一个窗口
  2. ax1 = fig.add_subplot(1, 2, 1, polar=True) # 启动一个极坐标子图
  3. theta = np.arange(0, 2 * np.pi, 0.02) # 角度数列值
  4. ax1.plot(theta, 2 * np.ones_like(theta), lw=2) # 画图,参数:角度,半径,lw线宽
  5. ax1.plot(theta, theta / 6, linestyle='--', lw=2) # 画图,参数:角度,半径,linestyle样式,lw线宽
  6. # 启动一个极坐标子图
  7. ax2 = fig.add_subplot(1, 2, 2, polar=True)
  8. ax2.plot(theta, np.cos(5 * theta), linestyle='--', lw=2)
  9. ax2.plot(theta, 2 * np.cos(4 * theta), lw=2)
  10. # 设置网格轴的距离和角度
  11. ax2.set_rgrids(np.arange(0.2, 2, 0.2), angle=45)
  12. ax2.set_thetagrids([0, 45, 90])
  13. plt.show()
  14. 复制代码

Python数据相关模块 - 图8
Matplotlib 是 Python 绘图库中功能强大,但是偏底层的,对于绘图的控制能力比较强,但是配置项也比较多。

Seaborn

Seaborn 是基于 Matplotlib 开发的绘图库,提供了更高级和简洁的语法。通过 Seaborn 的高级 API,能够快速绘图图形,且无需配置就提供了一些好看的样式。
Python数据相关模块 - 图9
先上一个折线图。

  1. import seaborn as sns
  2. sns.set(style="ticks")
  3. # 载入测试数据集
  4. df = sns.load_dataset("anscombe")
  5. # 为每个数据集绘制折线图并展示线性回归线
  6. sns.lmplot(x="x", y="y", col="dataset", hue="dataset", data=df,
  7. col_wrap=2, ci=None, palette="muted", height=4,
  8. scatter_kws={"s": 50, "alpha": 1})
  9. 复制代码

Python数据相关模块 - 图10
绘图只有一行代码,是不是非常的酷!
Seaborn 有一套统一的绘图 API,要求原始数据的输入类型为 Pandas 的 Dataframe 或 Numpy 数组,API 形式如下:

  • sns.图名(x=’X轴列名’, y=’Y轴列名’, data=原始数据df对象)
  • sns.图名(x=’X轴列名’, y=’Y轴列名’, hue=’分组绘图参数’, data=原始数据df对象)
  • sns.图名(x=np.array, y=np.array[, …])

也来绘制一个柱状图。

  1. import seaborn as sns
  2. import numpy as np
  3. import pandas as pd
  4. import matplotlib.pyplot as plt
  5. # 生成数据
  6. x = np.arange(8)
  7. y = np.array([1,5,3,6,2,4,5,6])
  8. df = pd.DataFrame({"x-axis": x, "y-axis": y})
  9. # 绘制柱状图
  10. sns.barplot("x-axis", "y-axis", palette="RdBu_r", data=df)
  11. # 调用 Matplotlib 的底层 API
  12. plt.xticks(rotation=90)
  13. plt.show()
  14. 复制代码

Python数据相关模块 - 图11
Seaborn 提供了一个高层的简便的 API,可以和 Matplotlib 结合使用,大大简化了 Matplotlib 的配置操作。

plotnine/ggplot

plotnine 和 ggplot 的灵感都来源于 R 语言的 ggplot2,和 Matplotlib 的设计思路完全不同,是图层叠加的思想,即一层层叠加绘制。不过它们仍旧是基于 Matplotlib 开发的。输入数据需要是 Pandas 的 DataFrame 类型。两个库的语法类似,ggplot 最近已经不更新,而 plotnine 是最近比较活跃的。
下面的代码来自 plotnine,绘制柱状图。

  1. import pandas as pd
  2. import numpy as np
  3. from plotnine import *
  4. from plotnine.data import *
  5. ggplot(mpg) + geom_bar(aes(x='class'))
  6. 复制代码

Python数据相关模块 - 图12
通过 + 把一个个图层叠加起来,一般会有数据层、几何图形层和美化层组成。
然后加一些色彩。

  1. ggplot(mpg) + geom_bar(aes(x='class', fill='drv'))
  2. 复制代码

Python数据相关模块 - 图13
再进行一些变换。

  1. (
  2. ggplot(mpg)
  3. + geom_bar(aes(x='class', fill='drv'))
  4. + coord_flip()
  5. + theme_classic()
  6. )
  7. 复制代码

Python数据相关模块 - 图14
plotnine / ggplot 的语法非常简洁,非常适合于喜欢或习惯 R 语言绘图的人。

Bokeh

Bokeh 是 Python 中一个非常强大的交互式图表库,即通过 JS 代码生成可以交互的网页端图表,非常适合嵌入到前端应用中。Bokeh 绘制的图表非常好看,并且有不错的交互体验。
Python数据相关模块 - 图15
左侧的选择框可实时影响中间的数据渲染,图表右侧还有交互式的控制栏。同时数据点也能响应鼠标事件,例如鼠标移动上去后显示具体数值。
上述图表的代码如下:

  1. import pandas as pd
  2. from bokeh.layouts import column, row
  3. from bokeh.models import Select
  4. from bokeh.palettes import Spectral5
  5. from bokeh.plotting import curdoc, figure
  6. from bokeh.sampledata.autompg import autompg_clean as df
  7. df = df.copy()
  8. SIZES = list(range(6, 22, 3))
  9. COLORS = Spectral5
  10. N_SIZES = len(SIZES)
  11. N_COLORS = len(COLORS)
  12. # 数据清理
  13. df.cyl = df.cyl.astype(str)
  14. df.yr = df.yr.astype(str)
  15. del df['name']
  16. columns = sorted(df.columns)
  17. discrete = [x for x in columns if df[x].dtype == object]
  18. continuous = [x for x in columns if x not in discrete]
  19. def create_figure():
  20. xs = df[x.value].values
  21. ys = df[y.value].values
  22. x_title = x.value.title()
  23. y_title = y.value.title()
  24. kw = dict()
  25. if x.value in discrete:
  26. kw['x_range'] = sorted(set(xs))
  27. if y.value in discrete:
  28. kw['y_range'] = sorted(set(ys))
  29. kw['title'] = "%s vs %s" % (x_title, y_title)
  30. p = figure(plot_height=600, plot_width=800, tools='pan,box_zoom,hover,reset', **kw)
  31. p.xaxis.axis_label = x_title
  32. p.yaxis.axis_label = y_title
  33. if x.value in discrete:
  34. p.xaxis.major_label_orientation = pd.np.pi / 4
  35. sz = 9
  36. if size.value != 'None':
  37. if len(set(df[size.value])) > N_SIZES:
  38. groups = pd.qcut(df[size.value].values, N_SIZES, duplicates='drop')
  39. else:
  40. groups = pd.Categorical(df[size.value])
  41. sz = [SIZES[xx] for xx in groups.codes]
  42. c = "#31AADE"
  43. if color.value != 'None':
  44. if len(set(df[color.value])) > N_COLORS:
  45. groups = pd.qcut(df[color.value].values, N_COLORS, duplicates='drop')
  46. else:
  47. groups = pd.Categorical(df[color.value])
  48. c = [COLORS[xx] for xx in groups.codes]
  49. p.circle(x=xs, y=ys, color=c, size=sz, line_color="white", alpha=0.6, hover_color='white', hover_alpha=0.5)
  50. return p
  51. def update(attr, old, new):
  52. layout.children[1] = create_figure()
  53. x = Select(title='X-Axis', value='mpg', options=columns)
  54. x.on_change('value', update)
  55. y = Select(title='Y-Axis', value='hp', options=columns)
  56. y.on_change('value', update)
  57. size = Select(title='Size', value='None', options=['None'] + continuous)
  58. size.on_change('value', update)
  59. color = Select(title='Color', value='None', options=['None'] + continuous)
  60. color.on_change('value', update)
  61. controls = column(x, y, color, size, width=200)
  62. layout = row(controls, create_figure())
  63. curdoc().add_root(layout)
  64. curdoc().title = "Crossfilter"
  65. 复制代码

由于 Bokeh 生成的是 Html 网页,无法直接看到图片,可以使用 Bokeh 服务器运行查看,当然也可以整合到自己的前端服务中。

  1. bokeh serve --show crossfilter
  2. 复制代码

Pygal

Pygal 是一个 SVG 绘图库,SVG 是一种矢量图,也可嵌入前端网页中做交互式展示。不过和 Bokeh 不同的是,SVG 只是图表交互,并不包含 JS 代码来动态修改数据和样式。
Python数据相关模块 - 图16
Pygal 绘制的图表也是可以通过鼠标交互的,也可是隐藏或显示某个系列数据。
绘制柱状图。

  1. bar_chart = pygal.Bar()
  2. # 添加数据
  3. bar_chart.add('Fibonacci', [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55])
  4. # 生成 SVG
  5. bar_chart.render_to_file('bar_chart.svg')
  6. 复制代码

Python数据相关模块 - 图17
只能展示静态的截图。
多个系列的数据。

  1. import pygal
  2. # 配置图表
  3. line_chart = pygal.Bar()
  4. line_chart.title = 'Browser usage evolution (in %)'
  5. line_chart.x_labels = map(str, range(2002, 2013))
  6. # 添加数据
  7. line_chart.add('Firefox', [None, None, 0, 16.6, 25, 31, 36.4, 45.5, 46.3, 42.8, 37.1])
  8. line_chart.add('Chrome', [None, None, None, None, None, None, 0, 3.9, 10.8, 23.8, 35.3])
  9. line_chart.add('IE', [85.8, 84.6, 84.7, 74.5, 66, 58.6, 54.7, 44.8, 36.2, 26.6, 20.1])
  10. line_chart.add('Others', [14.2, 15.4, 15.3, 8.9, 9, 10.4, 8.9, 5.8, 6.7, 6.8, 7.5])
  11. line_chart.render()
  12. 复制代码

Python数据相关模块 - 图18
Pygal 语法简单,且能生成交互式 SVG 图表,适合有前端简单交互需求的场景使用。

Plotly

Plotly 是针对科学计算和机器学习可视化开发的图表库,同时它们还有 Dash 框架,用于快速开发机器学习或数据科学的应用。Plotly 绘制的图表非常美观,而且也是前端交互式的图表。Plotly 是 Plotly 公司的产品,不过它仍旧是开源且免费使用的,无需联网或注册账户,不过它们也提供企业版本。
Python数据相关模块 - 图19
还是绘制柱状图。

  1. import plotly.express as px
  2. data_canada = px.data.gapminder().query("country == 'Canada'")
  3. fig = px.bar(data_canada, x='year', y='pop')
  4. fig.show()
  5. 复制代码

Python数据相关模块 - 图20
图表也是可以交互的,且右上角有菜单栏。
简单的代码美化效果。

  1. import plotly.express as px
  2. data = px.data.gapminder()
  3. data_canada = data[data.country == 'Canada']
  4. fig = px.bar(data_canada, x='year', y='pop',
  5. hover_data=['lifeExp', 'gdpPercap'], color='lifeExp',
  6. labels={'pop':'population of Canada'}, height=400)
  7. fig.show()
  8. 复制代码

Python数据相关模块 - 图21
Plotly 非常适合构建前端交互式图表,同时可以使用 Dash 框架快速构建数据分析应用,使用上也非常简单明了。

Altair

Altair 是基于 VegaVega-Lite 语法的声明式绘图语言,可以使用简单的语法绘制交互式的图表,效果还是比较好的。
Python数据相关模块 - 图22
照例来绘制柱状图。

  1. import altair as alt
  2. import pandas as pd
  3. source = pd.DataFrame({
  4. 'a': ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I'],
  5. 'b': [28, 55, 43, 91, 81, 53, 19, 87, 52]
  6. })
  7. alt.Chart(source).mark_bar().encode(
  8. x='a',
  9. y='b'
  10. )
  11. 复制代码

Python数据相关模块 - 图23
图表也是交互式的。

  1. import altair as alt
  2. from vega_datasets import data
  3. source = data.wheat()
  4. bar = alt.Chart(source).mark_bar().encode(
  5. x='year:O',
  6. y='wheat:Q'
  7. )
  8. # 添加均值线
  9. rule = alt.Chart(source).mark_rule(color='red').encode(
  10. y='mean(wheat):Q'
  11. )
  12. (bar + rule).properties(width=600)
  13. 复制代码

Python数据相关模块 - 图24
Altair 的使用也非常简单,且可以绘制强大的交互式图表,不过需要学习 Vega 或 Vega-lite 绘图语法。

参考