Python-项目入门指南-四-

81 阅读54分钟

Python 项目入门指南(四)

原文:Python Projects for Beginners

协议:CC BY-NC-SA 4.0

十、数据分析介绍

到目前为止,我们已经介绍了足够多的 Python 基础和编程概念,可以向更大更好的东西前进了。本周将全面介绍 Python 必须提供的数据分析库。我们不会像其他专注于这个主题的书那样深入探讨;相反,我们将涵盖足够多的内容,帮助您顺利地分析和解析信息。

我们将了解 Pandas 库以及如何使用表格数据结构,如何使用 BeautifulSoup 进行网页抓取,如何解析数据,以及像 matplotlib 这样的数据可视化库。在周末,我们将一起使用所有这些库来创建一个抓取和分析网站的小项目。

概述

  • 使用 Anaconda 环境和发送请求

  • 学习如何用 Pandas 分析表格数据结构

  • 了解如何使用 matplotlib 显示数据

  • 使用 BeautifulSoup 库从网上搜集数据

  • 创建网站分析工具

挑战问题

假设你是一名数据分析师,你刚刚得到一组数据,显示了所有司机的事故数量、年龄和发动机的大小。你需要想出一种方法来显示这些信息,让它讲述一个故事。通常你会用 x,y,z 坐标创建一个图表;然而,这可能会变得复杂,你没有时间去做。你如何渲染信息,使它仍然被认为是三维的,但你只能使用 x 和 y 轴?

星期一:虚拟环境和请求模块

今天我们将学习所有关于虚拟环境的知识,为什么我们需要它们以及如何使用它们。它们对于我们本周要做的事情是必要的,我们需要下载和导入一些库来使用。我们还将进入请求模块,并简要介绍API

今天的课,我们不会从朱庇特笔记本开始;相反,如果您还没有打开终端,请将光盘放入“python_bootcamp”文件夹。如果你有运行 Jupyter Notebook 的终端,一定要停止它,因为我们需要在终端中写一些命令。

什么是虚拟环境?

Python 虚拟环境本质上是一种工具,允许您将项目依赖关系保存在与其他项目分离的空间中。Python 中的大多数项目需要使用 Python 中默认不包含的模块。现在,您可以简单地将模块(或库)下载到 Python 文件夹中使用;然而,这可能会导致一些问题。假设你正在进行两个独立的项目,其中第一个项目使用 Python 版本 2.7 ,第二个项目使用 Python 版本 3.5 。如果您尝试对两者使用相同的语法,您将会遇到几个问题。相反,您将创建两个独立的虚拟环境,每个项目一个。这样,由于个性化的虚拟环境,两个项目都可以使用正确的依赖项正常运行。

注意

创建虚拟环境时,会出现一个名为“ venv 的文件夹。这是保存您下载的所有库的地方。简单地说,虚拟环境只不过是一个存储其他文件的文件夹。

作为理解虚拟环境的类比,首先想象一下我们自己的星球。现在把它想象成一个充满了草、太阳、云、空气等的环境。在编程的情况下,Python 就像是地球,草地、太阳、云和空气就像是你需要包含在环境中的库。由于 Python 没有包含在它们中,我们将创建一个虚拟环境来存储这些库,以便在需要时可以将它们导入到我们的项目中。如果你想到火星,这将是另一个项目,有一个单独的虚拟环境专门为该计划。

对于第一次看到虚拟环境的人来说,虚拟环境通常是一个难以理解的概念,所以这里有另一个类比。假设你计划了两次假期,一次去海滩,另一次去滑雪。你决定打包两个独立的行李箱,而不是用同一个行李箱装不同的衣服。沙滩装包括泳衣、太阳镜和人字拖。另一个手提箱包括一件夹克、一双滑雪鞋和一双靴子。在下文中,您可以找到此类比中的关系:

  • 度假➤项目

  • 行李箱➤虚拟环境

  • 服装和配饰➤项目依赖/文件

注意

记住从第一章开始,当在终端中工作时,你会在我们输入的命令旁边看到 $ 。在接下来的几节中,我们将在终端内部工作。

皮普是什么?

Pip 是 Python 的标准包管理器。任何时候你需要下载、卸载或者管理一个库或者模块来在你的项目中使用,你可以使用 pip 。从 v3.4 开始,它就包含在 Python 的所有安装中。要检查您的 pip 版本,请在终端中编写以下内容:

$ pip --version

请随意访问 Python 包索引 ( PyPI )来查看您能够下载的所有可能的库。您可以在未来的项目中使用它们中的任何一个。今天,我们将学习如何安装和使用请求模块,但首先,让我们创建并激活一个虚拟环境。

创建虚拟环境

Anaconda 之所以是如此出色的工具,其中一个重要原因是它能够为我们组织虚拟环境。我们将用它来创建我们的第一个虚拟环境。在终端中,键入以下命令:

$ conda create --name data_analysis python=3.7

继续运行该命令。然后,它会问你是否想通过键入“ y 或“ n ”来继续,只需键入“ y ”表示是,然后按回车键。在我们的程序文件中,将在 Anaconda 目录下创建一个文件夹。该文件夹将被命名为" data_analysis。“我们刚刚使用 Python 版本 3.7 创建了我们自己的虚拟环境。为了使用它,我们必须激活它。如果您想使用 Python 的默认虚拟环境系统,可以使用关键字“virtualvenv”如果你感兴趣的话,一定要去看看。为了便于使用,我们将在本章中使用 Conda 的环境。

注意

你可以从任何地方创造康达环境;你不需要被 cd 到一个特定的文件夹。

激活虚拟环境

使用虚拟环境的第二步是激活它。激活一个环境允许计算机从一个单独的可执行文件中执行我们的脚本。默认情况下,我们使用存储在程序目录中的 Python 可执行文件。我们可以通过在终端中输入以下命令来查看可执行文件的路径:

我们需要首先激活 Python shell:

$ python

现在,我们可以通过键入以下几行来查看路径:

>>> import os
>>> import sys
>>> os.path.dirname(sys.executable)

您会注意到路径是 Python 最初安装的默认文件夹。完成后,继续并退出 Python shell。我们将在本节的最后回到这些相同的命令,看看一旦环境被激活,路径是如何改变的。

一旦创造了环境,就不需要再创造了;您可以在需要使用时随时激活它。在您能够将库下载到环境中之前,您必须首先激活它。根据您的操作系统,在终端中编写以下命令:

对于窗口:

$ activate data_analysis

激活环境后,您将看到名称出现在终端左侧的括号中。它将显示如下:

    >>> (data_analysis) C:\Users...

对于 Mac/Linux:

$ source activate data_analysis

与 Windows 一样,激活环境后,您将在目录左侧看到名称:

    >>> (data_analysis)  <User>~/Desktop...

如果你能看到旁边的名字,你已经成功地激活了环境。在我们继续之前,让我们看看我们的可执行文件现在在哪里,方法是在 Python shell 中运行与本节开头相同的命令,以查看可执行文件的路径:

>>> import os
>>> import sys
>>> os.path.dirname(sys.executable)

在运行这些相同的行之后,您会注意到输出了一个不同的路径。这是我们的 Conda 环境的可执行文件,将运行我们的脚本。现在我们可以开始安装我们可能需要使用的任何库或包。

安装软件包

为了将包安装到虚拟环境中,我们将使用 pip 。安装任何软件包的语法都是一样的。它的关键字是 pip 安装,后跟软件包名称。在我们的例子中,我们今天将使用请求包。让我们编写以下命令:

$ pip install requests

继续运行该命令。我们刚刚在我们的环境中安装了请求模块。要确保安装正确,请编写以下命令:

$ conda list

该命令列出了安装在该环境中的所有软件包。您将能够看到我们刚刚下载的 requests 包,以及我们创建环境时最初下载的其他包。

API 和请求模块

请求模块允许我们使用 Python 发出 HTTP 请求。它是进行 API 调用和从外部资源请求信息的标准库。

注意

如果你对 HTTP 请求不熟悉,我建议查看一下 w3schools 1 资源以获取更多信息,因为这本书不是为涵盖网络而设计的。

一个应用程序编程接口 ( API )是一组允许应用程序访问操作系统、应用程序或其他服务的特征或数据的功能和过程。简单来说,API允许我们与其他开发者设计的网页和软件进行交互。假设你需要一些关于房价的数据。你可以利用像 Zillow 和 Trulia 这样的大公司汇集的资源,而不是自己收集所有的信息。为了访问这些信息,您需要调用他们的 API ,这将返回您需要的数据。API让开发人员的生活更加轻松,因为我们可以在我们的项目中使用其他公司创建的数据或工具。

使用请求模块

现在我们已经创建并激活了我们的环境,并且安装了我们将在今天余下的时间里使用的包,我们可以打开 Jupyter Notebook 了。

注意

如果您没有激活环境或者没有安装请求模块,那么您将会收到错误。确保激活环境,并检查是否安装了请求模块。

要了解本课程剩余部分的内容,请从“终端”中的“ python_bootcamp ”文件夹打开 Jupyter 笔记本。打开后,创建一个新文件,并将其重命名为“ Week_10。“接下来,对第一个标题为:“虚拟环境和请求模块”的单元格进行降价我们将开始在那间牢房下面工作。

发送请求

在这一课中,我们将从由 Github 创建的 API 中请求信息。通常,API需要一个密钥来使用它们的服务;然而,我们将使用一个不需要 API 键的函数。首先,我们必须向一个特定的 URL 发送一个请求,它将向我们发送一个响应。该响应将包含我们能够解析的数据。写下以下内容:

1| # sending a request and logging the response code
3| import requests
5| r = requests.get("https://api.github.com/users/Connor-SM")
7| print( r )
8| print( type( r ))

去查查手机。为了使用请求,您必须导入它,这就是我们在第 3 行所做的。接下来,我们在请求对象中使用 get() 方法,以便从我们传入的给定 URL 中请求信息。我们期望得到的数据将是我的 Github 账户的资料信息。请随意将 URL 中的“ Connor-SM ”替换为您自己的个人资料用户名。第一个 print 语句将输出一个响应代码。你该回去了**<回应【200】>**;如果没有,请务必检查您的互联网连接。这个输出让我们知道我们成功地从 Github URL 请求了信息。有关响应代码列表及其含义,请务必访问w3schools2资源。第二个 print 语句将输出变量的类型,这是一个请求对象。所有请求对象都预装了我们可以访问的默认方法和属性。这将允许我们处理收到的数据。

访问响应内容

为了访问我们在响应中得到的数据,我们需要访问请求对象中的内容属性:

# accessing the content that we requested from the URL
data = r.content
print(data)

去查查手机。我们将得到一个字节字符串输出,它带有大量的括号和难以阅读的信息。来自API的响应通常以字符串格式发送,因为字符串是比对象轻得多的数据类型。我们得到的实际响应是以 JSON 格式显示的。JavaScript 对象符号( JSON )格式相当于 Python 字典,是通过请求发送数据的默认格式。下一步是将数据从一个 JSON 格式的字符串转换成一个我们可以解析的字典。

转换响应

幸运的是,requests 对象附带了一个名为 json() 的内置 JSON 转换方法。在我们将响应转换成字典之后,让我们输出所有的键值对:

# converting data from JSON into a Python dictionary and outputting all key-value pairs
data = r.json( )      # converting the data from a string to a dictionary
for k, v in data.items( ):
      print("Key: { } \t Value: { }".format(k, v))
print(data["name"])     # accessing data directly

去查查手机。通过 for 循环实现和简单的 print 语句,现在所有的信息都易于阅读和访问。

传递参数

您执行的大多数 API 调用都需要额外的信息,比如参数或头文件。这些信息由 API 接收,并用于执行特定的任务。让我们这次执行一个调用,同时在 URL 中传递参数,以在 Github 上搜索特定于 Python 的存储库:

# outputting specific key-value pairs from data
r = requests. get("https://api.github.com/search/repositories?q=language:python")
data = r.json( )
print(data["total_count"])    # output the total number of repositories that use python

去查查手机。有几种不同的方法可以通过请求发送参数。在这种情况下,我们将它们直接写入 URL 字符串本身。您也可以在 get 方法中定义它们,如下所示:

>>> requests.get("https://api.github.com/search/repositories",
>>>           params = { 'q' = 'language:python' } )

当通过 URL 发送参数时,用问号分隔 URL 和参数。问号的右边是一组键值对,表示正在传递的参数。对于我们的例子,被传递的参数有一个键“ q ”和一个值“请求+语言:python ”。 Github 上的 API 将获取这些信息,并将使用 Python 的存储库数据返回给我们,因为这是我们在参数中要求的。然而,并不是所有的API都需要参数,就像我们在本课前面的第一个调用一样。为了弄清楚调用一个 API 时需要什么,总是要阅读文档。好的 API 文档非常重要,可以让你的开发者生活更加轻松。

注意

要停止运行虚拟环境,只需在终端中写入" deactivate。“本周每节课前会要求你激活环境。

周一练习

  1. 测试环境:创建一个新的虚拟环境,名为测试。“创建时安装 Python 版本 2.7 而不是当前版本。完成后,通过检查列表,确保它安装了正确的 Python 版本。

  2. JavaScript 库:使用我们上一课中的请求模块和 Github API 链接,计算出 Github 上有多少库使用 JavaScript。

今天是数据分析的一个重要开端。我们不仅讲述了如何使用虚拟环境及其原因,而且还简要介绍了 API 的请求模块。当在一周的剩余时间里使用任何图书馆时,我们都需要激活我们的数据分析虚拟环境。在本周末,我们将讨论网页抓取,这需要我们使用请求模块。

星期二:熊猫

当你需要处理数据时, Pandas 是终极工具。本质上是类固醇的优势。如果你熟悉 SQL 语言,这对你来说会更容易,因为 Pandas 是 Python 和 SQL 的混合体。最终,您将能够以比其他传统方法更有效的方式分析和处理表格数据。

就像昨天的课是如何开始的一样,我们需要将熊猫图书馆安装到我们的虚拟环境中。为了继续今天的课程,请将 cd 放入“ python_bootcamp ”文件夹,并激活环境。我们今天将从候机厅开始。

注意

如果你不记得如何激活环境,回到昨天的课程。

熊猫是什么?

Pandas 是一个在 C 语言中构建的灵活的数据分析库,非常适合处理表格数据。这是目前基于 Python 的数据分析事实上的标准,流利地使用 Pandas 将会对你的生产力和你的简历产生奇迹。这是从零开始回答的最快方法之一。由于是用 C 语言编写的,它在执行计算时提高了速度。Pandas 模块是一个高性能、高效率、高水平的数据分析库。它允许我们处理大量的数据,称为数据帧。

注意

NumPy 是 Python 中科学计算的基础包。它由 C 语言构建,使用多维数组,可以高速执行计算。

熊猫图书馆在很多方面都很有用,你可以做以下任何事情,甚至更多:

  • 计算统计数据并回答关于数据的问题,如每列的平均值、中值、最大值和最小值

  • 查找列之间的相关性

  • 跟踪一个或多个列的分布

  • 在 matplotlib 的帮助下,使用柱状图、柱状图等可视化数据。

  • 清理和过滤数据,无论是缺失的还是不完整的,只需应用用户定义的函数( UDF )或内置函数

  • 将表格数据转换为 Python 以便使用

  • 将数据导出到 CSV、其他文件或数据库中

  • 对可应用于您的分析的新列进行功能设计

无论您需要对数据做什么,Pandas 都是您的终极分析库。

关键术语

以下是我们将在本节中使用的关键术语。请务必仔细阅读并在必要时参考它们:

  • 系列➤一维标记数组,能够保存任何类型的数据

  • 数据框架➤电子表格

  • 轴➤列或行,轴= 0 乘行;轴= 1(按列)

  • 记录➤一行

  • 数据帧或系列对象的➤数据类型

  • 时间序列➤序列使用时间间隔的对象,如按小时跟踪天气

安装熊猫

要安装 Pandas,首先确保您的虚拟环境被激活,然后将以下命令写入终端:

$ pip install pandas

运行该命令后,它应该会安装 Pandas 需要的几个包。如果您想检查并确保您下载了正确的库,只需写出 list 命令。

进口熊猫

为了继续本课的剩余部分,让我们打开并继续我们之前的笔记本文件“ Week_10 ”,并在底部添加一个标有“熊猫”的降价单元格

进口熊猫很简单;但是,在导入库时有一个行业标准:

# importing the pandas library
import pandas as pd          # industry standard name of pd when importing

去查查手机。我们将熊猫作为 pd 导入,因为它更短,更容易引用。

创建数据帧

Pandas 研究的中心对象是 DataFrame,这是一种表格数据结构,包含行和列,就像 Excel 电子表格一样。您可以从 Python 字典或包含表格数据的文件(如 CSV 文件)创建数据框架。让我们从字典中创建我们自己的字典:

 1| # using the from_dict method to convert a dictionary into a Pandas DataFrame
 2| import random
 4| random.seed(3)     # generate same random numbers every time, number used doesn't matter
 6| names = [ "Jess", "Jordan", "Sandy", "Ted", "Barney", "Tyler", "Rebecca" ]
 7| ages = [ random.randint(18, 35) for x in range( len(names) )]
 9| people = { "names" : names, "ages" : ages }
11| df = pd.DataFrame.from_dict(people)
12| print(df)

去查查手机。我们导入了 random 模块,这样我们就可以为第 7 行的人创建随机年龄。在第 4 行使用种子方法会给我们两个相同的随机数。您可以将任何数字作为参数传递给 seed 然而,如果你使用一个不是 3 的数字,你会得到一个不同于这本书的输出。

注意

随机数不是真正的随机;它们遵循特定的算法返回一个数字。

在我们为每个人生成一个姓名和随机年龄的列表后,我们创建了一个名为“ people”的字典。“神奇的事情真的发生在第 11 行,我们用熊猫来创建我们将要使用的数据帧。当它被创建时,它使用键作为列名,值与相应的索引相匹配,这样的名字[0]的年龄[0] 将成为一条记录。您应该输出一个类似于表 10-1 的表格。

表 10-1

从虚假数据创建的数据框架

|   |

年龄

|

名称

| | --- | --- | --- | | Zero | Twenty-five | 系以脚带 | | one | Thirty-five | 约旦 | | Two | Twenty-two | 桑迪 | | three | Twenty-nine | 泰德 | | four | Thirty-three | 大吵大闹 | | five | Twenty | 泰勒(男子名) | | six | Eighteen | 丽贝卡(女子名ˌ寓意迷人的美) |

访问数据

有几种不同的方法可以访问数据帧中的数据。您可以选择按列或按记录进行选择。让我们来看看如何做到这两者。

按列索引

按列访问数据与用键从字典中访问数据是一样的。在第一组括号中,输入您想要访问的列名。如果您想要访问该列中的特定记录,您可以使用第二组带索引的括号:

1| # directly selecting a column in Pandas
2| print( df["ages"] )
3| print( df["ages"][3] )     # select the value of "ages" in the fourth row (0-index based)
5| # print( df[4] )   doesn't work, 4 is not a column name

去查查手机。在第 2 行,我们输出数据的整个年龄列。第二条语句允许我们访问特定单元格中的值。但是要小心,将索引号放在第一组括号中会产生错误,因为第一组括号仅用于列名,而“ 4 不是列。

记录索引

当你需要访问整个记录时,你必须使用 loc 。这允许我们通过索引来指定记录位置。让我们访问整个第一条记录,然后访问该记录中的名称:

# directly selecting a record in Pandas using .loc
print( df.loc[0] )
print( df.loc[0]["names"] )     # selecting the value at record 0 in the "names" column

去查查手机。我们可以看到我们能够输出整个记录。在使用 loc 的情况下,必须首先指定记录索引位置,然后指定列名。

分割数据帧

当您想要访问特定数量的记录时,您必须对数据帧进行切片。分割 Pandas 的工作方式与 Python 列表完全相同,在一组括号内使用开始停止步骤。让我们访问从索引 2 到索引 5 的记录:

# slicing a DataFrame to grab specific records
print( df[2:5] )

去查查手机。这将输出索引 234 处的记录。同样,切片时要小心,因为去掉冒号会导致试图访问列名。

内置方法

当使用熊猫时,这些方法经常被用来使你的生活更容易。可能要花整整一周的时间,仅仅探索熊猫中 DataFrames 支持的内置函数。然而,我们将简单地突出几个有用的,给你一个熊猫开箱即用的想法。

头部( )

当您处理大型数据集时,您通常会希望查看几条记录来了解您正在查看的内容。要查看数据帧中最上面的记录以及列名,可以使用 head() 方法:

# accessing the top 5 records using .head( )
df.head(5)

去查查手机。这将输出前五条记录。传递给该方法的参数是任意的,将从顶部显示您想要的任意多的记录。

尾部( )

要从底部查看给定数量的记录,可以使用 tail() 方法:

# accessing the bottom 3 records using .tail( )
df.tail(3)

去查查手机。这将输出底部的三条记录供我们查看。

按键( )

有时您需要列名。无论您是在制作模块化脚本还是分析您正在处理的数据,使用 keys( ) 方法都将有所帮助:

# accessing the column headers (keys) using the .keys( ) method
headers = df.keys( )
print(headers)

去查查手机。这将在我们的数据帧中输出一个标题名称列表。

。形状

数据帧的形状通过列数来描述记录的数量。检查形状以确保使用适当数量的数据始终很重要:

# checking the shape, which is the number of records and columns
print( df.shape )

去查查手机。我们将得到一个返回的 (7,2) 元组,表示记录和列。

描述( )

描述方法将为您提供所有数字数据的基本分析。您可以查看最小值、最大值、25%、50%、平均值等。,只需在 DataFrame 上调用此方法。这些信息有助于您开始分析,但通常不会回答您正在寻找的那些问题。相反,我们可以使用这种方法作为从哪里开始的指南:

# checking the general statistics of the DataFrame using .describe( ), only works on numerical columns
df.describe( )

去查查手机。请记住,它只会返回数字列类型的信息,这就是为什么我们只看到 ages 列的输出。

排序值( )

当需要根据列信息对数据帧进行排序时,可以使用这种方法。您可以传入一列或多列作为排序依据。当传递多个时,必须将它们作为列名列表传递,其中第一个名称优先:

# sort based on a given column, but keep the DataFrame in tact using sort_values( )
df = df.sort_values("ages")
df.head(5)

去查查手机。在这个单元格中,我们将 df 变量的值重新声明为新排序的 DataFrame。这样我们可以查看所有按年龄排序的人。您也可以传入一个参数来按降序排序。

过滤

让我们来看看如何过滤数据帧以获取满足特定条件的信息。

条件式

我们可以创建一个布尔数据类型的列来表示我们正在检查的条件,而不是过滤掉信息。让我们用当前的数据框架写一个条件,显示 21 岁或以上可以喝酒的人:

# using a conditional to create a true/false column to work with
can_drink = df["ages"] > 21
print(can_drink)

去查查手机。当您想要创建基于布尔数据类型的列时,您需要写出基于整个列的条件。在这里,我们创建了一个 can_drink 变量来存储整个 ages 列的值。它们是真-假值,因为我们创造了我们的条件。我们可以用它来创建另一个列。

系统增强

当您需要过滤掉记录但保留数据帧中的信息时,您需要使用一个称为子集的概念。我们将使用与前面相同的条件,只是这次我们将使用它来过滤掉记录,而不是创建一个真假表示:

# using subsetting to filter out records and keep DataFrame intact
df[ df["ages"] > 21 ]

去查查手机。输出只产生年龄等于或大于 21 岁的记录。我们从上面获取条件,并在访问 df 变量时将它放在括号内。尽管看起来很奇怪,但语法表示如下:

>>> dataframe_variable [ conditional statement to filter records with ]

您也可以编写以下代码来获得相同的结果:

>>> df[ can_drink ]

请记住,can_drink 是 true-false 值的表示,这意味着前面的语句将过滤掉所有值为 false 的记录。

列转换

从 CSV 或数据库导入的原始数据帧中的列很少会是您分析所需的列。您将花费大量的时间,使用一般的计算操作不断地转换列或列组,以产生作为旧列的函数的新列。熊猫对此全力支持,并且做得很有效率。

生成包含数据的新列

要在 DataFrame 中创建一个新列,可以使用与向字典中添加新的键值对相同的语法。让我们创建一列假数据,代表我们数据框架中的人成为我们公司的客户有多长时间了:

1| # generating a new column of fake data for each record in the DataFrame to represent customer tenure
2| random.seed(321)
4| tenure = [ random.randint(0, 10) for x in range( len(df) )]
6| df["tenure"] = tenure         # same as adding a new key-value pair in a dictionary
7| df.head( )

去查查手机。输出将产生一个新的列,该列是为他们的任期创建的随机数。我们能够在第 6 行添加列及其值。在表 10-2 中,你会发现更新的数据帧,按年龄排序。

表 10-2

向数据框架添加新列

|   |

年龄

|

名称

|

任期

| | --- | --- | --- | --- | | six | Eighteen | 丽贝卡(女子名ˌ寓意迷人的美) | four | | five | Twenty | 泰勒(男子名) | six | | Two | Twenty-two | 桑迪 | Two | | Zero | Twenty-five | 系以脚带 | five | | three | Twenty-nine | 泰德 | eight | | four | Thirty-three | 大吵大闹 | seven | | one | Thirty-five | 约旦 | five |

应用( )

基于当前数据添加新列被称为“特征工程”这是数据分析师工作的一大部分。通常,你无法从你收集的数据中回答你的问题。相反,您需要创建对回答问题有用的自己的数据。对于这个例子,让我们试着回答以下问题:“每个顾客属于哪个年龄段?”。你可以看这些人的年龄,并推测他们的年龄组;然而,我们想让它变得更简单。为了容易地回答这个问题,我们将需要一个代表每个客户年龄组的新列。我们可以通过在数据帧上使用应用方法来做到这一点。apply 方法接收每条记录,应用传递的函数,并将返回值设置为新的列数据。让我们来看看:

# feature engineering a new column from known data using a UDF
def ageGroup(age):
      return "Teenager" if age < 21 else "Adult"
df["age_group"] = df["ages"].apply(ageGroup)
df.head(10)

去查查手机。使用 apply 方法,我们能够创建一个新列来轻松回答我们的问题。当添加新的年龄组列时,我们基于年龄组列中的值应用了年龄组函数。然后,它遍历 DataFrame 中的每条记录,并将"青少年或"成人"的返回值设置为新的 age_group 列的值。apply 方法使我们可以很容易地用自己的 UDF 添加新数据。看一下表 10-3 。

表 10-3

特征工程年龄组列

|   |

年龄

|

名称

|

任期

|

年龄组

| | --- | --- | --- | --- | --- | | six | Eighteen | 丽贝卡(女子名ˌ寓意迷人的美) | four | 十几岁的青少年 | | five | Twenty | 泰勒(男子名) | six | 十几岁的青少年 | | Two | Twenty-two | 桑迪 | Two | 成人 | | Zero | Twenty-five | 系以脚带 | five | 成人 | | three | Twenty-nine | 泰德 | eight | 成人 | | four | Thirty-three | 大吵大闹 | seven | 成人 | | one | Thirty-five | 约旦 | five | 成人 |

注意

当需要基于多列应用值时,必须设置 axis = 1。

聚集

原始数据加上转换通常只是故事的一半。您的目标是从数据中提取实际的见解和可操作的结论,这意味着通过聚合函数将数据从潜在的数十亿行缩减为统计数据的摘要。本节假设您对 SQL 和 groupby 函数有所了解。如果你不熟悉 groupby 在 SQL 中的工作方式,请访问 w3schools3获取参考资料。

groupby(群件)

为了将信息浓缩成统计数据的摘要,我们需要使用 Pandas 的 groupby 方法。无论何时将信息分组,都需要使用聚合函数让程序知道如何将信息分组。现在,让我们计算一下在我们的数据框架中每个年龄组有多少记录:

# grouping the records together to count how many records in each group
df.groupby("age_group", as_index=False).count( ).head( )

去查查手机。当使用计数方法将信息分组在一起时,程序将简单地累加属于每个类别的记录数。我们将有两个类别:有五项记录的成人和有两项记录的青少年。我们的 groupby 方法的第一个参数是我们想要分组的列,第二个参数是确保我们不会将索引重置为年龄组列。如果设置为 True,,那么产生的数据帧将使用 age_group 作为每条记录的唯一标识符。

平均值( )

我们不用计算每个类别中有多少条记录,而是使用 mean 方法求出每一列的平均值。我们将基于同一列进行分组:

# grouping the data to see averages of all columns
df.groupby("age_group", as_index=False).mean( ).head( )

去查查手机。使用平均值方法,我们将能够得到所有数值列的平均值。输出结果应该是类似于表 10-4 的数据帧。

表 10-4

按年龄组分组并平均数据

|   |

年龄组

|

年龄

|

任期

| | --- | --- | --- | --- | | Zero | 成人 | Twenty-eight point eight | Five point four | | one | 十几岁的青少年 | Nineteen | Five |

仅仅通过平均信息,我们可以看到成年人倾向于拥有更长的任期。请注意,names 列已被删除。这是因为 groupby 只保存数字数据,因为它不能计算字符串的平均值。

具有多列的 groupby()

当需要按多列分组时,参数必须作为列表传入。列表中的第一项将是数据帧分组所依据的主列。在我们的例子中,让我们看看有多少成年人的任期是五年:

# grouping information by their age group, then by their tenure
df.groupby( [ "age_group", "tenure" ], as_index=False).count( ).head(10)

去查查手机。为了回答这个问题,我们需要首先按照年龄组进行分组,以便将信息浓缩为成年人和青少年。接下来,我们需要根据任期进一步对数据进行分组。这可以让我们看到每个任期内有多少成年人。由于我们没有太多的数据,答案只有两个。我们得出这个结论是因为我们在分组时使用了计数方法。每个年龄组的所有其他任期只有一个客户。

添加记录

要将记录添加到数据帧中,需要访问下一个索引,并以列表结构的形式赋值。在我们的例子中,下一个索引将是 7 。让我们添加一个已经存在于 DataFrame 中的相同行,这样我们就可以看到如何删除下一个单元格中的重复信息:

# adding a record to the bottom of the DataFrame
df.loc[7] = [ 25, "Jess", 2, "Adult" ]    # add a record
df.head(10)

去查查手机。这将在底部添加一条新记录,其数据与索引 0 中的记录相同。您不需要太频繁地添加新记录,但是当时机到来时知道如何做是有帮助的。

drop_duplicates()

您经常会看到带有重复信息的数据,或者只是重复的 id。您必须删除所有重复的记录,因为这会扭曲您的数据,导致不正确的答案。您可以根据单个列或整个记录是否相同来删除重复记录。在我们的例子中,让我们删除基于相似名称的重复项,这将删除我们刚刚添加到数据帧中的记录:

# removing duplicates based on same names
df = df.drop_duplicates( subset="names" )
df.head(10)

去查查手机。这将删除名为“ Jess ”的第二条记录通过将列名传递给 subset 参数,我们可以删除所有同名的重复项。

注意

省略 subset 参数将只删除所有列中具有相同值的重复记录。

熊猫加入

通常,您将不得不组合来自多个不同来源的数据,以获得您的探索或建模所需的实际数据集。Pandas 在设计连接时大量使用 SQL。本节假设对 SQLSQL 连接有所了解。如果您不熟悉 SQL 中的连接的工作方式,请访问 w3schools 4 获取参考资料。

创建第二数据帧

让我们创建一个二级数据框架来代表客户对我们公司的评价。我们将为三个用户创建评级,这样我们就可以看到内部连接和外部连接:

# creating another fake DataFrame to work with, having same names and a new ratings column
ratings = {
       "names" : [ "Jess", "Tyler", "Ted" ],
       "ratings" : [ 10, 9, 6 ]
}
ratings = df.from_dict(ratings)
ratings.head( )

去查查手机。现在我们已经创建了第二个数据帧,我们可以将这两个数据帧连接在一起,就像在 SQL 中将两个表连接在一起一样。

内部连接

每当执行联接时,都需要一个唯一的列来联接数据。在我们的例子中,我们可以使用 names 列将 ratings 数据帧与我们的原始数据帧连接起来。让我们对这两个数据集执行内部连接,以便将用户与其评级联系起来:

# performing an inner join with our df and ratings DataFrames based on names, get data that matches
matched_ratings = df.merge(ratings, on="names", how="inner")
matched_ratings.head( )

去查查手机。我们将得到类似于表 10-5 的输出:

表 10-5

连接数据框架以一起查看客户评级和年龄

|   |

年龄

|

名称

|

任期

|

年龄组

|

等级

| | --- | --- | --- | --- | --- | --- | | Zero | Twenty | 泰勒(男子名) | six | 十几岁的青少年 | nine | | one | Twenty-five | 系以脚带 | five | 成人 | Ten | | Two | Twenty-nine | 泰德 | eight | 成人 | six |

使用 merge 方法,我们能够执行一个连接。通过将 how 参数指定为“ inner ,我们能够返回一个数据帧,其中只包含那些发布评级的记录。与以前相比,我们现在可以利用这些数据做更多的事情。我们可以计算给我们评级的客户的平均年龄,每个年龄组的平均评级等。连接总是有助于将独立的数据帧连接在一起,这在处理数据库时尤其有用。

外部连接

如果我们想要返回所有的记录,但是要连接给出一个记录的人的评分,我们需要执行一个外部连接。这将允许我们保留原始数据帧中的所有记录,同时添加评级列。我们需要指定如何将参数改为外层:

# performing an outer join with our df and ratings DataFrames based on names, get all data
all_ratings = df.merge(ratings, on="names", how="outer")
all_ratings.head( )

去查查手机。这次我们将得到所有七个记录的数据帧;然而,那些没有给出评级的人会被给一个 NaN 作为一个值。这代表的不是一个数字。“一旦我们将这些信息结合起来,我们就可以找出那些给出评价和没有给出评价的人的平均年龄。从营销的角度来看,这将有助于了解谁是目标人群。

数据集管道

数据集管道是一个特定的过程,在这个过程中,我们获取数据,并为我们的模型清理数据,从而能够进行预测。如果您使用的数据集不干净,这可能是一个漫长的过程。不干净的数据集将具有重复记录、到处都是空值或导致不正确预测的未过滤信息。一般流程如下:

  1. 进行探索性分析

    • 在这一步中,您希望非常了解您的数据。记下您一眼看到的内容或您可能想要清理或添加的内容。本质上,您希望对您的数据所能提供的东西有所了解。记下列数、数据类型、异常值、空值和不必要的列。这通常是当您想要绘制出每列数据并推测相关性、非信息特征等的时候。
  2. 数据清理

    • 不适当的清理会导致不良预测和不良数据集。在这里,您需要移除不需要的观察值(如重复值),修复结构错误(如同名但输入错误的列),处理缺失数据,并过滤异常值信息。这是下一步的关键。
  3. 特色工程

    • 创建数据集没有描述的新信息非常重要。如果你对这个主题有所了解,你可以利用自己的专业知识,你可以隔离数据,让你的算法更专注于重要的观察。在这里,您可以将特征工程列归入一个组,添加虚拟变量,删除未使用的特征等。如果您认为数据缺失或者可以根据数据集中的信息创建数据,那么您可以在这里用自己的知识扩展数据集。

现在您已经知道了清理数据集的过程,这将在一天结束时的第一个练习中派上用场。

星期二练习

  1. 加载数据集:进入 www.Kaggle.com ,点击顶栏菜单中的数据集。选择一个你喜欢的数据集,下载到“ python_bootcamp ”文件夹。然后,使用 read_csv 方法将数据集加载到 Pandas DataFrame 中,并显示前五条记录。

  2. 数据集分析:这是一个开放式的练习。对您在练习#1 中选择的数据集进行一些分析。尝试回答这样的问题:

    1. 有多少项记录?

    2. 每一列的数据类型是什么?

    3. 是否有重复的记录或列?

    4. 是否有数据缺失?

    5. 两列或多列之间是否存在相关性?

今天的重点是学习非常重要的 Pandas 库以及如何使用数据框架。我们用了一些小的现实生活中的例子,但在大多数情况下,今天只是了解你可以在熊猫身上做什么。对于周五的项目,我们将使用熊猫来帮助我们分析体育统计数据。

周三:数据可视化

数据可视化是分析师拥有的最强大的工具之一,主要有两个原因。首先,它在指导分析师决定“下一步看什么”方面的能力是无与伦比的。通常,直观显示的是数据中的模式,这些模式不容易通过只看数据帧来辨别。其次,他们是分析师最伟大的沟通工具。专业分析师需要向负责根据数据采取行动的团队展示他们的结果。视觉效果比原始数据更能讲述你的故事。

就像昨天的课程是如何开始的一样,我们需要在我们的虚拟环境中安装一个库。为了继续今天的课程,请将 cd 放入“ python_bootcamp ”文件夹并激活环境。我们今天将从候机厅开始。

图表的类型

知道使用哪种图表对于正确显示数据非常重要。我们今天会看几个图表。但是,以下是一些您想知道的常见图表:

  • 折线图:随时间探索数据

  • 条形图:比较数据类别,跟踪随时间的变化

  • 饼图:探索整体的部分,即分数

  • 散点图:像折线图一样,追踪两个类别之间的相关性

  • 直方图:与条形图无关,显示变量分布

  • 蜡烛图:在金融领域用的很多,就是可以比较一只股票在一段时间内的走势

  • 箱线图:看起来与烛台图表一样,比较最小值、第一个四分位数、中间值、第三个四分位数和最大值

根据您在概念化数据时需要完成的任务,您将能够选择特定类型的图表来描绘您的数据。

安装 Matplotlib

要安装 matplotlib ,首先确保您的虚拟环境被激活,然后将以下命令写入终端:

$ pip install matplotlib

运行该命令后,它应该安装几个 matplotlib 需要的包。如果您想检查并确保您下载了正确的库,只需写出 list 命令。

导入 Matplotlib

为了跟上本课的其余部分,让我们打开并继续我们之前的笔记本文件" Week_10 "并在底部添加一个标有" Matplotlib "的 markdown 单元格

与 Pandas 一样,matplotlib 在您导入库时也有一个行业标准名称:

# importing the matplotlib library from matplotlib import
pyplot as plt          # industry standard name of plt when importing

去查查手机。我们将 pyplot 作为 plt 导入,这样我们就可以引用 matplotlib 提供的许多图表。

线形图

让我们从我们可以创建的最基本的图表开始,折线图:

 1| # creating a line plot using x and y coords
 3| x, y = [ 1600, 1700, 1800, 1900, 2000 ] , [ 0.2, 0.5, 1.1, 2.2, 7.7 ]
 5| plt.plot(x, y)      # creates the line
 7| plt.title("World Population Over Time")
 8| plt.xlabel("Year")
 9| plt.ylabel("Population (billions)")
11| plt.show( )

去查查手机。首先,我们创建我们的 xy 坐标用于绘图。 plot() 方法允许我们绘制一条单线;它只需要传入坐标。第 7、8 和 9 行都是用来定制图表及其外观的。最后,我们使用 show() 方法来呈现图表。你应该输出一个类似图 10-1 的图表。

img/481544_1_En_10_Fig1_HTML.jpg

图 10-1

人口数据单线图

当您想要在图表中添加更多的线条时,只需根据需要应用任意多的 plot()方法。这一次,让我们为每条情节主线添加更多定制:

 1| # creating a line plot with multiple lines
 3| x1, y1 = [ 1600, 1700, 1800, 1900, 2000 ] , [ 0.2, 0.5, 1.1, 2.2, 7.7 ]
 4| x2, y2 = [ 1600, 1700, 1800, 1900, 2000 ] , [ 1, 1, 2, 3, 4 ]
 6| plt.plot(x1, y1, "rx-", label="Actual")      # create a red solid line with x dots
 7| plt.plot(x2, y2, "bo--", label="Fake")      # create a blue dashed line with circle dots
 9| plt.title("World Population Over Time")
10| plt.xlabel("Year")
11| plt.ylabel("Population (billions)")
12| plt.legend( )    # shows labels in best corner
14| plt.show( )

去查查手机。通过添加第二组坐标,我们能够在第 7 行使用 plot() 方法绘制第二条线。我们还使用速记语法指定了这些行应该如何呈现。对于 plot 方法中的第三个参数,我们可以传递一个表示颜色、点符号和线条样式的字符串。最后,我们为每一行添加了一个标签,以便于阅读多行图表,并且我们能够通过调用 legend() 方法来显示它。输出应该如图 10-2 所示。

img/481544_1_En_10_Fig2_HTML.jpg

图 10-2

人口数据的多线图

条形图

当您需要绘制分类数据时,条形图是更好的选择。让我们为选择他们最喜欢的电影类别的人数创建一些假数据,并绘制出来:

 1| # creating a bar plot using x and y coords
 3| num_people, categories = [ 4, 8, 3, 6, 2 ] , [ "Comedy", "Action", "Thriller", "Romance", "Horror" ]
 5| plt.bar(categories, num_people)
 7| plt.title("Favorite Movie Category", fontsize=24)
 8| plt.xlabel("Category", fontsize=16)
 9| plt.ylabel("# of People", fontsize=16)
10| plt.xticks(fontname="Fantasy")
11| plt.yticks(fontname="Fantasy")
13| plt.show( )

去查查手机。创建了要处理的数据后,我们在第 5 行创建了我们的图。使用 bar() 方法,我们能够创建条形图。数字数据必须总是设置在 y 轴上,这就是为什么我们的类别在 x 轴上。我们还向图表添加了几个新的定制。我们可以调整字体大小、要显示的字体,甚至可以调整刻度线显示的大小。你应该渲染一个类似图 10-3 的图表。

img/481544_1_En_10_Fig3_HTML.jpg

图 10-3

电影类别数据的条形图

箱形图

在需要比较一段时间内或不同类别的单个统计数据的情况下,箱线图非常有用。它们的设计类似于烛台图表,您可以在其中查看最小值、最大值、25%四分位数、75%四分位数和中值,这对于显示一段时间内的数据非常有用。对于股票,货币是 y 轴数据,时间是 x 轴数据。对于我们的示例,让我们创建两个单独的组并显示每个组的高度:

 1| # creating a box plot – showing height data for male-female
 3| males, females = [ 72, 68, 65, 77, 73, 71, 69 ] , [ 60, 65, 68, 61, 63, 64 ]
 4| heights = [ males, females ]
 6| plt.figure(figsize=(15, 8))     # makes chart bigger
 7| plt.boxplot(heights)           # takes in list of data, each box is its' own array, heights contains two lists
 9| plt.xticks( [ 1, 2 ] , [ "Male" , "Female " ] )         # sets number of ticks and labels on x-axis
10| plt.title("Height by Gender", fontsize=22)
11| plt.ylabel("Height (inches)", fontsize=14)
12| plt.xlabel("Gender", fontsize=14)
14| plt.show( )

去查查手机。为了在单独的类别中绘制数据,我们需要一个列表列表。在第 4 行,我们声明我们的数据,它包含男性和女性的身高列表。当我们绘制数据时,它会将每个列表放入自己的框中。你会注意到这个数字比平常大得多;我们在第 6 行声明一个新的图形大小。为了呈现图表,我们在第 7 行使用了 boxplot() 方法,并将 heights 作为我们的数据传入。然而,更重要的一行是第 9 行,在这里我们定义了在 x 轴上出现的类别的数量。我们将它们排序为“”然后“”,因为这是它们在第 4 行声明的顺序。图表应呈现如图 10-4 所示。**

**img/481544_1_En_10_Fig4_HTML.jpg

图 10-4

高度数据的箱线图

散点图

如果你熟悉聚类,那么你就会知道散点图的重要性。这些类型的图通过为每组数据绘制一个点来帮助区分不同的组。使用两个特征,像花的高度和宽度,我们可以分类花属于哪个物种。让我们创建一些假数据,并绘制点:

 1| # creating a scatter plot to represent height-weight distribution
 2| from random import randint
 3| random.seed(2)
 5| height = [ randint(58, 78) for x in range(20) ]        # 20 records between 4'10" and 6'6"
 6| weight = [ randint(90, 250) for x in range(20) ]     # 20 records between 90lbs. and 250lbs.
 8| plt.scatter(weight, height)
10| plt.title("Height-Weight Distribution")
11| plt.xlabel("Weight (lbs)")
12| plt.ylabel("Height (inches)")
14| plt.show( )

去查查手机。为了创建一些假数据,我们使用了来自 random 模块的 randint 方法。在这里,我们能够为身高体重列表创建 20 条记录。为了绘制数据,我们使用 scatter() 方法,并向图中添加一些特征。您应该得到如图 10-5 所示的输出。

img/481544_1_En_10_Fig5_HTML.jpg

图 10-5

身高体重数据散点图

柱状图

虽然折线图对于可视化时间序列数据的趋势非常有用,但直方图是可视化分布的王者。通常,您感兴趣的是变量的分布,可视化比一组汇总统计数据提供了更多的信息。首先,让我们看看如何创建直方图:

 1| # creating a histogram to show age data for a fake population
 2| import numpy as np         # import the numpy module to generate data
 3| np.random.seed(5)
 5| ages = [ np.random.normal(loc=40, scale=10) for x in range(1000) ]    # ages distributed around 40
 7| plt.hist(ages, bins=45)         # bins is the number of bars
 9| plt.title("Ages per Population")
10| plt.xlabel("Age")
11| plt.ylabel("# of People")
13| plt.show( )

去查查手机。我们之前提到过 NumPy 模块。在数据科学中,它被用来进行极快的数值计算。熊猫的数据帧建立在 NumPy 阵列之上。然而,对于这个单元格,您只需要知道我们用它来创建围绕给定数字的随机数。我们指定的数字被传递到第 5 行的 loc 参数中。 scale 参数是我们希望随机数相隔多远。当然,它仍然会创建这个范围之外的数字,但它主要是创建 1000 个集中在 40 岁左右的随机数。

为了创建直方图,我们使用了 hist() 方法并传入适当的数据。直方图让我们可以看到一个特定的数据出现了多少次。在我们的例子中,40 岁出现了 60 多次。y 轴代表 x 轴值的频率。参数指定了您在图表上看到多少个条形。你可能在想:垃圾箱越多越好,对吗?错,太多和太少之间总是有一条细微的界线;通常你只需要测试出正确的数字。我们通过添加定制来完成此图表。结果应该如图 10-6 所示。

img/481544_1_En_10_Fig6_HTML.jpg

图 10-6

集中分布的年龄数据直方图

虽然数据是假的,但我们可以从图表中推断出很多信息。我们可以看到可能存在的异常值,大致的年龄范围,等等。

直方图分布的重要性

要了解直方图对理解中心分布如此重要的原因,我们需要创建更多的假数据。然后,我们将绘制两个数据集,看看它们是如何叠加的:

# showing the importance of histogram's display central distribution
florida = [ np.random.normal(loc=60, scale=15) for x in range(1000) ]        # assume numpy is imported
california = [ np.random.normal(loc=35, scale=5) for x in range(1000) ]
# chart 1
plt.hist(florida, bins=45, color="r", alpha=0.4)           # alpha is opacity, making it see through
plt.show( )
# chart 2
plt.hist(california, bins=45, color="b", alpha=0.4)     # alpha is opacity, making it see through
plt.show( )
# chart 3
plt.hist(florida, bins=45, color="r", alpha=0.4)           # alpha is opacity, making it see through
plt.hist(california, bins=45, color="b", alpha=0.4)     # alpha is opacity, making it see through
plt.show( )

去查查手机。因为调用了三个 show 方法,所以我们能够在这个单元格中输出三个不同的直方图。当你看前两个直方图时,它们看起来是一样的。如果不深入研究图表,很难看出数据完全不同。因此,为了正确查看数据,我们输出第三个直方图,两个数据集重叠,如图 10-7 所示。我们现在能够清楚地看到每个数据集中心分布的差异。这在分析数据时很重要。我们将 alpha 设置为 0.4 ,因为它允许我们设置不透明度。数字越大,数据就越可靠。

img/481544_1_En_10_Fig7_HTML.jpg

图 10-7

直方图分布图重要性

注意

当呈现几个图表时, matplotlib 知道如何在运行 s how 方法后通过将图表重置为空来分隔每个绘图,直到那时所有正在绘制的信息都将包含在一个图表中。

保存图表

能够渲染这些图表是美妙的;但是,有时您需要在演示文稿中使用它们。幸运的是, matplotlib 提供了一种方法,可以将我们创建的图表保存到文件中。方法支持许多不同的文件扩展名;最常见的*。jpg"* 就是我们要用的。让我们将一个简单的绘图折线图呈现到本地文件夹中:

 1| # using savefig method to save the chart as a jpg to the local folder
 3| x, y = [ 1600, 1700, 1800, 1900, 2000 ] , [ 0.2, 0.5, 1.1, 2.2, 7.7 ]
 5| plt.plot(x, y, "bo-")       # creates a blue solid line with circle dots
 7| plt.title("World Population Over Time")
 8| plt.xlabel("Year")
 9| plt.ylabel("Population (billions)")
11| plt.savefig("population.jpg")

去查查手机。你会注意到在“ python_bootcamp ”文件夹中有一个名为“【population.jpg】的新图像文件。如果不指定 URL 路径,它会将图像保存在 Jupyter 笔记本文件所在的本地文件夹中。

注意

您可以将图表保存为 PDF 或 PNG 等其他格式。

扁平化多维数据

通常,在数据分析中,您希望尽可能避免 3D 绘图。这并不是因为你想要传达的信息没有包含在结果中,而是有时用其他方式表达一个观点更容易。表现第三维的最好方法之一是使用颜色而不是深度。

例如,假设您有三个数据集需要绘制:身高、体重和年龄。你可以渲染一个 3D 模型,但是那太过分了。相反,你可以像我们之前在散点图上一样渲染身高和体重,并给每个点着色来代表年龄。颜色的第三维现在很容易阅读,而不是试图用 z 轴(深度)来描绘数据。让我们在下面一起创建这个确切的散点图:

 1| # creating a scatter plot to represent height-weight distribution
 2| from random import randint
 3| random.seed(2)
 5| height = [ randint(58, 78) for x in range(20) ]
 6| weight = [ randint(90, 250) for x in range(20)
 7| age = [ randint(18, 65) for x in range(20) ]              # 20 records between 18 and 65 years old
 9| plt.scatter(weight, height, c=age)             # sets the age list to be shown by color
11| plt.title("Height-Weight Distribution")
12| plt.xlabel("Weight (lbs)")
13| plt.ylabel("Height (inches)")
14| plt.colorbar(label="Age")         # adds color bar to right side
16| plt.show( )

去查查手机。通过将代表颜色的参数 c 添加到散点图中,我们可以很容易地以 2D 方式表示三个数据集,如图 10-8 所示。右边的颜色条是通过第 14 行创建的,我们也为它创建了标签。在某些情况下,您确实需要使用 z 轴,比如表示空间数据。然而,在可能的情况下,简单地使用颜色作为第三维度不仅更容易创作,也更容易阅读。

img/481544_1_En_10_Fig8_HTML.jpg

图 10-8

使用颜色作为第三维渲染 3D 绘图

周三练习

  1. 三线图:创建三个随机数据列表,包含 20 个 1 到 10 之间的数字。然后创建一个三行的线图,每个列表一行。给每条线赋予自己的颜色、点符号和线型。

  2. 用户信息:创建一个程序,要求任意数量的用户给出 1 到 5 颗星之间的评级,并在没有更多用户愿意回答时绘制数据的条形图。使用下面的文本作为提问的例子

今天我们学习了数据可视化的重要性,以及如何创建自定义图表来恰当地展示我们的数据。使用 matplotlib 时,有大量的绘图可供选择,每种绘图都有自己的优缺点,在选择绘图类型时,您需要考虑这些优缺点。最后,如果您不能正确地向业务决策者展示数据,那么您收集的所有数据都将被浪费。

周四:网络抓取

你可能以前听说过“网页抓取”这个术语。在像 Python 这样的大多数语言中,web 抓取由两部分组成:发出请求和解析数据。我们需要为第一部分使用请求模块,为第二部分使用一个名为的库。简而言之,您编写的请求数据并解析数据的脚本被称为" scraper。“在今天的课程中,我们将使用这两个库来收集一些数据。

像昨天的课一样,我们需要在虚拟环境中安装一个库。为了继续今天的课程,请将 cd 放入“ python_bootcamp ”文件夹,并激活环境。我们今天将从候机厅开始。

安装美丽的汤

要安装美汤,首先要确保你的虚拟环境被激活,然后将以下命令写入终端:

$ pip install bs4

运行命令后,应该会安装几个美汤需要的包。

进口美丽的汤

为了跟上本课的其余部分,让我们打开并继续我们之前的笔记本文件“ Week_10 ”,并在底部添加一个标有“ Web 抓取”的 markdown 单元格。

我们需要导入请求bs4 库中的 BeautifulSoup 类:

# importing the beautiful soup and requests library
from bs4 import BeautifulSoup
import requests

去查查手机。我们将使用请求模块向给定的 URL 发出请求。当 URL 端点不是一个返回正确格式数据的 API,而是一个呈现 HTML 和 CSS 的网页时,我们得到的响应是该网页的代码。为了解析这段代码,我们将它传递到 BeautifulSoup 对象中,这使得操作和遍历代码变得容易。

请求页面内容

要开始收集数据,让我们向一个只包含一首诗的简单网页发送一个请求:

# performing a request and outputting the status code
page = requests.get("http://www.arthurleej.com/e-love.html")
print(page)

去查查手机。我们会得到一个“ <响应【200】>”的输出。这让我们知道对网页的请求是成功的。为了查看我们收到的响应,我们需要访问页面变量的内容属性:

# outputting the request response content
print(page.content)

去查查手机。这将输出一大串用于编写该网页的所有代码,包括标签、样式、脚本等。正如本书前面提到的,这个 URL 呈现一个网页,所以我们得到的响应是所有代码的字符串。下一步是将响应转换成我们可以处理和解析数据的对象。

用漂亮的汤解析响应

漂亮的 Soup 库附带了许多属性和方法,使我们更容易解析代码。使用这个库,我们可以使代码易于查看、抓取和遍历。我们需要创建一个 BeautifulSoup 对象,将页面内容以及我们想要使用的解析器类型传递给它。在我们的例子中,我们正在处理 HTML 代码,所以我们需要使用 HTML 解析器:

# turning the response into a BeautifulSoup object to extract data
soup = BeautifulSoup(page.content, "html.parser")
print( soup.prettify( ) )

去查查手机。方法将创建一个格式良好的输出供我们查看。这使得我们更容易看到实际编写的代码。由于我们指定的解析器, soup 对象知道如何正确地解析内容。Beautiful Soup 适用于其他语言,但我们将在本书中使用 HTML。既然我们已经将内容转化为可以使用的对象,让我们学习如何从代码中提取数据。

抓取数据

利用美汤提取数据的方法有很多。下面几节将介绍这样做的一些主要方法。本节假定了基本的 HTML 知识。

。查找( )

为了找到代码中的特定元素,我们可以使用 find() 方法。我们传递的参数是我们想要搜索的标签,但是它只会找到第一个实例并返回它。这意味着,如果我们的代码中有四个粗体元素标签,并且我们使用这个方法来查找一个粗体标签,它将只返回找到的第一个粗体元素标签。让我们试一试:

# using the find method to scrape the text within the first bold tag
title = soup.find("b")
print(title)
print( title.get_text( ) )         # extracts all text within element

去查查手机。如果您使用 web 浏览器控制台工具中的 inspector 选项卡查看代码,您将能够看到代码中的第一个粗体标签是这首诗的标题。第一个 print 语句的结果是“ < b > Love < /b > ”,第二个语句只是元素中的文本。我们能够通过使用 get_text() 方法提取文本。

。查找全部( )

为了找到给定元素的所有实例,我们使用了 find_all() 方法。这将返回代码中所有标签的列表。让我们找到代码中所有粗体标签并提取文本:

# get all text within the bold element tag then output each
poem_text = soup.find_all("b")
for text in poem_text:
      print( text.get_text( ) )

去查查手机。如果您使用检查器工具来查看代码,您会注意到所有文本都在粗体标签内。结果是整首诗的输出。

通过属性查找元素

所有的 HTML 元素都有与之相关的属性,无论是样式、id、类等等。,您可以使用 Beautiful Soup 来查找具有特定属性值的元素。让我们从我的个人 Github 页面请求响应,并找到显示我的用户名的元素:

1| # finding an element by specific attribute key-values
3| page = requests.get("https://github.com/Connor-SM")
4| soup = BeautifulSoup(page.content, "html.parser")
6| username = soup.find( "span", attrs={ "class" : "vcard-username" } )       # find first span with this class
8| print(username)       # will show that element has class of vcard-username among others
9| print( username.get_text( ) )

去查查手机。我们向 Github 发送一个请求,并将内容解析成一个 BeautifulSoup 对象进行处理。在第 6 行,我们搜索一个 span 标记元素,它的属性为 class,值为" vcard-username "这将在第 8 行输出整个 span 标记,包括文本、属性和语法。最后,我们提取第 9 行的文本,输出与该页面相关的用户名。

注意

通过属性查找元素也适用于 find_all 方法。您还可以在属性参数中包含多个键值对。

DOM 遍历

本节将介绍如何通过遍历 DOM 层次结构来提取信息。DOM 是文档对象模型的缩写,是网页设计中的一个概念,它描述了浏览器中元素之间的关系和结构。网页上的所有元素都属于三种关系之一:

  1. 亲子

  2. 兄弟姐妹

  3. 祖孙

当您进行 web 抓取时,理解这个概念很重要,因为您可能需要访问特定元素的子元素。子元素引用另一个元素中的所有元素。以下面的 HTML 代码为例:

<body>
     <div>
         <h1>Title</h1>
         <h3>Sub-title</h3>
         <p>Text</p>
     </div>
</body>

在本例中, < div > 元素是 h1h3p 元素的父元素。这三个元素被称为子元素。如果我们想从这个 < div > 元素中提取所有文本,我们可以访问子元素。

注意

在前面的示例中,h1、h3 和 p 元素都是同级元素。body 是 div 元素的父元素,也是 h1、h3 和 p 元素的祖父元素。

因为 DOM 是一个 web 设计概念,所以在本书中会简单介绍一下。如果你想了解更多关于这个主题或 HTML 基础知识的信息,一定要访问 w3schools 5 资源。

访问子属性

幸运的是,当 Beautiful Soup 将页面内容转换为对象时,它会跟踪所有元素的子元素。这允许我们遍历 DOM,并按照我们认为合适的方式解析数据。让我们从前面的诗中抓取并将响应转换成一个 BeautifulSoup 对象:

# traversing through the DOM using Beautiful Soup – using the children attribute
page = requests.get("http://www.arthurleej.com/e-love.html")
soup = BeautifulSoup(page.content, "html.parser")
print(soup.children)       # outputs an iterator object

去查查手机。soup 对象中的子元素存储在一个迭代器中。在下面的练习中,让我们从网页中提取出 title 元素。

了解孩子的类型

在开始之前,我们首先需要了解 BeautifulSoup 对象中的子类型。让我们将迭代器转换成可以循环的元素列表:

# understanding the children within the soup object
for child in list( soup.children ):
      print( type( child ) )

去查查手机。结果,我们会有四个孩子,但只有三种不同的类型:

  • <类‘bs4 . element . doctype’>

    • 一个 Doctype 对象引用了定义所用 HTML 版本的 Docstring。
  • <类‘bs4 . element . navigable string’>

    • 一个字符串对应于一个标签中的一段文本。Beautiful Soup 使用 NavigableString 类来包含这些文本。到目前为止,我们已经使用了 get_text() 方法来提取文本;但是,您也可以使用以下方法提取数据:

      >>> tag.string
      
      

      这产生了可导航字符串类型。

  • <类‘bs4 . element . tag’>

    • 一个标签对象对应于原始文档中的一个 XML 或 HTML 标签。当我们访问元素和它们的文本时,我们将访问原始的标签来这样做。

如果您要输出这些对象中的每一个,您会发现除了 Doctype 之外的所有代码都出现在标签对象中。

访问标签对象

如果我们想访问 title 标签中的文本,我们需要首先遍历它的父标签,这恰好是 head 标签。既然我们知道我们要寻找的元素存在于标签对象中,我们需要将该对象保存到一个变量中,并输出其中的部分:

# accessing the .Tag object which holds the html – trying to access the title tag
html = list( soup.children )[2]
for section in html:
     print("\n\n Start of new section")
     print(section)

去查查手机。当您输出 HTML 变量中的每个部分时,您会发现在第一个索引处有一个空的部分,在 head 元素的位置之前。我们为每个新部分输出 print 语句,以防空字符串占用索引。

访问 Head 元素标签

现在我们知道 head 元素位于 HTML 子元素的索引 1 处,我们可以执行相同的执行来访问 head 中的每个子元素:

# accessing the head element using the children attribute
head = list( html.children )[1]
for item in head:
      print("\n\n New Tag")
      print(item)

去查查手机。当你输出中的每个标签时,你会注意到我们一直在寻找的标题标签位于索引 1

注意

记住,存储在这些变量中的每个对象都是一个迭代器,可以被类型转换成列表。

抓取标题文本

最后一步是从标题标签中提取文本:

# scraping the title text
title = list( head )[1]
print(title.string)    # .string is used to extract text as well
print( type(title.string) )      # results in NavigableString
print( title.get_text( ) )

去查查手机。我们刚刚遍历了 DOM ,以便从我们的 title 元素中抓取文本。

注意

访问对象的子元素的能力允许我们创建模块化或自动化的 web scrapers,它可以执行各种任务。由于大多数网站在其网页上遵循相似的风格,如果我们知道正确的模式,创建一个在单个页面上提取信息的脚本将允许我们在许多其他页面上这样做。例如,名为 baseball-reference 的棒球在线统计数据库保存了 MLB 历史上所有棒球运动员的数据。每个玩家在网站的 URL 上都有一个唯一的标识符。如果您编写了一个解析脚本来提取一个球员的信息,那么您将能够编写一个循环来提取数据库中所有球员的信息。

周四练习

  1. 字数统计:写一个程序,统计以下环节有多少字: www.york.ac.uk/teaching/cws/wws/webpage1.html 。使用 requests 模块和 Beautiful Soup 库提取所有文本。

  2. 问题#2 :使用以下链接,从表格中提取每个体育场名称: https://en.wikipedia.org/wiki/List_of_current_National_Football_League_stadiums 。总共应该有 32 个名字。

今天我们学习了如何通过网络搜集信息。使用请求模块,我们可以接收呈现给定网页的代码响应。然后,我们可以将这个响应转换成一个对象,通过漂亮的 Soup 库轻松解析和提取数据。在明天的课程中,我们将使用这周学过的所有库来分析我们从网上搜集的信息。

星期五:网站分析

今天的项目将包括请求模块、美汤matplotlib 库。这个项目的目标是创建一个脚本,该脚本将接受一个网站来抓取和显示网站中使用的热门词汇。我们将在一个格式良好的条形图中绘制结果,使那些查看数据的人更容易理解。

为了继续今天的课程,请将 cd 放入“ python_bootcamp ”文件夹,并激活环境。我们将从之前的笔记本文件“ Week_10 ”继续,并在底部添加一个减价单元格,内容为“周五项目:网站分析

完工图纸

正如我们每周所做的,我们需要设计出最终的程序应该是什么样子,以及它应该如何运行。出于测试目的,我们将使用微软的主页。最终,我们希望最终的输出看起来如图 10-9 所示。

img/481544_1_En_10_Fig9_HTML.jpg

图 10-9

分析微软主页上最常用的词

我们将让程序不断地询问用户是否想抓取一个网站,然后接受用户对他们想分析的网站的输入。之后,我们可以执行我们的网站;过滤掉所有无用的信息,如冠词、换行符等。;最终能够创建条形图。程序输出应该如下所示:

>>> Would you like to scrape a website (y/n)? y
>>> Enter a website to analyze: https://www.microsoft.com/en-us/
>>> The top word is: Microsoft
>>> *** show bar plot ***
>>> Would you like to scrape a website (y/n)? n
>>> Thanks for analyzing! Come back again!

为了让输出正常工作,我们需要创建一个程序所需步骤的大纲。请花点时间试着自己写出来。我们创建的程序需要执行以下步骤:

  1. 继续问用户他们是否愿意抓取一个网站,直到他们说不。

  2. 询问用户是否愿意对网站进行网络抓取。

    • 如果用户说是

      1. 接受用户关于他们想要抓取的站点的输入。

      2. 向网站发送请求。

      3. 解析请求响应中页面内容的所有文本。

      4. 过滤掉所有非文本元素,如脚本、注释等。

      5. 过滤掉所有的文章单词和无用的字符,如换行符和制表符。

      6. 循环所有剩余的文本,并计算每个单词的频率。

      7. 保留前七个单词,显示最常用的单词。

      8. 创建前七个单词的条形图。

    • 如果用户说不

      1. 退出程序并显示感谢信息。

导入库

我们需要从导入我们将在整个项目中使用的所有库开始。让我们将所有导入放在它们自己的单元格中,这样我们只需要运行一次导入,而不是每次运行程序代码时都导入它们:

1| # import all necessary libraries
2| import requests
3| import matplotlib.pyplot as plt
4| from bs4 import BeautifulSoup
5| from bs4.element import Comment
6| from IPython.display import clear_output

去查查手机。唯一没有见过的新导入是第 5 行的导入。为了只分析页面上出现的单词,我们需要过滤掉程序中某个地方的注释中的所有文本。稍后使用 Comment 类将允许我们识别文本是否在注释中,以便我们可以正确地过滤掉它。

创建主循环

让我们在下一个单元格中编写以下所有代码,这样我们就不必重新运行所有的导入。我们需要创建一个主循环,这样我们就可以继续询问用户是否愿意浏览网站。当他们这样做时,我们将简单地打印他们现在进入的站点:

 1| # main loop should ask if user wants to scrape, then what site to scrape
 2| while input("Would you like to scrape a website (y/n)? ") == "y":
 3|  try:
 4|          clear_output( )
 6|          site = input("Enter a website to analyze: ")
 8|          print(site)       # remove after runs properly
 9|  except:
10|          print("Something went wrong, please try again.")
12| print("Thanks for analyzing! Come back again!")

去查查手机。这给了我们基本的循环结构,要求用户输入他们想要抓取的站点。如果他们选择不刮,那么我们输出一个感谢消息。我们希望将这个循环的主要部分包装在一个 try-except 子句中,因为我们不能期望用户总是输入有效的 URL。如果用户没有输入有效的 URL,就会出错。现在,我们不必担心错误,程序会不断地询问用户是否愿意抓取另一个网站。

注意

每当您重新启动笔记本时,您都需要再次运行导入单元格。

抓取网站

现在我们已经接受了用户的输入,我们需要抓取网站。最好将这段代码从主循环中分离出来,所以让我们将它放在自己的函数中:

 1| # request site and return top 7 most used words
 2| def scrape(site):
 3|  page = requests.get(site)
 5|  soup = BeautifulSoup(page.content, "html.parser")
 7|  print( soup.prettify( ) )    # remove after runs properly
 9| # main loop should...    ◽◽◽
14|           site = input("Enter a website...      ◽◽◽
16|           scrape(site)
17|  except:     ◽◽◽

去查查手机。在我们要求用户输入一个网站后,我们在第 16 行调用 scrape 函数,用站点变量作为参数。然后,该程序将从网站请求内容,并用 BeautifulSoup 解析它。出于测试目的,我们使用*pretify()*方法来输出我们得到的响应。如果仔细查看输出,您会注意到元素标记中有许多文本没有在网站上显示出来。像 scriptscomments 这样的标签包含了我们不希望包含在分析中的文本,所以我们最终需要将它们过滤掉。一旦它们被过滤掉,我们就只剩下出现在网站主页上的实际文本了。一旦单元正常运行,删除第 7 行的代码。

注意

要阅读这本书,请使用微软网址: www.microsoft.com/en-us/

删除所有文本

现在我们收到了响应,我们可以开始解析页面内容中的所有文本:

 1| # request site and return top 7 most used words
 2| def scrape(site):
 3|  page = requests.get(site)
 5|  soup = BeautifulSoup(page.content, "html.parser")
 7|  text = soup.find_all(text=True)     # will get all text within the document
 9|  print(text)     # remove after runs properly
11| # main loop should...    ◽◽◽

去查查手机。我们使用 BeautifulSoup 对象中的 find_all 方法来获取页面中包含的每一段文本。注意,这返回给我们一个列表,其中包含换行符、制表符、脚本、注释以及我们在适当的文本元素中需要的实际文本,如 h1p 等。下一步是过滤掉那些不必要的元素。一旦电池正常运行,移除线 9;这仅用于测试目的。

过滤元件

尽管我们正在从页面内容中解析文本,但是大部分文本包含在我们不想包含在分析中的元素中。让我们以脚本标签为例。脚本标签用于在网页中编写 JavaScript。如果我们在分析中包括这一点,就会导致不恰当的结果。HTML 注释也是如此,如下所示:

<!-- this is an HTML comment -->

网页上看不到 HTML 注释中的任何文本。这和 Python 注释的概念是一样的。它们是供程序员使用的,不被编译器读取。知道我们只想对页面上出现的单词进行分析,我们必须过滤掉这些不必要的元素:

 1| # filter out all elements that do not contain text that appears on site
 2| def filterTags(element):
 3|  if element.parent.name in [ "style", "script", "head", "title", "meta", "[document]" ]:
 4|          return False
 6|  if isinstance(element, Comment):
 7|          return False
 9|  return True
17|  text = soup.find_all(text=True)...     ◽◽◽
19|  visible_text = filter(filterTags, text)
21|  for text in visible_text:
22|          print(text)       # remove after runs properly
24| # main loop should...    ◽◽◽

去查查手机。在我们从第 17 行的页面内容中解析出所有文本后,我们在第 19 行过滤掉不必要的元素。filter 方法用于循环遍历我们的文本变量中的每一项,并应用 filterTags 函数来确定该项是否应该被包含。如果项目不是不应该包含的注释或元素标签,我们基本上想要返回 True 。第 3 行是我们检查文本是否在我们不想包含的元素中的地方。第 3 行列表中包含的所有字符串都是 HTML 元素。但是注释略有不同,因为它们不是元素。要知道一个条目是否是评论,我们需要使用 Beautiful Soup 的评论对象。

注意

当 BeautifulSoup 解析页面内容时,它将 HTML 识别为四个对象之一:Tag、NavigableString、Beautiful Soup 和 Comment。

在第 6 行,我们检查该项是否是注释对象的实例。如果是,我们返回 False ,这样我们就可以过滤掉它。如果该项不是注释或者它的父元素是有效元素,那么我们返回 True 。然后,我们对变量进行循环,在第 21 行输出每一项。我们现在只剩下出现在网页上的单词。你会注意到每个单词之间有很多空格,这是下一步。在电池正常运行后,移除线 22,因为它仅用于测试目的。

过滤废物

下一步是过滤掉任何转义字符(换行符,制表符);冠词,如 a、an、the 等。;以及其他我们认为无用的词。当我们对一个网站进行分析时,我们希望看到最上面的描述性文字。知道一个网站的关键词是“,并不能描述这个网站的任何信息。例如,当抓取一个新闻网站时,我们会期望看到关于当天头条新闻的关键词。为了执行这个过滤,我们需要创建另一个函数来处理删除我们所谓的“浪费”:

 1| filter article words and hidden characters
 2| def filterWaste(word):
 3|  bad_words = ( "the", "a", "in", "of", "to", "you", "\xa0", "and", "at", "on", "for", "from", "is", "that", "his",
 4|                 "are", "be", "-", "as", "&", "they", "with", "how", "was", "her", "him", "i", "has", "|" )
 6|  if word.lower( ) in bad_words:
 7|          return False
 8|  else:
 9|          return True
11| # filter out all elements that do not...    ◽◽◽
31|  for text in visible_text:     ◽◽◽
32|          words = text.replace("\n", "").replace("\t", "").split(" ")       # replace all hidden chars
34|          words = list( filter(filterWaste, words) )
36|          for word in words:
37|                  print(word, end=" ")      # remove after runs properly
39| # main loop should...    ◽◽◽

去查查手机。我们从第 31 行开始这个过程,遍历 visible_text 中的每一项,用空字符串替换所有换行符和制表符。然后,我们用我们的 filterWaste 函数对那个项目运行一个过滤器,看看我们是否需要将它从第 34 行的列表中删除。在 filterWaste 函数中,我们定义了一组我们想要过滤掉的单词或字符,称为 bad_words 。将项目转换成小写后,我们检查它是否存在于 bad_words 中;如果是,我们返回False;否则,我们返回 True 来保持它在列表中。在第 37 行,我们在执行过滤后输出每个单词。这个输出中包含的单词具有足够的描述性和信息性,可以告诉我们网站主要在谈论什么。一旦电池正常运行,移除线 37,因为这仅用于测试目的。

注意

如果愿意,您可以向 bad_words 数据集合中添加更多单词或字符。这只是为了让我们暂时过得去。有一个名为 NLTK 的库,它有一个很大的文章单词和字符列表,必要时你可以在更大的项目中使用。

统计词频

在我们过滤掉所有的浪费和元素后,我们只剩下适当的词来进行分析。下一步是计算一个给定单词出现的次数。字典将是记录每个单词的计数的最佳实践,因为我们可以使用单词作为键,使用频率作为值:

29|   visible_text = filter(filterTags, text)...     ◽◽◽
31|   word_count = { }
33|   for text in visible_text:     ◽◽◽
38|          for word in words:     ◽◽◽
39|                  if word != "":         # if it doesn't equal an empty string
40|                          if word in word_count:
41|                                  word_count[ word ] += 1
42|                          else:
43|                                  word_count[ word ] = 1
45|   print(word_count)      # remove after runs properly
53| # main loop should...    ◽◽◽

去查查手机。在第 31 行,我们创建了字典来记录字数。当我们遍历我们的单词列表中的每个单词时,我们首先检查它是否是一个空字符串,因为我们将所有转义字符都转换为空字符串,当然不想将它们包括在计数中。在第 40 行,我们检查这个单词是否已经被添加到字典中,在这种情况下,我们将把值加 1(第 41 行)。如果它还没有被添加到字典中,那么我们只需添加该单词的一个键和一个值 1 。我们在第 45 行输出结果,以查看单词及其频率。现在我们可以查看所有的单词和它们出现的时间;然而,我们想要标出最热门的单词,所以接下来我们需要对字典进行排序。电池正常运行后,移除管线 45。

按词频对词典排序

为了输出或返回前七个单词,我们需要按值对字典进行排序:

43|                                 word_count[ word ] = 1       ◽◽◽
45|   word_count = sorted(word_count.items( ), key=lambda kv: kv[1], reverse=True)   # sort on value
47|   print( word_count[ :7 ] )     # remove after runs properly
49| # main loop should...    ◽◽◽

去查查手机。为了理解这里发生的事情,首先我们需要澄清的输出是什么。 项()】变成了:

>>> d = { "word" : 1, "hello" : 2 }
>>> result = d.items( )
>>> print(result)
dict_items( [ ("word", 1), ("count", 2) ] )

结果是一个列表中的一对元组。通常,在字典上使用排序的函数会产生一个按键排序的列表;然而,当我们使用 lambda 函数通过改变 key 参数来基于值进行排序时,它实际上是接受这些元组中的每一个,并基于索引 1 进行排序,索引 1 是代表单词频率的值。记住,排序的函数返回一个列表。当我们运行第 45 行时,由于参数“ reverse=True ”,它产生了一个从最高值到最低值排序的元组列表。最后,我们通过切片输出前七个单词。电池正常运行后,移除线 47。

显示顶部的单词

现在我们得到了前七个单词,让我们输出最常用的单词来衡量:

45|   word_count = sorted...     ◽◽◽
47|   return word_count[ :7 ]
49| # main loop should...    ◽◽◽
54|            site = input("Enter a website...      ◽◽◽
56|            top_words = scrape(site)
58|            top_word = top_words[0]    # tuple of (word, count)
60|            print("The top word is: { }".format( top_word[0] ))        # don't remove
61|   except:      ◽◽◽

去查查手机。我们首先从 scrape 函数返回前七个单词,而不是打印出来。这将把元组列表返回到我们的主循环的第 56 行,并将它们保存到 top_words 变量中。之后,我们将第一个元组赋给我们的 top_word 变量,因为它代表页面上使用最频繁的单词。最后,我们通过访问包含单词和频率计数的元组的零索引来输出第 60 行的顶部单词。

绘制结果图表

我们程序中需要执行的最后一步是在柱状图中绘制结果:

 1| # graph results of top 7 words
 2| def displayResults(words, site):
 3|  count = [ item[ 1 ] for item in words ][ : : -1 ]      # reverses order
 4|  word = [ item[ 0 ] for item in words ][ : : -1 ]       # gets word out of reverses order
 6|  plt.figure( figsize=(20, 10) )     # define how large the figure appears
 8|  plt.bar(word, count)
10|  plt.title("Analyzing Top Words from: { }...".format( site[ :50 ] ), fontname="Sans Serif", fontsize=24)
11|  plt.xlabel("Words", fontsize=24)
12|  plt.ylabel("# of Appearances", fontsize=24)
13|  plt.xticks(fontname="Sans Serif", fontsize=20)
14|  plt.yticks(fontname="Sans Serif", fontsize=20)
16|  plt.show( )
77|          print("The top word is...      ◽◽◽
79|          displayResults(top_words, site)     # call to graph
80|  except:      ◽◽◽

去查查手机。我们会得到最后的输出,这是我们在这节课中编程的目标。该图将通过调用第 79 行的 displayResults 函数,在一个格式良好的条形图中显示前七个单词及其频率。我们传入顶词站点的参数,以便为图表提供数据和标题。在第 3 行和第 4 行,我们使用 comprehension 将值和单词分成各自的列表。然后我们用最后的切片来反转它们。否则,它将从最高到最低显示图形。通过将该数据传递给 bar 方法,在第 8 行绘制条形图。最后,我们添加一个标题、标签和一些样式。

最终输出

该程序现在已经完成,可以用来分析任何网站的热门词汇。请注意,一些站点可以并且确实会阻止请求,在这种情况下,将会执行异常。你可以在 Github 资源库中找到本周的所有代码,以及这个项目。

今天我们学习了如何创建一个程序来抓取用户输入的任何网站内容。重要的是看看我们如何一起使用这些数据分析库来创建一个有用的工具。现在,我们可以使用这个 web 工具来分析新闻网站并查看趋势信息。当然,这是一个简单的网页刮刀,但经过适当的修改后,可以成为一个更有用的工具。

每周总结

有许多 Python 库对数据分析很有用。在这一周里,我们讨论了一些业界最广泛使用的模块和库。本周你已经准备好开始学习更多关于分析的知识,以及如何实现这些库来进一步提高你的技能。学习了虚拟环境,你将知道如何使用 Python 库和管理你的包。使用请求模块,我们能够调用 API 并解析页面内容。这个模块允许我们的程序与其他软件通信,以改善用户体验。然而,本周最重要的图书馆之一是熊猫数据分析图书馆。几乎每个领域的数据分析师和科学家都在使用它。它赋予你同时使用 Python 和 SQL 的能力;它的效率极高,使得处理数据库和文件更加容易。这真的是所有的结束,所有的分析库。然而,没有可视化,数据分析是不完整的。使用 matplotlib,我们能够覆盖我们可以使用的各种图表,以及如何有效地展示我们的数据。重要的是要记住,没有适当可视化的数据永远不会产生高质量的结果。我们介绍的最后一个库是用 Beautiful Soup 进行 web 抓取的,这是一个重要的库,有助于理解 Python 中的其他语言,在这里我们能够解析来自页面请求的信息和文本。最后,我们将这四课中的三课结合到一个程序中,创建了一个 web 抓取分析工具。为了进一步学习这个主题,您可以使用 www.elitedatascience.com 或了解数据科学库,例如 NLTKSK-Learn

挑战问题解决方案

正如我们在周三的课程中了解到的,尝试和实现数据的 3D 可视化既困难又耗时。本周的问题是我们如何在表达一个三维图形的同时简化它。在周三课程快结束时,我们已经找到了答案,我们发现我们可以使用颜色作为第三维。这允许我们在二维空间内保存一个图形,但它是三维的。对于那些基于可视化做出决策的人来说,在试图简化数据时,记住这一点很重要。

每周挑战

要测试你的技能,请尝试以下挑战:

  1. 用户输入:正如我们在周五的项目中看到的,有许多文章单词或字符我们想要过滤掉。不幸的是,我们无法跟踪每个站点的所有信息。对于这个挑战,实现一个代码块,询问用户他们想要过滤掉哪些额外的单词或字符,以便他们可以改变显示的单词。

  2. 保存图:实现一个代码块,询问用户是否想要保存文件。如果他们这样做了,一定要问用户他们想给图像起什么名字,并用那个名字保存它。

  3. Pandas 实现:在我们的项目中,不是使用字典来跟踪来自网站的单词,而是在代码中实现 Pandas 来跟踪信息。您应该能够执行 head 或 tail 功能来查看顶部或底部最常用的单词。

  4. 保存数据:实现 Pandas 保存唯一词及其频率后,将信息输出到每个站点的 CSV 文件中。文件名应该代表网站名称,例如,“Microsoft _ frequency _ words . CSV”。

Footnotes 1

www.w3schools.com/tags/ref_httpmethods.asp

  2

www.w3schools.com/tags/ref_httpmessages.asp

  3

www.w3schools.com/sql/sql_groupby.asp

  4

www.w3schools.com/sql/sql_join.asp

  5

www.w3schools.com/js/js_htmldom.asp

 

**