树莓派数据科学教程(全)
一、数据科学导论
数据是关于主题的文字、数字和描述形式的信息的集合。考虑下面的陈述:“狗有四条腿,1.5 米高,有棕色的毛。”这一陈述具有关于狗的三种不同类型的信息(即,数据)。数据“四”和“1.5m”是数值数据,“棕发”是描述性的。了解各种数据类型有助于理解数据、执行有效的分析以及更好地从数据中提取知识。基本上,数据可以分为两种类型。
-
数据
-
质量数据
定量数据只能借助测量而不是通过观察来获得。这可以用数值的形式来表示。定量数据可以进一步分为连续数据和离散数据。精确的整数值是离散数据,而连续数据可以是一个范围内的任何值。定性数据是对受试者特征的描述。通常定性数据可以从观察中获得,无法测量。换句话说,定性数据可以被描述为分类数据,而定量数据可以被称为数值数据。
例如,在前面的陈述中,“棕色毛发”描述了狗的特征,是定性数据,而“四条腿”和“1.5m”是定量数据,分别被归类为离散和连续数据。
数据可以以结构化和非结构化的形式提供。当数据被组织在预定义的数据模型/结构中时,它被称为结构化数据。借助查询语言,结构化数据可以存储在表格格式或关系数据库中。我们也可以将这类数据以 Excel 文件格式存储,如表 1-1 中给出的学生数据库。
表 1-1
结构化数据的一个例子
|学生点名
|
马克斯
|
出席
|
一批
|
性
|
| --- | --- | --- | --- | --- |
| One hundred and eleven thousand four hundred and one | 492/500 | 98% | 2011-2014 | 男性的 |
| One hundred and eleven thousand four hundred and two | 442/500 | 72% | 2011-2014 | 男性的 |
| One hundred and twenty-one thousand five hundred and one | 465/500 | 82% | 2012-2015 | 女性的 |
| One hundred and twenty-one thousand five hundred and two | 452/500 | 87% | 2012-2015 | 男性的 |
大多数人类生成和机器生成的数据都是非结构化数据,例如电子邮件、文档、文本文件、日志文件、文本消息、图像、视频和音频文件、网络和社交媒体上的消息以及来自传感器的数据。这些数据只能通过人工或机器干预才能转换成结构化格式。图 1-1 显示了非结构化数据的各种来源。
图 1-1
非结构化数据的来源
数据类型在数据科学中的重要性
在开始分析数据之前,了解数据类型非常重要,这样您就可以选择合适的分析方法。连续数据的分析不同于分类数据的分析;因此,对两者使用相同的分析方法可能会导致不正确的分析。
例如,在涉及连续数据的统计分析中,确切事件的概率为零,而离散数据的结果可能不同。
您还可以根据数据类型选择可视化工具。例如,连续数据通常用直方图表示,而离散数据可以借助条形图可视化。
数据科学:概述
正如本章开头所讨论的,数据科学只不过是从数据中提取知识或信息。不幸的是,并不是所有的数据都能提供有用的信息。它基于客户需求、假设、数据类型的本质以及用于分析和建模的方法。因此,在对数据进行分析或建模以进行智能决策之前,需要进行一些处理。图 1-2 描述了这些数据科学流程。
图 1-2
数据科学过程
数据要求
为了开发一个数据科学项目,数据科学家首先根据客户/业务需求理解问题,然后定义分析问题的目标。例如,假设一个客户想要分析人们对政府政策的情绪。首先,问题的目标可以设定为“收集人民对政府政策的意见”然后,数据科学家决定支持目标的数据类型和数据资源。对于示例问题,可能的数据是社交媒体数据,包括各种类别的人的文本消息和民意调查,以及关于他们的教育水平、年龄、职业等的信息。在开始数据收集之前,一个好的工作计划对于从各种来源收集数据至关重要。设定目标和工作计划可以减少收集数据所花费的时间,并有助于准备报告。
数据采集
互联网上有许多类型的结构化开放数据,我们称之为二级数据,因为这种数据是由某人收集并构建成某种表格格式的。如果用户想直接从一个来源收集数据,那就叫做原始数据。最初,非结构化数据是通过许多资源收集的,例如移动设备、电子邮件、传感器、摄像头、与人的直接交互、视频文件、音频文件、文本消息、博客等。
数据准备
数据准备是数据科学过程中最重要的部分。准备数据会将数据转换成适当的形式,以便进行知识提取。数据准备阶段有三个步骤。
-
数据处理
-
数据清理
-
数据转换
数据处理
这一步很重要,因为当我们从各种来源导入数据时,需要检查数据的质量。进行这种质量检查是为了确保数据具有正确的数据类型和标准格式,并且变量中没有打字错误或错误。这一步将减少进行分析时的数据问题。此外,在这个阶段,收集的非结构化数据可以以结构化数据的形式进行组织,以便进行分析和可视化。
数据清理
数据处理完成后,需要清理数据,因为数据可能仍有一些错误。这些误差会影响数据中的实际信息。可能的错误如下:
-
复制
-
人为或机器错误
-
缺少值
-
极端值
-
不适当的价值观
复制
在数据库中,一些数据重复多次,导致重复。最好检查并删除重复项,以减少数据分析期间的计算开销。
人为或机器错误
数据由人类或机器从数据源收集。在这个过程中,由于人的疏忽或机器故障,一些错误是不可避免的。避免这类错误的可能解决方案是将变量和值与标准变量和值相匹配。
缺少值
在将非结构化数据转换成结构化形式时,一些行和列可能没有任何值(即,空)。这种误差将导致信息的不连续性,并使其难以可视化。编程语言中有许多内置函数,我们可以用它们来检查数据是否有任何缺失值。
极端值
在统计学中,异常值是与其他观察值显著不同的数据点。异常值可能是因为测量中的可变性,或者它可能表明实验误差;离群值有时会被排除在数据集之外。图 1-3 显示了一个异常数据的例子。异常数据会导致某些类型的模型出现问题,进而影响决策。
图 1-3
异常数据
转换数据
数据转换可以通过使用标准化、最小-最大运算、相关信息等多种方法来完成。
数据可视化
根据用户的要求,可以借助图表、图形等可视化工具对数据进行分析。这些可视化工具帮助人们理解数据集中特定变量的趋势、变化和偏差。可视化技术可以作为探索性数据分析的一部分。
数据分析
借助于数学技术,如统计技术,可以进一步分析这些数据。改进、偏差和变化以数字形式确定。我们还可以通过结合可视化工具和分析技术的结果来生成分析报告。
建模和算法
今天,许多机器学习算法被用来从原始数据中预测有用的信息。例如,神经网络可以用于根据用户之前的行为来识别愿意向孤儿捐赠资金的用户。在这种场景下,可以基于用户的教育、活动、职业、性别等来收集用户之前的行为数据。可以用这些收集的数据来训练神经网络。每当一个新用户的数据被馈入这个模型,它就可以预测这个新用户是否会给出资金。但是,预测的准确性取决于可靠性和训练时使用的数据量。
有许多可用的机器学习算法,例如回归技术、支持向量机(SVM)、神经网络、深度神经网络、递归神经网络等。,可以应用于数据建模。在数据建模之后,可以通过提供来自新用户的数据并开发预测报告来分析模型。
报告生成/决策制定
最后,在可视化工具、数学或统计技术以及模型的帮助下,可以基于分析开发报告。这种报告在许多情况下是有用的,例如预测一个组织、行业、政府等的优势和劣势。报告中的事实和发现可以使决策变得相当容易和明智。此外,分析报告可以根据客户需求使用一些自动化工具自动生成。
数据科学的最新趋势
数据科学中的某些领域正在呈指数增长,因此对数据科学家来说很有吸引力。下面几节将对它们进行讨论。
数据科学中的自动化
在当前的场景中,数据科学仍然需要大量的手动工作,例如数据处理、数据清理和数据转换。这些步骤消耗大量的时间和计算。现代世界需要数据科学流程的自动化,如数据处理、数据清理、数据转换、分析、可视化和报告生成。因此,自动化领域将是数据科学行业的最大需求。
基于人工智能的数据分析师
可以有效地实施人工智能技术和机器学习算法来对数据进行建模。特别地,使用深度神经网络的强化学习来基于数据的变化升级模型的学习。此外,机器学习技术可以用于自动化数据科学项目。
云计算
如今人们使用的数据量呈指数级增长。一些行业每天收集大量数据,因此很难在本地服务器的帮助下进行存储和分析。这使得它在计算和维护方面很昂贵。因此,他们更喜欢云计算,在云计算中,数据可以存储在云服务器上,并可以随时随地进行检索和分析。许多云计算公司在其云服务器上提供数据分析平台。数据处理的发展越快,这个领域就越受关注。
边缘计算
许多小规模行业不需要分析云服务器上的数据,而是需要即时的分析报告。对于这些类型的应用,边缘设备可以是一种可能的解决方案来获取数据,分析数据,并以视觉形式或数字形式立即向用户呈现报告。未来,边缘计算的需求将显著增加。
自然语言处理
自然语言处理(NLP)可用于从网站、电子邮件、服务器、日志文件等中提取非结构化数据。此外,NLP 对于将文本转换成单一数据格式也很有用。例如,我们可以将人们在社交媒体上的信息转换成数据格式。这将是从许多来源收集数据的有力工具,其需求将继续增加。
为什么要在树莓派上使用数据科学?
许多书籍解释了与云计算相关的数据科学中涉及的不同过程。但是在本书中,数据科学的概念将作为使用 Raspberry Pi 的实时应用程序的一部分进行讨论。Raspberry Pi 板可以通过使用通用输入/输出(GPIO)引脚连接到各种传感器,与实时世界进行交互,从而更容易收集实时数据。由于它们的小尺寸和低成本,这些 Raspberry Pi 板的多个节点可以连接成网络,从而实现本地化操作。换句话说,Raspberry Pi 可以用作数据处理和存储的边缘计算设备,更接近用于获取信息的设备,从而克服与云计算相关的缺点。因此,许多数据处理应用程序可以使用这些设备的分布来实施,这些设备可以管理实时数据并在本地运行分析。这本书将帮助你使用 Raspberry Pi 实现实时数据科学应用。
二、Python 编程基础
Python 是一种通用动态编程语言,由荷兰程序员吉多·范·罗苏姆于 1989 年创建。它是数据科学领域最常用的编程语言。因为 Python 比其他语言更容易学习和编写代码,所以它是初学者的最佳选择。Python 的广泛使用也归功于它是免费和开源的。Python 社区开发的大量科学库和包允许数据科学家使用数据密集型实时应用程序。一些领先的组织,如谷歌、Dropbox 和网飞,都在不同程度上使用 Python 来增强他们的软件。在本章中,我们将讨论 Python 在 Windows 操作系统上的安装、不同的 Python IDEs、Python 可用的基本数据类型、控制流语句、Python 函数以及用于数据科学的不同 Python 库。
为什么是 Python?
Python 是数据科学家最喜欢的编程语言,原因如下:
-
它是一种开源编程语言,拥有强大且不断增长的贡献者和用户社区。
-
它的语法比 C、C++和 Java 等其他编程语言更简单。
-
它允许用户执行面向对象的编程。
-
它有一个很大的库集,可用于执行各种任务,如开发网站、构建机器学习应用程序等。
-
它可以用于嵌入式小型硬件设备,如 Raspberry Pi,支持各种应用的实时实施。
Python 安装
Linux 操作系统的大多数发行版都预装了 Python 包,但是在 Windows 操作系统的情况下,它必须单独安装。在 Windows 操作系统上安装 Python 的步骤如下:
图 2-1
Python 的安装向导
-
打开浏览器,进入 Python.org,Python 的官方网站。
-
在该页面上,单击“下载”选项卡,并在出现的页面上下载软件的最新版本。
-
下载完成后,打开安装包。在安装向导中,如图 2-1 所示,选择将 Python 添加到 path 中,这将确保 Python 自动添加到您的系统变量 PATH 中;否则,必须在系统的环境变量设置中手动添加该路径。
-
单击“立即安装”安装软件包。
安装完成后,您可以通过在命令提示符下键入python --version
来验证安装,这将显示系统上安装的 Python 版本。如果它没有显示版本,那么可能是安装或系统路径变量有问题。
请参考官方网站上提供的 Python 文档,以了解下载该软件的附加模块和包的过程。您可以在命令提示符下开始使用 Python,也可以在下一节讨论的各种 ide 中安装一个。
Python IDEs
集成开发环境 (IDE)是一个软件套件,它将开发人员的工具组合到一个图形用户界面(GUI)中,其中包括用于编辑代码以及构建、执行和调试程序的选项。Python 可以使用许多 ide,每种 ide 都有自己的优势。这里讨论一些常用的 ide。
皮查姆
PyCharm IDE 由捷克公司 JetBrains 开发。它是一个跨平台的 IDE,可以在 Windows、macOS 和 Linux 上使用。它提供了代码分析和图形调试器。它还支持使用 Django 进行 web 开发,以及使用 Anaconda 进行数据科学。PyCharm 的一些吸引人的特性是智能代码完成、简单的包管理界面和重构选项,它提供了跨代码中的多行进行更改的能力。
斯派德
Spyder 是用 Python 语言进行科学编程的跨平台 IDE。Spyder 集成了许多科学软件包,包括 NumPy、SciPy、Matplotlib、Pandas、IPython 和其他开源软件。它是在麻省理工学院许可下发布的。
Jupyter 笔记型电脑
Jupyter Notebook 是一个基于网络的交互式计算环境。该笔记本将代码及其输出集成在一个文档中,该文档结合了可视化、文本、数学方程和其他媒体,从而使其适合数据科学应用。
用 IDLE 进行 Python 编程
IDLE 是一个简单的跨平台 IDE,适合教育环境中的初学者。它具有多窗口文本编辑器、带语法高亮显示的 Python shell 和集成调试器等特性。由于这是 Python 自带的默认编辑器,所以让我们看看如何使用 IDLE 执行 Python 代码。
在这种空闲状态下,有两种执行 Python 代码的方法。第一种方式是交互模式,可以直接在 Python shell 中的符号>>>旁边键入代码,如图 2-2 所示。每一行代码都将在你按下回车键后被执行。使用交互模式的缺点是,当您保存代码时,它会与结果一起保存,这意味着您不能将保存的代码用于以后的执行。
图 2-2
在交互模式下运行 Python 代码
第二种方法是在脚本模式下运行代码,您可以打开一个脚本窗口并在那里键入完整的代码,然后可以用一个.py
扩展名保存以供以后使用。要打开脚本文件窗口,请转到顶部的文件菜单,然后单击新建文件。在脚本窗口中,键入同样的两行代码,如图 2-2 所示。图 2-3 显示了带有代码的脚本文件窗口。然后转到文件菜单,单击保存,然后通过指定一个正确的文件名保存程序。确保文件名不以数字开头或与现有 Python 关键字同名。
图 2-3
脚本文件窗口
文件保存后,可以通过转到顶部的运行菜单并单击运行模块来执行脚本。这将执行脚本并在 Python shell 中打印输出,如图 2-4 所示。
图 2-4
脚本文件的输出
Python 注释
在我们开始讨论 Python 数据类型之前,有必要了解一下 Python 中的注释行,因为我们会在代码中经常用到它们。根据您的注释目的,有两种方式来编写注释行。
如果你打算为自己写一个简短的注释,关于代码中的某一行,那么单行注释是最好的选择。这些单行注释可以通过简单地以一个散列字符(#
)开始来创建,并且它们在行尾自动终止。执行代码时,Python 编译器将忽略散列符号之后直到行尾的所有内容。
多行注释旨在向他人解释代码的某个特定方面,可以通过在注释的开头和结尾添加三个单引号('''
)来创建。Python 编译器不会忽略这些注释,如果您的脚本除了注释之外没有其他内容,它们将出现在输出中。这两个注释使用空闲 Python shell 格式进行说明,如下所示:
>>> # This is a comment
>>> "'This is a comment"'
'This is a comment'
Python 数据类型
在编程语言中,数据类型是由变量可以取值的类型定义的。Python 数据类型主要可以分为数值和序列数据类型。属于这两个类别的数据类型将在本节中讨论,并为每个类别提供相关的图示。
数字数据类型
数字数据类型是可以接受数值的标量变量。数字数据类型的类别有int
、float
和complex
。此外,我们将讨论使用布尔变量的bool
数据类型。
(同 Internationalorganizations)国际组织
int
数据类型表示不带小数点的有符号整数。清单 2-1 中的代码显示了整数的数据类型。
a=5
"'print the data type of variable a using type() funcion"'
print("a is of type",type(a))
Output:
a is of type <class 'int'>
Listing 2-1Integer
Data Type
漂浮物
float
数据类型表示浮点数,用小数点分隔整数和小数部分。清单 2-2 中的代码打印浮点值的数据类型。
a = 5.0
print('a is of type',type(a))
Output:
a is of type <class 'float'>
Listing 2-2float Data Type
复杂的
complex
数据类型表示形式为 a + bj 的复数,其中 a 和 b 分别是实部和虚部。数字 a 和 b 可以是整数,也可以是浮点数。清单 2-3 中的代码打印复数的数据类型。
a=3.5+4j
print('a is of type',type(a))
Output :
a is of type <class 'complex'>
Listing 2-3complex Data Type
弯曲件
在 Python 中,布尔变量由True
和False
关键字定义。因为 Python 是区分大小写的,所以关键字True
和False
的首字母必须大写。清单 2-4 展示了bool
数据类型。
a= 8>9
print('a is of type',type(a))
print(a)
Output:
a is of type <class 'bool'>
False
Listing 2-4bool Data Type
布尔值可以用布尔运算符操作,包括and
、or
和not
,如清单 2-5 所示。
a = True
b = False
print(a or b)
Output:
True
Listing 2-5Manipulation of boolean Data Type
数字运算符
表 2-1 总结了 Python 中可应用于数值数据类型的数值运算。
表 2-1
Python 中的数值运算符
|操作员
|
操作
|
| --- | --- |
| ( )
| 圆括号 |
| **
| 指数运算 |
| *
| 增加 |
| /
| 分开 |
| +
| 添加 |
| -
| 减法 |
| %
| 模运算 |
表 2-1 中的操作符按其优先顺序列出。当在代码的特定行中执行多个操作时,执行顺序将根据表 2-1 中的优先顺序。考虑示例 23* + 5 ,其中涉及乘法和加法。由于乘法的优先级高于加法,从表 2-1 中可以看出,乘法运算符(*
)将首先被执行,给出23=6*,然后是加法运算符(+),这将给出 6 + 5 = 11 的最终结果。
序列数据类型
序列数据类型允许在一个变量中存储多个值。五类序列数据类型分别是list
、tuple
、str
、set
和dict
。
目录
列表是数据科学家在 Python 中最常用的数据类型。列表是元素的有序序列。列表中的元素不必是相同的数据类型。列表可以声明为用方括号[]
括起来的逗号分隔的项目。列表是可变的;即列表中元素的值可以改变。列表中的元素从零开始索引,因此列表中的任何元素都可以通过其对应的索引来访问,如清单 2-6 所示。索引应该是整数,索引使用任何其他数据类型都会导致TypeError
。类似地,试图访问列表范围之外的索引将导致IndexError
。
a = [1, 2.5, 5, 3+4j, 3, -2]
print("a is of type",type(a))
"'print the first value in the list"'
print("a[0]=",a[0])
"'print the third value in the list"'
print("a[2]=",a[2])
"' print the values from index 0 to 2"'
print("a[0:3]=",a[0:3])
"'print the values from index 4 till the end of the list"'
print("a[4:]=",a[4:])
"'Change the value at the index 3 to 4"'
a[3]=4
print("a=",a)
"'fractional index leads to TypeError"'
print(a[1.5])
"out of range index leads to IndexError"'
print(a[8])
Output of line 2: a is of type <class 'list'>
Output of line 4: a[0]= 1
Output of line 6: a[2]= 5
Output of line 8: a[0:3]= [1, 2.5, 5]
Output of line 10: a[4:]= [3, -2]
Output of line 13: a= [1, 2.5, 5, 4, 3, -2]
Otuput of line 15: TypeError: list indices must be integers or slices, not float
Output of line 17: IndexError: list index out of range
Listing 2-6Operations in a List
考虑分别存储在变量a
和b
中的两个列表。表 2-2 显示了 Python 提供的一些附加操作,这些操作可以在列表a
和b
上执行。其中一些函数也适用于元组、字符串和集合。
表 2-2
Python 中的列表操作
|功能
|
描述
|
| --- | --- |
| a+b
| 连接两个列表a
和b
|
| a*n
| 将列表a
重复n
次,其中n
是一个整数 |
| len(a)
| 计算列表a
中元素的数量 |
| a.append()
| 将一个元素添加到列表的末尾a
|
| a.remove()
| 从列表a
中删除项目 |
| a.pop()
| 移除并返回列表a
中给定索引处的元素 |
| a.index()
| 返回列表a
中第一个匹配项的索引 |
| a.count()
| 返回列表a
中作为参数传递的项目数 |
| a.sort()
| 按升序排列列表a
中的项目 |
| a.reverse()
| 颠倒列表a
中项目的顺序 |
元组
元组也是像列表一样的有序元素序列,但区别在于元组是不可变的;即,元组中的值不能改变。试图改变元组中某个元素的值会导致TypeError
。通过将不会改变的数据存储为元组,可以确保它们保持写保护。元组可以声明为用圆括号括起来的逗号分隔的项,()
。元组也可以像列表一样被索引,如列表 2-7 中所述。
a = (1, 3, -2, 4, 6)
print("a is of type",type(a))
print("a[3]=",a[3])
a[2] = 5
Output of line 2: a is of type <class 'tuple'>
Output of line 3: a[3]= 4
Output of line 4: TypeError: 'tuple' object does not support item assignment
Listing 2-7Operations in a Tuple
潜艇用热中子反应堆(submarine thermal reactor 的缩写)
str
数据类型代表一个字符串。字符串可以声明为双引号(" "
)内的字符。单引号(' '
)也可以使用,但是由于它们在一些单词中以撇号的形式出现,所以使用双引号可以避免混淆。字符串中的字符的索引方式与列表和元组相同。字符串中两个单词之间的空格也被视为字符。像元组一样,字符串也是不可变的,如清单 2-8 所述。
a = "Hello World!"
print("a is of type",type(a))
print("a[3:7]=",a[3:7]
a[2] = "r"
Output of line 2: a is of type <class 'str'>
Output of line 3: a[3:7]= lo W
Output of line 4: TypeError: 'str' object does not support item assignment
Listing 2-8Operations in a String
设置
集合是项目的无序集合,因此不支持索引。集合由集合大括号{}
内逗号分隔的值定义。集合可用于从序列中删除重复项。清单 2-9 显示了集合中的操作。
a = {1, 2, 3, 2, 4, 1, 3}
print("a is of type",type(a))
print("a=",a)
Output of line 2: a is of type <class 'set'>
Output of line 3: a= {1, 2, 3, 4}
Listing 2-9Operations in a Set
考虑分别存储在变量a
和b
中的两个集合。表 2-3 说明了 Python 支持的可以应用于这两个集合的各种集合操作。
表 2-3
Python 中的集合运算
|功能
|
描述
|
| --- | --- |
| a.union(b)
| 返回新集合中两个集合a
和b
的并集 |
| a.difference(b)
| 将两个集合a
和b
的差作为新集合返回 |
| a.intersection(b)
| 返回两个集合a
和b
的交集作为新集合 |
| a.isdisjoint(b)
| 如果两个集合a
和b
有空交集,则返回True
|
| a.issubset(b)
| 如果a
是b
的子集,则返回True
;即集合a
的所有元素都出现在集合b
中 |
| a.symmetric_difference(b)
| 将两个集合a
和b
之间的对称差作为新集合返回 |
词典
一个dict
表示字典数据类型,这是一个由键值对表示的无序数据集合。字典可以在大括号{}
中定义,每个条目都是一对,形式为{key:value}
。字典针对检索数据进行了优化,其中字典中的特定值可以通过使用其对应的键来检索。换句话说,该键充当该值的索引。键和值可以是任何数据类型。键通常是不可变的,不能在字典中重复,而值可能有重复的条目。试图访问字典中不存在的键将导致KeyError
,如清单 2-10 中所述。
a = {1: 'Hello', 4: 3.6}
print("a is of type", type(a))
print(a[4])
print(a[2])
Output of line 2: a is of type <class 'dict'>
Output of line 3: 3.6
Output of line 4: KeyError: 2
Listing 2-10Operations in a Dictionary
类型变换
类型转换是将任何数据类型的值转换为另一种数据类型的过程。Python 为类型转换提供的函数如下:
-
int()
:将任意数据类型更改为int
数据类型 -
float()
:将任意数据类型更改为float
数据类型 -
tuple()
:将任何数据类型更改为元组 -
list()
:将任何数据类型更改为列表 -
set()
:将任何数据类型更改为集合 -
dict()
:将任何数据类型更改为字典
清单 2-11 展示了其中的一些功能。
a = 2
print(a)
float(a)
b = [2 , 3, -1, 2, 4, 3]
print(tuple(b))
print(set(b))
Output of line 2: 2.0
Output of line 4: (2, 3, -1, 2, 4, 3)
Output of line 5: (2, 3, 4, -1)
Listing 2-11Type Conversion Operations
控制流语句
控制流语句允许根据表达式的值执行一条或一组语句。控制流语句可以分为三类:顺序控制流语句,按照语句出现的顺序执行程序中的语句;决策控制流语句,根据条件是True
还是False
来执行或跳过语句块;以及循环控制流语句,允许多次执行语句块,直到满足终止条件。
如果语句
决策控制流语句类别中的if
控制语句以if
关键字开始,后跟一个条件语句,以冒号结束。条件语句评估一个布尔表达式,只有当布尔表达式评估为True
时,才会执行if
语句中的语句体。if
block 语句以缩进开始,第一条没有缩进的语句标志结束。if
语句的语法如下,清单 2-12 展示了它是如何工作的:
if <expression>:
<statement(s)>
x = 12
y=8
if x > y:
out = "x is greater than y"
print(out)
Output: x is greater than y
Listing 2-12if Statement Operations
if-else 语句
if
语句后面可以跟一个可选的else
语句。如果if
语句中条件语句对应的布尔表达式为True
,则执行if
块中的语句,如果布尔表达式为False
,则执行else
块中的语句。换句话说,if-else
语句提供了一个双向决策过程。if-else
语句的语法如下:
if <expression>:
<statement(s)>
else:
<statement(s)>
清单 2-13 显示了if-else
语句的示例代码。
x = 7
y=9
if x > y:
out = "x is greater than y"
else:
out = "x is less than y"
print(out)
Output:
x is less than y
Listing 2-13if-else Statement Operations
如果... 否则如果...else 语句
if...elif...else
语句可以提供多路决策过程。关键词elif
是else-if
的简称。如果需要从几个可能的选项中进行选择,elif
语句可以和if
语句一起使用。else
语句将最后出现,作为默认动作。以下是if...elif...else
语句的语法,清单 2-14 显示了示例代码:
if <expression>:
<statement(s)>
elif <expression>:
<statement(s)>
elif <expression>:
<statement(s)>
...
else:
<statement(s)>
x = 4
y=4
if x > y:
out = "x is greater than y"
elif x<y:
out = "x is less than y"
else:
out = "x is equal to y"
print(out)
Output:
x is equal to y
Listing 2-14if...elif...else Statement Operations
while 循环
while
和for
循环是循环控制流语句。在一个while
循环中,条件语句中的布尔表达式被求值。只有当布尔表达式为True
时,才会执行while
循环中的语句块。循环块的每次重复被称为循环的一次迭代。每次迭代后检查while
语句中的布尔表达式。循环继续执行,直到表达式变为False
,此时while
循环退出。while
循环的语法如下,清单 2-15 显示了它是如何工作的:
while <expression>:
<statement(s)>
x=0
while x < 4:
print("Hello World!")
x=x+1
Output:
Hello World!
Hello World!
Hello World!
Hello World!
Listing 2-15while Loop Operations
for 循环
for
循环使用一个迭代变量运行,该变量随着每次迭代而递增,并且这种递增一直持续到变量到达循环操作序列的末尾。在每一次迭代中,将获取与迭代变量给定的位置相对应的序列中的项,并使用这些项执行循环中的语句。for
循环的语法如下:
for <iteration_variable> in <sequence>:
<statement(s)>
range()
函数在for
循环中很有用,因为它可以生成一个数字序列,这个序列可以使用for
循环进行迭代。range()
函数的语法是range([start,] stop [,step])
,其中start
表示序列的开始(如果没有指定,从零开始),stop
表示必须生成的数字的最大值(不包括数字本身),而step
表示生成的序列中每两个连续数字之间的差值。起始值和步长值是可选的。range 参数生成的值应该总是整数。清单 2-16 显示了一个for
循环,用于逐个打印字符串中的元素。
x = "Hello"
for i in x:
print(i)
Output:
H
e
l
l
o
Listing 2-16for Loop Operations
清单 2-17 展示了如何使用range()
函数打印一个整数序列。
for i in range(4):
print(i)
Output:
0
1
2
3
Listing 2-17for Loop Operations with range Function
异常处理
异常只是在执行过程中检测到的错误。当程序中出现异常时,执行被终止,从而中断程序的正常流程。通过异常处理,可以向用户提供关于错误的有意义的信息,而不是系统生成的消息。例外可以是内置的,也可以是用户定义的。用户定义的异常是由用户创建的自定义异常,可以使用try...except
语句来完成,如清单 2-18 所示。
while True:
try:
n=int(input("Enter a number"))
print("The number you entered is",n)
break
except ValueError:
print("The number you entered is not
the correct data type")
print("Enter a different number")
Output:
Enter a number 5
The number you have entered is 5
Enter a number3.6
The number you entered is not the correct data type
Enter a different number
Listing 2-18Exception Handling
在清单 2-18 中,当一个变量接收到一个不合适的数据类型的值时,就会出现一个ValueError
异常。如果没有异常发生,即输入的数字是一个整数,则跳过except
程序块,只执行try
程序块。如果在输入不同数据类型的数字时出现异常,则跳过try
块中的其余语句,执行except
块,程序返回到try
块。
功能
函数是 Python 编程中的基本块,当需要在一个程序中多次执行一个语句块时,可以使用函数。可以通过对这一组语句进行分组并给它一个名称来创建函数,这样只需通过这个名称就可以在程序的任何部分调用这些语句,而不是重复整个块。因此,函数可以通过消除冗余代码来减少程序的大小。这些功能可以是内置的,也可以是用户定义的。
Python 解释器有许多内置函数,其中一些我们已经见过,比如print()
、range()
、len()
等。另一方面,Python 使用户能够定义自己的函数,并根据需要使用它们。函数定义的语法如下:
def function_name(parameter1, .... parameter n):
statement(s)
函数名可以包含字母、数字或下划线,但不能以数字开头,也不能与关键字重名。让我们考虑一个简单的函数,它将单个参数作为输入,并计算其平方;参见清单 2-19 。
def sq(a):
b = a * a
print(b)
sq(36)
Output:1296
Listing 2-19Square Functions
让我们来看一个稍微复杂的函数,它计算给定十进制数的二进制表示。
如清单 2-20 所示,计算十进制数的二进制表示所需的五行代码可以用一行用户定义的函数来代替。
import math as mt
def dec2bin(a):
b=' '
while a!=0:
b=b+str(a%2)#concatenation operation
a=math.floor(a/2)
return b[:-1]# reverse the string b
print(int(dec2bin(19))
Output: 10011
Listing 2-20Square Functions
用于数据科学的 Python 库
Python 社区积极参与了许多面向各种应用程序的工具箱的开发。数据科学应用中最常用的一些工具箱有 NumPy、SciPy、Pandas 和 Scikit-Learn。
科学计算的 NumPy 和 SciPy
NumPy 是 Python 中可用的科学计算包。NumPy 支持多维数组、线性代数函数和矩阵。NumPy 数组表示为数据科学家提供了一种有效的数据结构。一个 NumPy 数组被称为一个 ndarray ,它可以使用array()
函数创建。清单 2-21 展示了如何创建 1D 和 2D 数组以及如何索引它们的元素。
'''import the NumPy library'''
import numpy as np
'''creates an 1D array'''
a=np.array([1,2,3,4])
'''print the data type of variable a'''
print(type(a))
'''creates a 2D array'''
a=np.array([[1, 2, 3, 4], [5, 6, 7, 8]])
print(a)
'''print the dimension of the array'''
print(a.ndim)
'''print the number of rows and columns in the array'''
print(a.shape)
'''print the third element in the first row'''
print(a[0,2])
'''print the sliced matrix as per given index'''
print(a[0:2,1:3])
a=np.array([1, 2, 3, 4, 5, 6, 7, 8, 9])
'''reshape the 1 x 9 array into a 3 x 3 array'''
b=a.reshape(3,3))
print(b)
Output of line 6: <class 'numpy.ndarray'>
Output of line 9:
[[1 2 3 4]
[5 6 7 8]]
Output of line 11: 2
Output of line 13: (2, 4)
Output of line 15:3
Output of line 17
[[2 3]
[6 7]]
Output of line 21:
[[1 2 3]
[4 5 6]
[7 8 9]]
Listing 2-21Array Using NumPy
使用sum()
可以计算任意维度数组中元素的总和。可以为数组中的所有元素计算总和,也可以沿着清单 2-22 中所示的一个维度为之前创建的数组b
计算总和。
'''print the sum of elements in array b'''
print(b.sum())
'''print the sum of elements along each column'''
print(b.sum(axis=0))
'''print the sum of elements along each row'''
print(b.sum(axis=1))
Output:
Output of line 2: 45
Output of line 4: array([12,15,18])
Output of line 6: array([6, 15, 18])
Listing 2-22Array Using NumPy
关于数组的另一个重要操作是多维数组的展平。这个过程在许多基于机器学习的应用程序中更常见,可以通过使用flatten()
函数来完成,如下所示:
b.flatten()
Output:
array([1, 2, 3, 4, 5, 6, 7, 8, 9]
flatten()
函数将任意维度的数组转换成一维数组。这也可以使用reshape()
来实现,但是与flatten()
函数不同,在这种情况下,必须指定一维数组的大小。表 2-4 描述了在使用数据分析应用程序时可能会派上用场的其他一些数组操作。
表 2-4
数据分析的 NumPy 函数
|句法
|
描述
|
| --- | --- |
| np.ones()
| 在括号内指定的维度中创建一个 1 的数组。 |
| np.zeros()
| 在括号内指定的维度中创建一个零数组。 |
| np.flip(a,axis)
| 沿给定轴反转数组a
。如果未指定 axis,数组将沿两个维度反转。 |
| np.concatenate(a,b,axis)
| 沿指定轴串联两个数组a
和b
(=0
或1
对应垂直和水平方向)。 |
| np.split(a,n)
| 将数组a
分割成n
个更小的数组。这里的n
可以是任意正整数。 |
| np.where(a==n)
| 给出数组a
中数字n
的索引值。 |
| np.sort(a,axis)
| 沿给定轴对数组a
中的数字进行排序。 |
| np.random.randint(n,size)
| 使用从 0 到数字n
的整数生成给定大小的数组。 |
SciPy 生态系统是基于 Python 的 NumPy 扩展构建的科学计算开源软件集合。它提供了操作和可视化数据的高级命令。这个生态系统的两个主要组件是 SciPy 库和 Matplotlib,前者是数值算法和特定领域工具箱的集合,后者是提供 2D 和 3D 绘图的绘图包。以下语法可用于在代码中导入和使用 SciPy 模块中的任何函数:
from scipy import some_module
some_module.some_function()
根据官方 SciPy 文档,该库被组织成覆盖不同领域的不同子类型,如表 2-5 中所总结的。
表 2-5
SciPY 中的子包
|分装
|
描述
|
| --- | --- |
| cluster
| 聚类算法 |
| constants
| 物理和数学常数 |
| fftpack
| 快速傅立叶变换例程 |
| integrate
| 积分和常微分方程解算器 |
| interpolate
| 插值和平滑样条 |
| io
| 输入和输出 |
| linalg
| 线性代数 |
| ndimage
| n 维图像处理 |
| odr
| 正交距离回归 |
| optimize
| 优化和求根例程 |
| signal
| 信号处理 |
| sparse
| 稀疏矩阵和相关例程 |
| spatial
| 空间数据结构和算法 |
| special
| 特殊功能 |
| stats
| 统计分布和函数 |
sci kit-为机器学习而学习
Scikit-Learn 是一个用于 Python 编程的开源机器学习库,具有各种分类、回归和聚类算法。它旨在与其他 Python 库(如 NumPy 和 SciPy)进行互操作。
用于数据分析的 Pandas
Pandas 是一个快速而强大的开源库,用于 Python 编程中的数据分析和操作。它有一个快速有效的DataFrame
对象,用于集成索引的数据操作。它具有在内存数据结构和不同文件格式(如 CSV、Microsoft Excel 等)之间读写数据的工具。考虑一个名为data.csv
的 CSV 文件,其中包含三名学生在三个科目上的成绩,如图 2-5 所示。清单 2-23 显示了使用 Pandas 读取和访问这些数据的过程。
图 2-5
包含学生成绩数据的 CSV 文件
import pandas as pd
'''reads the file data.csv with read_csv package and the header=None option allows pandas to assign default names to the colums
Consider the data in the above table is typed in a excel sheet and saved as csv file in the following path C:\Python_book\data.csv
'''
d = pd.read_csv("C:\Python_book\data.csv",header=None)
print(type(d))
print(d)
"'print the element common to row1-column2"'
print(d.loc[1,2])
"'print the elements common to rows 1,2 and
columns 1,2"'
d.loc[1:2, 1:2]
Output of line 4:
<class 'pandas.core.frame.DataFrame'>
Output of line 5:
0 1 2 3
0 Roll No Science Maths English
1 RN001 70 76 85
2 RN002 86 98 88
3 RN003 76 65 74
Output of line 7: 76
Output of line 9:
1 2
1 70 76
2 86 98
Listing 2-23Data Modification Using Pandas Functions
同样,还有其他的读取功能如read_excel
、read_sql
、read_html
等。,读取其他格式的文件,每一个读取函数都带有相应的写入函数,如to_csv
、to_excel
、to_sql
、to_html
等。,它允许你把 Pandas 数据帧写成不同的格式。
从传感器收集的大部分实时数据是时间序列数据的形式,即按时间顺序索引的一系列数据。让我们考虑一个数据集,它由澳大利亚墨尔本 10 年(1981 年到 1990 年)的最低日气温(摄氏度)组成。数据来源是澳大利亚气象局。尽管这也是一个 CSV 文件,但与上图中的DataFrame
不同,它是时间序列数据。清单 2-24 展示了探索时序数据的不同方法。
Series=pd.read_csv('daily-min-
temperatures.csv',header=0, index_col=0)
"'prints first 5 data from the top of the series"'
print(series.head(5))
"'prints the number of entries in the series"'
print(series.size)
print(series.describe())
"'describe() function creates 7 descriptive statistics of the time series data including mean, standard deviation, median, minimum, and maximum of the observations"'
Output of line 3:
Date Temp
1981-01-01 20.7
1981-01-02 17.9
1981-01-03 18.8
1981-01-04 14.6
1981-01-05 15.8
Output of line 5: 3650
Output of line 6:
Temp
count 3650.000000
mean 11.177753
std 4.071837
min 0.000000
25% 8.300000
50% 11.000000
75% 14.000000
max 26.300000
Listing 2-24Data Modification in Pandas
用于机器学习的张量流
TensorFlow 是 Google Brain 团队打造的机器学习端到端开源平台。TensorFlow 有很多机器学习模型和算法。它使用 Python 提供一个前端 API,用框架构建应用程序。Keras 是一个运行在 TensorFlow 之上的高级神经网络 API。Keras 允许简单快速的原型制作,并支持卷积网络和递归神经网络。
三、树莓派简介
Raspberry Pi,简称 Pi,是由英国 Raspberry Pi 基金会发明的一系列小型、低成本、单板计算机,旨在向世界各地的学生推广基础计算机科学和电子技术。学生和技术爱好者使用 Raspberry Pi 学习编程概念,构建硬件项目和机器人,以及制作人工智能项目。它也用于工业应用。
你能用覆盆子酱做什么?
一个 Raspberry Pi 板可以做几乎所有台式电脑可以做的事情:上网冲浪,观看高清视频,听音乐,查看和编辑图片,进行文字处理,制作电子表格和演示文稿,编写和编译代码,参加视频会议,甚至玩游戏。
使用 Raspberry Pi 进行物理计算
树莓皮也可以用来与物理世界互动。这是通过 Raspberry Pi 板上的通用输入/输出(GPIO)引脚实现的。这使得 Raspberry Pi 功能强大,因为它可以与传感器和其他电气和电子组件连接,如 led、伺服和步进电机、继电器等。
树莓派怎么编程?
Raspberry Pi 附带了两种预装语言(Scratch 和 Python),但它也支持其他语言。Scratch 是面向儿童的可视化编程语言,而 Python 是高级通用编程语言;这两种语言都很容易学。如果你学习用 Python 编程,那么你可以用 Raspberry Pi 做任何可能的事情。
Raspberry Pi 硬件
树莓派基金会于 2012 年发布了首款树莓派——树莓派 B 型。在那之后发布了许多改进的版本,我们将在后面查看所有这些版本。最新版本是 2019 年 6 月发布的树莓 Pi 4 model B。图 3-1 显示了带有许多 I/O 端口的 Raspberry Pi 板的俯视图。我们先来看看它的硬件规格和其他特性。
图 3-1
Raspberry Pi 硬件
片上系统
图 3-2 所示的片上系统 (SoC),是树莓派的大脑。这个小芯片由许多重要部分组成:中央处理器(CPU)、图形处理器(GPU)和数字信号处理器。
图 3-2
片上系统
树莓 Pi 4 model B 拥有强大的 Broadcom BCM2711 (1.5 GHz 64 位四核)SoC。Pi 的 CPU 执行基本算术、逻辑、控制和输入/输出等操作,而 Pi 的 GPU 用于处理多媒体任务,如数字图像处理、绘制 3D 图形和玩游戏。
树莓皮公羊
随机存取存储器(RAM)是树莓 Pi 4 model B 中位于 SoC 旁边的黑色矩形,如图 3-3 所示。在以前版本的 Raspberry Pi 中,RAM 封装在 SoC 内部。Pi 4 提供了三种 LPDDR4 RAM 选择:1GB、2GB 和 4GB。
图 3-3
树莓皮公羊
RAM 存储应用程序使用的短期数据,当 Raspberry Pi 关闭时,这些数据将被删除。RAM 由中央处理单元和图形处理单元共享。
连通性
树莓 Pi 4 型号 B 具有板载 Wi-Fi、蓝牙和千兆以太网。这些功能对于远程访问 Raspberry Pi 非常方便,使其成为物联网(IoT)项目的理想硬件选择。这也释放了 USB 端口和 GPIO 引脚,用于连接外部 Wi-Fi 和蓝牙模块。
设置树莓 Pi
本节解释如何设置树莓 Pi。
微型存储卡
与台式机和笔记本电脑不同,Raspberry Pi 使用 microSD 存储卡来存储文件、应用程序甚至操作系统。与硬盘相比,microSD 存储卡体积较小,易于使用。Pi 至少需要 8GB 的内存。对于数据科学项目,建议使用 16GB 或 32GB 的 microSD 存储卡。建议使用 10 级超高速(UHS)存储卡,以便更快地读取/写入数据。
安装操作系统
Raspbian 是树莓 Pi 基金会发布的树莓 Pi 的官方和最常用的操作系统。使用 Raspberry Pi Imager 软件可以很容易地将其安装在 microSD 卡上,如图 3-4 所示。树莓派还支持其他操作系统,如 Ubuntu 和 Windows 10 IOT 核心。
图 3-4
Raspberry Pi 成像仪软件的界面
按照以下说明在您的 Raspberry Pi 中安装 Raspbian 操作系统:
-
访问 Raspberry Pi 网站的下载页面,并在您的操作系统上下载 Raspberry Pi 成像仪软件。
-
下载完成后,通过单击启动安装程序。
-
将 microSD 存储卡插入电脑。请务必备份卡中的任何重要数据,因为存储在卡中的任何数据都将被格式化。
-
选择您想要安装的 Raspbian 或其他所需的操作系统,以及您想要安装的 microSD 卡。
-
最后,单击 Write 按钮,等待操作完成。
插入 microSD 存储卡
树莓 Pi 底面的细金属槽,如图 3-5 所示,是 microSD 存储卡槽。一旦操作系统安装在 microSD 存储卡上,将其插入 Raspberry Pi 的存储卡插槽。
图 3-5
MicroSD 卡插槽
由于操作系统与其他文件一起存储在 microSD 存储卡上,这使得 Pi 的存储器可移植。microSD 存储卡可以插入新的 Raspberry Pi,它会像魔咒一样工作。
连接键盘和鼠标
图 3-6 显示了 Raspberry Pi 引脚的 USB 端口。树莓 Pi 4 型号 B 有两个 USB 2.0 端口(黑色)和两个通用串行总线(USB) 3.0 端口(蓝色)。USB 可用于连接键盘、鼠标、网络摄像头和其他 USB 外围设备。USB 3.0 端口比 USB 2.0 端口快大约 10 倍。通常,键盘和鼠标等外围设备连接到 USB 2.0 端口,而更快的 USB 3.0 端口则用于硬盘和网络摄像头等设备。
图 3-6
USB 连接埠
如果您有无线键盘和鼠标而不是有线组合,可以通过将 USB 加密狗连接到两个黑色端口中的一个来将它们连接到 Raspberry Pi。这也释放了一个 USB 端口,可用于连接其他设备。
连接显示器
Raspberry Pi 可以通过图 3-7 所示的 micro-HDMI 端口连接到显示器。HDMI 代表高清多媒体接口,Raspberry Pi 从该端口提供组合音频和视频输出。Raspberry Pi model 4 带有两个支持 4K 的微型 HDMI 端口,这意味着您可以同时将两个 4K 显示器连接到 Raspberry Pi。
图 3-7
HDMI 端口
如果您的电视或显示器支持 HDMI 输入,那么您将需要一根微型 HDMI 转 HDMI 电缆来将 Raspberry Pi 连接到您的电视或显示器。旧版本的 Raspberry Pi 带有一个 HDMI 端口。如果您的电视或显示器有 VGA 输入,那么您需要使用微型 HDMI-VGA 适配器将其连接到 Raspberry Pi。同样,您可以将 HDMI 转 DVI 电缆用于带有 DVI 输入的显示器。
为树莓派供电
树莓派 4 B 需要通过 5.1V DC USB-C 型连接器供电,如图 3-8 所示,最小电流输入为 3A。它也可以通过 GPIO 接头供电。USB-C 型电源端口位于树莓派的一角附近。没有一款 Raspberry Pi 机型有开/关开关;一旦你将树莓派连接到电源,它就会打开。
图 3-8
USB-C 型连接器
提供不正确的电压或不足的电流会导致树莓皮损坏;因此,建议使用官方的 Raspberry Pi 电源。
树莓皮围栏
Raspberry Pi 需要封装在一个盒子里,以防止裸露的连接和 GPIO 头。树莓派有多种外壳案例可供选择,或者您可以自己制作案例,但建议使用树莓派基金会发布的官方案例。还提供带冷却风扇的机箱。它们可用于防止 Pi 在运行重型应用程序时过热。
树莓 Pi 版本
本节解释了不同的版本。
树莓 Pi 1
树莓 Pi B 是树莓 Pi 基金会在 2012 年推出的第一款机型,随后在 2013 年推出了 Pi A。它们有 26 个 GPIO 引脚,700MHz 处理器,256MB/512MB 内存,没有任何内置 Wi-Fi 或蓝牙。2014 年发布了 40 个 GPIO 引脚的紧凑型 Pi A+和改进的 B+型号。
树莓 Pi 2
树莓 Pi 2 于 2015 年发布,采用改进的 900MHz 四核处理器和 1GB RAM。这款机型有 40 个 GPIO 引脚,没有内置 Wi-Fi 或蓝牙。它有四个 USB 2.0 端口、一个以太网端口和一个 HDMI 端口。
树莓 Pi 3
2016 年,树莓 Pi 3 发布。它有一个 1.2GHz 的四核处理器和 1GB 内存。这个型号有 40 个 GPIO 引脚,这是第一个内置 Wi-Fi 和蓝牙的 Raspberry Pi 型号。类似于 Raspberry Pi 2,它有四个 USB 2.0 端口,一个以太网端口和一个 HDMI 端口。2018 年晚些时候,推出了紧凑型 Pi 3 A+和改进的 Pi 3 B+车型。
树莓派零度(W/WH)
2015 年推出了小尺寸、低成本、GPIO 引脚更少的 Raspberry Pi Zero。Pi Zero W 于 2017 年发布,内置 Wi-Fi 和蓝牙。接下来是预焊 GPIO 头附带的 Pi 零 WH。
树莓 Pi 4
树莓 Pi 4 model B 于 2019 年发布,拥有强大的 1.5GHz 四核处理器和 1GB/2GB/4GB RAM 选项。这是第一款带有双 4K 显示输出、USB-C 型电源输入和两个 USB 3.0 端口的型号。
推荐树莓 Pi 版本
有不同版本的 Raspberry Pi 可用,但建议数据科学项目使用 Raspberry Pi 4,因为它比其他版本更强大,并且还提供高达 4GB 的 RAM 选项。
Raspberry Pi Zero WH 是可用的 Raspberry Pi 的最小变体,当单板计算机的尺寸需要很小时,建议使用它。但它配备了相对较慢的处理器、更少的 RAM 和更少的 GPIO 引脚。
Raspberry Pi 与传感器的接口
本节重点介绍如何将 Raspberry Pi 与传感器连接起来。
GPIO 引脚
图 3-9 所示的 GPIO 引脚是 Raspberry Pi 最强大的功能之一。GPIO 引脚是沿着电路板边缘的一排小引脚。最近发布的所有 Raspberry Pi 版本都有一个 40 引脚 GPIO 头。这些图钉是树莓派和现实世界之间的联系。GPIO 引脚可以在软件中指定为输入或输出,并可用于各种目的,如打开/关闭 led、控制伺服电机以及从传感器获取数据。它们可以用 Python 或任何其他语言编程,如 Scratch 或 C/C++。
图 3-9
Raspberry Pi GPIO 引脚
GPO 皮诺曹
在连接到 Raspberry Pi GPIO 引脚之前,我们需要知道 GPIO 引脚参考。引脚排列配置不打印在 Raspberry Pi 上,但是我们可以通过打开终端窗口并键入命令pinout
来获取任何 Raspberry Pi 的引脚排列参考。这个工具是由gpiozero
库提供的,预装在 Raspbian OS 上。
GPIO 输出
树莓 Pi 有两个 5V 管脚和两个 3V3 管脚;它还有 8 个无法配置的接地引脚(0V)。其余 28 个引脚都是通用 3V3 引脚。这些引脚的输出设置为 3V3,或者可以接收最高 3V3 的输入。指定为输出引脚的 GPIO 引脚可以设置为高电平(3V3)或低电平(0V)。
用 Python 控制 GPIO 输出
使用 Python 的gpiozero
库可以轻松控制 GPIO 引脚。让我们看一个简单的 Python 例子,演示如何打开/关闭连接到 GPIO 引脚的 led。led 总是需要通过一个电阻连接到 GPIO 引脚。电阻器将确保只有小电流在电路中流动;因此,树莓派或 LED 将受到保护免受损坏。
我们将通过一个 330ω的电阻将一个 LED 连接到 GPIO 引脚 17,如图 3-10 所示。现在,可以使用清单 3-1 中给出的 Python 代码让 LED 持续打开和关闭。led.on()
功能打开 LED,led.off()
功能关闭 LED。
图 3-10
将 LED 连接到 GPIO 引脚
from gpiozero import LED
from time import sleep
led = LED(17)
while True:
led.on()
sleep(1)
led.off()
sleep(1)
Listing 3-1LED Function Using GPIO
GPIO 输入信号
指定为输入引脚的 GPIO 引脚可以读取为高电平(3V3)或低电平(0V)。这意味着 GPIO 引脚不支持模拟输入,只能接收数字输入。虽然 Raspberry Pi 中没有模数转换器硬件,但我们可以使用 MCP3008 等外部 ADC 从传感器读取模拟数据。
用 Python 读取 GPIO 输入
通过将传感器连接到 GPIO 引脚,可以轻松地与 Raspberry Pi 接口。通过将传感器的 VCC 连接到树莓 Pi 的 3.3V/5V,并将传感器的 GND 连接到树莓 Pi 的 GND,可以为传感器供电。传感器的数字输出可以直接连接到 GPIO 引脚并读取。但在读取模拟输出时,模数转换器需要将模拟传感器与 Raspberry Pi 接口。
来自传感器的数字信号
Raspberry Pi 将任何低于 1.8V 的输入视为低电平(0),将任何高于 1.8V 的输入视为高电平(1),如图 3-11 所示。使用InputDevice.value
功能可以轻松读取任何传感器的数字输出数据。该函数返回给定 GPIO 引脚的当前状态。
图 3-11
低和高输入
清单 3-2 中的代码每秒打印一次 GPIO 管脚 17 的状态。
from gpiozero import InputDevice
from time import sleep
sensor = InputDevice(17, pull_up=True)
while True:
print(sensor.value)
sleep(1)
Listing 3-2State of GPIO
来自传感器的模拟信号
图 3-12 显示了一个模拟信号。要从传感器或其它器件读取模拟信号,我们应该使用一个模数转换器,如用于 Raspberry Pi 的 MCP3008。ADC 将模拟信号转换成数字信号。串行外设接口(SPI)协议用于将 ADC 的输出传输至 Raspberry Pi。
图 3-12
模拟信号
要启用 SPI 通信,请从主菜单打开 Raspberry Pi 配置,并在接口选项卡上启用 SPI。MCP3008 是一款 10 位 ADC,有 8 个输入通道(0–7)。让我们将模拟输入连接到 MCP3008 的第一个通道(0)和 MCP3008 的其他引脚,如图 3-13 所示。
图 3-13
10 位模数转换器 MCP3008
清单 3-3 中的代码每秒打印连接到 MCP3008 第一通道(0)的传感器的模拟值。由于 MCP3008 是一个 10 位 ADC,输出值范围为 0 至 1023。
from gpiozero import MCP3008
from time import sleep
sensor = MCP3008(0)
while True:
print(sensor.value)
sleep(1)
Listing 3-3Implement the MCP3008
将超声波传感器与 Raspberry Pi 接口
超声波传感器通过发现声波的时间来测量物体的距离。HC-SR04 超声波传感器可用于测量 2 厘米至 400 厘米的距离,精度为 3 毫米。超声波传感器通过发出频率为 40kHz 的声波来工作,这一频率高于人类的听觉范围,并通过空气传播。如果有障碍物或物体,声波会反弹回传感器。物体的距离可以通过传播时间的一半乘以声速来计算。图 3-14 显示了超声波传感器及其引脚,其中 VCC 引脚需要连接到树莓 Pi 的正极端子,GND 引脚可以连接到树莓 Pi 的 GND 引脚,Trig 引脚用于触发超声波脉冲,Echo 引脚在接收到反射声波时产生脉冲。
图 3-14
超声波传感器引脚
将超声波距离传感器连接到树莓皮上,如图 3-15 所示。
图 3-15
带 Raspberry Pi GPIO 引脚的超声波传感器
gpiozero
库中有一个名为DistanceSensor
的对象,可以使用 Python 中的超声波传感器来测量距离。距离函数返回超声波距离传感器测量的距离,单位为米。让我们将该值乘以 100,将其转换为厘米。清单 3-4 中的代码连续打印超声波距离传感器每秒测量的距离,单位为厘米。
from gpiozero import DistanceSensor
from time import sleep
sensor = DistanceSensor(echo=17, trigger=4)
while True:
print(sensor.distance * 100)
sleep(1)
Listing 3-4Code for calculting distance measured by the Ultrasonic Sensor
当代码运行时,移动放置在超声波传感器前面的物体的位置以获得不同的值。
将温度和湿度传感器与 Raspberry Pi 接口
顾名思义,这些传感器可以用来测量温度和湿度。它们由一个电容式湿度感测元件和一个用于感测温度的热敏电阻组成。温度和湿度传感器有一个专用的电阻式湿度测量组件,称为负温度系数 (NTC)温度测量组件,以及一个 8 位微控制器,以串行数据的形式输出温度和湿度值。单总线数据格式用于 Raspberry Pi 和 DHT11 传感器之间的通信和同步。
DHT 11 和 DHT 22 是常用的温度和湿度传感器。图 3-16 显示了温度和湿度传感器(THD ),图 3-17 解释了 THD 与 Raspberry Pi 的接口,其中 VCC 引脚需要连接到 Raspberry Pi 的正极端子,GND 引脚可以连接到 Raspberry Pi 的 GND,信号/数据引脚用于串行通信,需要连接到 GPIO 引脚。
图 3-16
温度和湿度传感器
将 DHT 11/22 传感器模块连接到 Raspberry Pi,如图 3-17 所示。
图 3-17
带 Raspberry Pi GPIO 引脚的温度和湿度传感器
让我们使用Adafruit_DHT
库从传感器获取温度和湿度值。清单 3-5 中的代码连续打印摄氏温度和湿度百分比。
import Adafruit_DHT
import time
DHT_SENSOR = Adafruit_DHT.DHT11
DHT_PIN = 17
while True:
humidity, temperature =
Adafruit_DHT.read(DHT_SENSOR, DHT_PIN)
if humidity is not None and temperature is not None:
print("Temperature="{0:0.1f}C)
humidity={1:0.1f}%".format(temperature, humidity))
else:
print("Sensor not connected.");
time.sleep(3);
Listing 3-5Code for Temperature and Humidity Sensor
可以使用以下代码将 Adafruit 模块安装在 Raspberry Pi 中。
对于 Python 2:
sudo pip install Adafruit_DHT
对于 Python 3:
sudo pip3 install Adafruit_DHT
将土壤湿度传感器与树莓接口
土壤湿度传感器用于检测土壤中的湿度。土壤湿度传感器由两个探头组成,用于测量土壤中的湿度。该传感器使用电容来测量土壤的介电常数,它是土壤含水量的函数。该传感器配有模拟和数字输出,因此它可以在模拟和数字模式下使用。但是,让我们从传感器获取模拟信号,并使用 Python 读取它。图 3-18 所示为土壤湿度传感器。这里,VCC 引脚需要连接到 Raspberry Pi 的正端,模拟输出(AO)产生一个与介电常数成比例的电压,从而与土壤含水量成比例,当土壤湿度高于阈值时,数字输出(DO)产生一个脉冲。阈值使用传感器模块中的电位计设置,GND 引脚可以连接到 Raspberry Pi 的 GND。
图 3-18
土壤湿度传感器
通过 MCP3008 ADC 将土壤湿度传感器模块连接到 Raspberry Pi,如图 3-19 所示。
图 3-19
带 Raspberry Pi GPIO 引脚的温度和湿度传感器
让我们使用gpiozero
库中的 MCP3008 模块从 MCP3008 中获取值。清单 3-6 中的代码每秒连续打印土壤湿度传感器的模拟值。
from gpiozero import MCP3008
from time import sleep
soil_sensor = MCP3008(0)
while True:
print(soil_sensor.value)
sleep(1)
Listing 3-6Code for interfacing soil moisture sensor
将相机与树莓 Pi 接口
相机是使用图像传感器记录图像的光学仪器。图像传感器检测并传送用于生成图像的信息。相机可以很容易地与 Raspberry Pi 接口,以获取图像或视频数据。有两个选项可用于将摄像机与 Raspberry Pi 接口。
方法 1: 第一种方法是使用 USB 端口将 USB 网络摄像头连接到 Raspberry Pi。图 3-20 显示了一个 USB 网络摄像头。一旦 USB 网络摄像头连接正确,就可以使用OpenCV
库在 Python 中访问它。OpenCV
是一个用于图像处理和实时计算机视觉的 Python 库。清单 3-7 中的代码可用于将 USB 网络摄像头连接到 Raspberry Pi。
图 3-20
USB 网络摄像头
import cv2
videoCaptureObject = cv2.VideoCapture(0)
result = True
while(result):
ret,frame = videoCaptureObject.read()
cv2.imwrite("/home/pi/Desktop/webcam_image.jpg ",frame)
result = False
videoCaptureObject.release()
cv2.destroyAllWindows()
Listing 3-7Code for Connecting USB Web Cameras with the Raspberry Pi
方法 2: 另一种方法是通过摄像头串行接口(CSI)端口连接 Raspberry Pi 摄像头模块。图 3-21 为树莓派相机。有两个 Raspberry Pi 相机模块可供选择:一个标准模块和一个黑色相机模块,用于在黑暗中拍照。要启用 Raspberry Pi 摄像头,请从主菜单打开 Raspberry Pi 配置,并在接口选项卡上启用摄像头。
图 3-21
带 CSI 的 Raspberry Pi 摄像机
清单 3-8 中的代码从 Raspberry Pi 相机模块中获取一张图片,并将图片存储在指定的位置。
from picamera import PiCamera
from time import sleep
camera = PiCamera()
camera.start_preview()
sleep(5)
camera.capture('/home/pi/Desktop/cammodule_img.jpg')
camera.stop_preview()
Listing 3-8Code for Connecting Raspberry Pi Camera with the Raspberry Pi
作为边缘设备的树莓皮
在数据源或其附近进行的计算被称为边缘计算。在需要即时或实时计算的领域,以及无法连接到中央云或连接受限的远程位置,边缘计算优于云计算。边缘计算的最大优势是其减少延迟的能力,因为传感器收集的数据在边缘设备中处理,不需要传输到数据中心。见图 3-22 。
图 3-22
树莓皮作为边缘装置
无人驾驶汽车中的边缘计算
自动驾驶汽车将依赖边缘计算,因为在路上行驶时,每一毫秒都非常重要。从他们的传感器和相机收集的大量数据不能被发送到云中进行分析,因为这将花费相当多的时间,并且还需要不间断的网络。因此,边缘计算因其更快的速度和高可靠性而成为这类应用的首选。
什么是边缘设备?
边缘计算在边缘设备中完成。边缘设备能够实时收集、存储和处理数据。因此,边缘设备提供了更快的响应并具有更好的可靠性。传感器和其他设备通过有线电缆或无线连接(如 Wi-Fi 或蓝牙)连接到边缘设备,如图 3-22 所示。有时,边缘设备连接到一个集中式云,用于大数据处理和数据仓库。
使用 Raspberry Pi 进行边缘计算
Raspberry Pi 具有良好的计算能力,能够通过有线和无线连接来连接传感器和设备。Raspberry Pi 还支持许多计算机编程语言,如 Python、C/C++和 Java。这使得 Raspberry Pi 成为边缘计算的绝佳选择。
作为本地化云的 Raspberry Pi
在这一章中,我们将讨论使用 Raspberry Pi 作为本地化的云。
云计算
云计算是使用互联网上的远程服务器网络来存储、管理和处理数据的实践。这些远程服务器被称为云服务器,位于世界各地的数据中心。从这类服务器访问数据需要强大的互联网连接。
树莓 Pi 作为本地化云
如今,物联网设备高速生成海量数据。通常,这些数据需要实时处理才能做出快速决策,而这可以由本地化云提供支持。此外,一些物联网传感器网络部署在互联网连接稀疏的偏远地区,这对本地化云的概念提出了挑战。Raspberry Pi 可以作为本地化的云来支持更接近物联网网络的实时数据处理。它需要通过以太网或 Wi-Fi 连接到网络。作为本地化云的 Raspberry Pi 可用于存储和处理从传感器或其他设备(如电脑和手机)收集的数据,如图 3-23 所示。
图 3-23
作为本地化云的 Raspberry Pi
连接外部硬盘
外部硬盘驱动器可以连接到 Raspberry Pi 以增加其存储容量。这些硬盘需要使用外部电源供电。如果没有电源,它们可以通过通电的 USB 集线器连接。这种增强的存储可以让 Raspberry Pi 收集和处理来自物联网网络的大量实时数据。
连接 USB 加速器
Coral USB Accelerator 是一个超快速的开发板,用于深度学习从业者在不需要互联网的情况下部署他们的模型,从而实现边缘计算。它为树莓派带来了机器学习界面。它由一个边缘 TPU 协处理器组成,每秒能够执行 4 万亿次运算(万亿次运算)。这使得实时运行 ML 模型成为可能。例如,该设备可以帮助 Raspberry Pi 以 400 FPS 的速度运行 MobileNet v2 模型。
四、传感器和信号
本章包括传感器和信号。
信号
通常,信号代表关于时间或空间的一些信息。例如,汽车速度随时间的变化就是一种信号。信息可以以信号的形式传递。在电气工程中,信号是一个携带时间或空间信息的函数。电气设备以电压、电流或电磁波的形式显示信号。根据 IEEE 信号处理汇刊,信号可以是音频、视频、语音、图像、声纳、雷达相关等等【1】。同样,从数学上讲,信号是一个或多个独立变量的函数。自变量就是那些不会被你试图测量的其他变量改变的变量。例如,考虑温度随时间的变化。这里,时间是独立变量,因为时间不会因为温度的变化而改变。
本章讨论如何通过 Raspberry Pi 使用传感器从实时环境中获取信息,然后将这些信息转换为结构化数据。传感器输出是电信号的形式。本章首先描述关于信号及其各种类型。信号有许多分类,我们集中描述以下电信号:
-
模拟和数字信号
-
连续时间和离散时间信号
-
确定性和非确定性信号
-
一维信号、二维信号、多维信号
模拟和数字信号
a 模拟信号表示相对于独立变量(即时间)连续变化的物理量的瞬时值。简单来说,模拟信号在时间和幅度上都是连续的。物理量可以是温度、压力、速度等。传感器可以将物理量的变化转换成电信号,如电压或电流。这样,可以使用传感器以电信号的形式收集实时环境数据。
数字信号是用于将数据表示为离散值序列的信号。独立变量(即时间)是离散的,并且具有量化的振幅。通过对模拟信号进行采样和量化,可以获得数字信号。在任何给定时间,数字信号只能呈现有限数量的值中的一个。
连续时间和离散时间信号
连续时间信号或连续信号是在其域(通常是时间)的连续体上定义的信号。任何模拟信号本质上都是连续的。
离散时间信号或离散信号是自变量(时间)只有离散值的信号。它是由一系列数量组成的时间序列。数字信号处理中使用的离散时间信号可以通过对连续信号进行采样和量化来获得。
确定性和非确定性信号
确定性信号是相对于其在任何时刻的值没有不确定性的信号。换句话说,可以使用数学公式精确定义的信号是确定性信号。
非确定性信号或随机信号是在某一时刻其值具有不确定性的信号。由于其随机性,该信号也被称为随机信号,并且该信号不能用数学方程来描述。
一维、二维和多维信号
一个一维信号仅仅是一个独立变量的函数。语音信号是一维信号的一个很好的例子,因为语音的幅度只取决于一个自变量(即时间)。
同样,如果信号是两个因变量的函数,则该信号称为二维信号。灰度图像是二维信号的一个例子。空间坐标(x,y)是图像中的两个独立变量。多维信号是两个以上变量的函数。电影(即视频)是多维信号的最佳例子。
收集实时数据
收集数据有两种方式:手动和自动。在手动方法中,可以从现有文件和文档中收集数据。然后,可以手动将收集的数据组织成结构化的方式(即,表格格式)。在自动化领域,可以使用一些叫做传感器的设备来收集数据。温度、压力、图像等物理量的实时信息。,可以使用传感器收集。本章重点描述使用传感器的自动数据收集。为了使数据收集自动化,需要数据采集系统。本节说明如何使用传感器收集数据,如超声波传感器、湿度、温度和来自摄像机的图像数据。此外,还讨论了以结构化格式存储收集的数据。
数据采集
对测量真实世界物理条件的信号进行采样,并将结果样本转换为可由计算机处理的数字数值的过程称为数据采集。数据采集系统(DAS 或 DAQ)通常将模拟信号转换为数字值进行处理。数据采集系统包括以下三个部分:
-
传感器
-
信号调理电路
-
模数转换器
传感器
通常,传感器产生与环境变化相对应的电信号。传感器是一种转换温度、湿度、距离等物理参数的装置。转换成电信号。传感器可以是能够检测环境中的事件或变化并将信息发送到其他电子设备(通常是计算机处理器)的设备、模块、机器或子系统。例如,热电偶是一种温度传感器,它根据输入温度变化产生输出电压。根据其输出信号类型,有两种类型的传感器:模拟和数字。
模拟传感器
模拟传感器产生连续的输出信号或电压,通常与被测量的量成比例。这些传感器通常产生随时间平滑连续变化的输出信号。见图 4-1 。
图 4-1
模拟信号
以下代码每秒连续打印连接到 MCP3008 第一个通道(0)的传感器的模拟值。由于 MCP3008 是一个 10 位 ADC,输出值范围为 0 至 1023。
from gpiozero import MCP3008
from time import sleep
sensor = MCP3008(0)
while True:
print(sensor.value)
数字传感器
数字传感器产生数字输出信号或电压,这些信号或电压是被测量量的数字表示。在这些传感器中,数据转换和数据传输以数字方式进行。见图 4-2 。
图 4-2
数字信号
以下代码连续打印 GPIO 引脚 17 的数字状态:
from gpiozero import InputDevice
from time import sleep
sensor = InputDevice(17, pull_up=True)
while True:
print(sensor.value)]
下面列出了电子行业中一些常见的传感器:
-
温度传感器
-
红外传感器
-
超声波传感器
-
压力传感器
-
近程传感器
-
触摸传感器
-
液位传感器
-
烟雾和气体传感器
什么是实时数据?
实时数据(RTD) 是收集后立即传递给最终用户的信息。实时数据可以是静态的,也可以是动态的,通常使用实时计算进行处理。
实时数据分析
实时分析是指在收集数据后立即对收集的数据进行分析。实时数据分析使我们能够毫不延迟地做出决策,并能够防患于未然。
在这里,我们将讨论如何从摄像机获取关于距离、湿度、温度和图像数据的实时数据。
从超声波传感器获取实时距离数据
超声波传感器的基本原理是发射和接收声波。物理变量(如距离、水平、高度、流量等。)可以基于发射波和接收回波声波之间的持续时间来计算。
将超声波传感器与 Raspberry Pi 接口
第三章已经讨论了超声波传感器与 Raspberry Pi 的接口。我们将从 HC-SR04 超声波传感器收集数据,该传感器可用于测量 2 厘米至 400 厘米的距离,精度为 3 毫米。这里,我们的目标是将超声波距离传感器与 Raspberry Pi 接口,并将收集的数据保存为 CSV 格式。为此,可以将超声波距离传感器连接到 Raspberry Pi GPIO 引脚,如图 4-3 所示。
图 4-3
超声波传感器与 Raspberry Pi GPIO 引脚连接
正如在第三章中所提到的,我们将使用gpiozero
库中的DistanceSensor
对象。distance
函数返回超声波距离传感器测量的距离,单位为米。为了以厘米为单位显示,我们需要将该值乘以 100。下面的代码以厘米为单位打印超声波距离传感器每秒测量的距离,并在 100 秒后保存收集的数据。
from gpiozero import DistanceSensor
from time import sleep
sensor = DistanceSensor(echo=17, trigger=4)
n = 100
for i in range(n):
print(sensor.distance * 100)
sleep(1)
当代码运行时,移动放置在超声波传感器前面的物体的位置以获得不同的值。测量的距离(单位 cm)连续打印 n 秒;在我们的例子中,是 100。
从摄像机获取实时图像数据
本节说明如何从网络摄像头获取实时视频。
从网络摄像头获取实时视频
通过 USB 端口将 USB 网络摄像头连接到 Raspberry Pi。使用OpenCV
Python 库,我们可以访问网络摄像头并从中捕捉图像和视频。以下代码可用于从网络摄像头获取实时视频。可以实时分析收集的帧。
import cv2
vid = cv2.VideoCapture(0)
while(True):
ret, frame = vid.read()
cv2.imshow('frame', frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
vid.release()
cv2.destroyAllWindows()
从 Pi-cam 获取实时视频
通过摄像头串行接口(CSI)端口将 Raspberry Pi 摄像头模块连接到 Raspberry Pi。要启用 Raspberry Pi 摄像头,请从主菜单打开 Raspberry Pi 配置,并在接口选项卡上启用摄像头。以下代码可用于使用 Raspberry Pi 摄像头模块捕捉图像,并将捕捉到的图像存储在指定位置:/home/pi/Desktop/cammodule_img.jpg
。
from picamera import PiCamera
from time import sleep
camera = PiCamera()
camera.start_preview()
sleep(5)
camera.capture('/home/pi/Desktop/cammodule_img.jpg')
camera.stop_preview()
数据传送
数据传输或数据传送是指在两个或多个数字设备之间传送数据的过程。数据以模拟或数字格式传输,数据传输过程使设备或设备内的组件能够相互通信。
串行和并行通信
串行通信是通过通信信道或计算机总线一次一位地顺序发送数据的过程。并行通信是一种同时传送多个二进制数字(位)的方法。
Arduino 与 Raspberry Pi 的接口
我们可以将 Arduino 连接到 Raspberry Pi,并将数据从 Arduino 传输到 Raspberry Pi,反之亦然。传感器、电机和致动器可以连接到 Arduino,并使 Arduino 向/从 Raspberry Pi 传输值。通过这样做,我们可以使用 Arduino 作为节点,并通过这些节点获取传感器数据。
Arduino 可以通过两种方式连接到 Raspberry Pi。
-
通过 USB 进行串行通信
-
通过 GPIO 引脚进行串行通信
通过 USB 串行
使用 Arduino USB 电缆连接两块电路板是在 Arduino 和 Raspberry Pi 电路板之间建立通信的最简单方法。
在 Raspberry Pi 上,选择板上可用的四个 USB 端口中的任意一个,并连接 USB 连接器。将 Arduino USB 电缆的另一端连接到 Arduino。不同版本的 Arduino 的连接器电缆会有所不同。
通过 GPIOs 串行
也可以使用普通导线在 Raspberry Pi GPIOs 和 Arduino 引脚之间建立串行连接。根据你的 Arduino 板,可能需要一个电压电平转换器。
Raspberry Pi 的工作电压为 3.3V,而诸如 Uno、Mega、Leonardo、Nano 等 Arduino 板的工作电压为 3.3V。工作电压为 5V。因此,在连接 RX 和 TX 引脚时,需要使用 3.3V/5V 电平转换器来保护 Raspberry Pi,如图 4-4 所示。
图 4-4
通过 GPIO 引脚连接 Arduino 和 Raspberry Pi
一般来说,建议使用 Arduino USB 电缆而不是 GPIOs 进行串行通信。
Arduino 和 Raspberry Pi 之间的数据传输
当通过 USB 电缆将 Arduino 连接到 Raspberry Pi 时,在 Raspberry Pi 终端窗口中运行命令ls /dev/tty*
来查找 Arduino 设备的名称。它应该返回类似于/dev/ttyACM0
或/dev/ttyUSB0
的东西。
pySerial
Python 库用于制作与 Python 的串行接口,封装了对串口的访问。
以下代码可用于在 Arduino 和 Raspberry Pi 之间进行双向通信。
Arduino 伫列
以下是 Arduino 代码:
void setup() {
Serial.begin(9600);
}
void loop() {
if (Serial.available() > 0) {
String data = Serial.readStringUntil('\n');
Serial.print("Received Data: ");
Serial.println(data);
}
}
Serial.available()
将给出已经到达并存储在接收缓冲区中的字节数。这可用于检查 Arduino 是否已接收到数据。
如果一些数据已经到达,Serial.readStringUntil()
与换行符\n
一起使用来获得下一行。在换行符\n
之前收到的所有字节都被自动转换并添加到 Arduino String
对象中。
然后,我们只返回包含接收到的数据和一些附加文本的字符串。
Raspberry Pi Python 代码
下面是显示串行数据的 Raspberry Pi Python 代码:
#!/usr/bin/env python3
import serial
import time
if __name__ == '__main__':
ser = serial.Serial('/dev/ttyACM0', 9600, timeout=1)
ser.flush()
while True:
ser.write(b"Data from Raspberry Pi!\n")
line = ser.readline().decode('utf-8').rstrip()
print(line)
time.sleep(1)
pySerial
功能write()
用于向 Arduino 发送数据。在发送字符串之前,它会将字符串编码成字节,因为你只能通过Serial
发送字节。任何不是字节或字节数组的数据都必须在通过Serial
发送之前进行转换。
此外,我们添加了一个换行符\n
,因为 Arduino 在使用Serial.readStringUntil('\n')
读取时希望它出现在字符串的末尾。
然后我们从Serial
读取一行,解码成一个字符串,最后打印接收到的字符串并等待一秒钟,然后通过Serial
发送下一个字符串。
时间序列数据
时间序列是按时间顺序索引的一系列数据点。最常见的是,它是在连续的等距时间点上拍摄的序列。因此,时间序列可以定义为一系列离散时间数据。在时间序列数据中,时间往往是自变量,目标一般是对未来做出预测。
时间序列经常通过折线图绘制。时间序列用于统计学、信号处理、通信工程、模式识别、天气预报、地震预测、控制工程、天文学等。
时间序列分析和预测
时序分析包括分析时序数据以提取有意义的统计数据和数据的其他特征的方法。时间序列分析还包括对未来序列的预测、从噪声数据中提取隐藏信号、发现数据生成机制等。时间序列预测是基于历史数据使用模型预测未来值。
内存要求
本节讨论内存需求。
更多存储空间
有时,microSD 卡的内存可能不够,可能需要更多内存。更多的存储空间对于存储收集的数据和重模型非常有益。为了增加存储容量,可以将外部硬盘驱动器连接到 Raspberry Pi。
更多内存
RAM 是数据科学项目的另一个重要因素。RAM 越大,它可以处理的数据量就越大,从而导致处理速度更快。虽然 1GB RAM 的基本版本可以完成这项工作,但对于大多数深度学习任务,推荐使用 4GB RAM 版本的 Raspberry Pi。
案例研究:收集实时行业数据
我们来看一个案例研究。
使用 Pandas 存储收集的数据
收集的数据也可以保存以备后用。Pandas 是一个用 Python 构建的开源数据分析和操作工具。我们将使用 Pandas 将收集的数据转换成结构化的数据格式。Pandas 库可以使用以下命令通过pip
安装:
pip install pandas
数据帧
数据帧是二维数据结构。数据以表格的形式排列成行和列,它通常是最常用的 Pandas 对象。一旦我们将数据转换成数据帧,我们就可以轻松地将数据转换成其他格式,如 CSV 和 Microsoft Excel。
将数据保存为 CSV 文件
逗号分隔值文件是使用逗号分隔值的分隔文本文件。文件的每一行都是一条数据记录。每个记录由一个或多个字段组成,用逗号分隔。Pandas 数据帧的to_csv()
函数将数据帧导出为 CSV 格式。
df.to_csv('file path\File Name.csv')
另存为 Excel 文件
要将单个对象写入 Excel .xlsx
文件,只需指定一个目标文件名。要写入多个工作表,需要创建一个带有目标文件名的ExcelWriter
对象,并在文件中指定要写入的工作表。Pandas 数据帧的to_excel()
函数将数据帧导出为.xlsx
格式。
df.to_excel("output.xlsx")
读取保存的数据文件
数据保存后,可使用read_csv()
或read_excel()
功能读取。read_excel()
函数将一个 Excel 文件读入 Pandas 数据帧,它支持从本地文件系统或 URL 读取的.xls
、.xlsx
、.xlsm
、.xlsb
和.odf
文件扩展名。它有一个选项来读取单个表或表的列表。read_csv()
函数将一个 CSV 文件读入一个数据帧,并且还支持可选的迭代或者将文件分成块。
向实时数据添加日期和时间
在收集数据的同时,我们还可以在数据中添加数据和时间。我们将使用datetime
Python 库。datetime.datetime.now()
可用于获取当前日期和时间。
from datetime import datetime
now = datetime.now()
print("now =", now)
# dd/mm/YY H:M:S
dt_string = now.strftime("%d/%m/%Y %H:%M:%S")
print("date and time =", dt_string)
来自温度和湿度传感器的行业数据
我们将使用温度和湿度传感器来测量温度和湿度。如第三章所示,将 DHT 11/22 传感器模块连接到 Raspberry Pi。
以下代码收集 100 秒的温度和湿度值,并将收集的数据存储为 CSV 文件:
import Adafruit_DHT
import time
from datetime import datetime
DHT_SENSOR = Adafruit_DHT.DHT11
DHT_PIN = 17
data = []
while _ in range(100):
humidity, temperature = Adafruit_DHT.read(DHT_SENSOR, DHT_PIN)
if humidity is not None and temperature is not None:
now = datetime.now()
dt_string = now.strftime("%d/%m/%Y %H:%M:%S")
data.append(dt_string,humidity,temperature)
time.sleep(60*5)
df = pd.DataFrame(data)
df.to_csv('data.csv',index=None,header=None)
CSV 文件将如下所示:
| 17/05/2020 01:05:14 | Twenty-six point two four | Sixty-nine point nine one | | 17/05/2020 01:10:14 | Twenty-six point two four | Seventy point six five | | 17/05/2020 01:15:14 | Twenty-six point two two | Sixty-eight point eight seven | | 17/05/2020 01:20:14 | Twenty-six point one five | Seventy point one one | | 17/05/2020 01:25:14 | Twenty-six point one one | Sixty-nine point zero two |五、准备数据
数据科学最重要的一步是准备数据。数据准备是清理、处理和转换用于分析的原始数据的过程。从这个阶段开始,可以通过清理、识别缺失值、处理异常值等方式有效地处理数据中的错误。因此,本章讨论了使用 Python 中的 Pandas 包准备数据的方法。
Pandas 和数据结构
Pandas 是一个为 Python 编程语言编写的软件库,主要用于数据操作和分析。
简而言之,Pandas 就像 Python 的 Excel,表格(在 Pandas 中称为 dataframes )由行和列组成(在 Pandas 中称为系列)。Pandas 有许多功能,使它成为一个数据处理、检查和操作的极好的库。
安装和使用 Pandas
在您的系统上安装 Pandas 需要安装 NumPy,如果从源代码构建库,则需要适当的工具来编译构建 Pandas 的 C 和 Cython 源代码。
您可以在 Pandas 文档中找到关于此安装的详细信息。Pandas 可以安装使用 pip 功能:pip 安装 Pandas。安装 Pandas 后,您可以导入它并检查版本,如下所示:
import pandas
pandas.__version__
正如我们通常以别名np
导入 NumPy 一样,我们将以别名pd
导入 Pandas,这种导入约定将贯穿本书的其余部分。
import pandas as pd
Pandas 数据结构
数据结构是一种数据组织、管理和存储格式,支持高效的访问和修改。更准确地说,数据结构是数据值、数据值之间的关系以及可应用于数据的功能或操作的集合。Pandas 向 Python 引入了两个新的数据结构,Series
和DataFrame
,这两个数据结构都是建立在 NumPy 之上的(这意味着它们很快)。
系列
系列是一个一维对象,类似于表格中的数组、列表或列。它将为序列中的每个项目分配一个带标签的索引。默认情况下,每个项目将收到一个从 0 到 N 的索引标签,其中 N 是序列的长度减 1,如下所示:
s = pd.Series([1, 'Raspberry Pi', 3.14, -500, 'Data'])
print(s)
Output:
0 1
1 Raspberry Pi
2 3.14
3 -500
4 Data
dtype: object
我们可以不提供默认索引,而是在创建序列时为每个条目指定一个索引,如下所示:
s = pd.Series([1, 'Raspberry Pi', 3.14, -500, 'Data'],
index=['M', 'A', 'X', 'I', 'E'])
print(s)
Output:
M 1
A Raspberry Pi
X 3.14
I -500
E Data
dtype: object
Series
构造函数也可以将字典转换成序列,使用字典的键作为索引,如下所示:
d = {'English': 95, 'Math': 100, 'Science': 98, 'Social Science': 93}
marks = pd.Series(d)
print(marks)
Output:
English 95
Math 100
Science 98
Social Science 93
dtype: float64
该索引可用于从系列中选择特定项目。例如,可以通过指定索引Math
来选择数学的标记。类似地,可以通过在列表中提供以逗号分隔的相应索引来打印一组项目,如下所示:
print (marks['Math'])
print(marks[['English', 'Science', 'Social Science']])
Output :
100.0
English 95
Science 98
Social Science 93
dtype: float64
也可以使用过滤值的布尔索引。例如,使用索引marks < 96
返回一系列布尔值,然后我们将这些值传递给我们的序列marks
,返回相应的True
项,如下所示:
marks[marks < 96]
Output:
Math 100
Science 98
dtype: float64
通过访问项目的相应索引,可以随时更改系列中特定项目的值,如下所示:
print('Old value:', marks['Math'])
marks['Math'] = 99
print('New value:', marks['Math'])
Output:
('Old value:', 100.0)
('New value:', 99.0)
我们还可以使用以下代码检查某个项目是否存在于序列中:
print('Math' in marks)
print('French' in marks)
Output:
True
False
也可以对一系列数值进行数学运算,如下所示:
marks * 10
Output:
English 950
Math 990
Science 980
Social Science 930
dtype: float64
np.square(marks)
Output:
English 9025
Math 9801
Science 9604
Social Science 8649
dtype: float64
数据帧
表格DataFrame
数据结构由行和列组成,类似于电子表格或数据库表。您也可以将一个DataFrame
看作一组共享一个索引(列名)的Series
对象。
阅读日期
为了从常见的 Python 数据结构中创建一个DataFrame
数据结构,我们可以将一个列表字典传递给DataFrame
构造函数。
a={'Name':['Augustus', 'Hazel', 'Esther', 'Cavas'],
'Gender':['Male','Female','Female','Male'],
'Age':[19, 18, 22, 21]}
b=pd.DataFrame.from_dict(a)
print(b)
Output:
Name Gender Age
0 Augustus Male 19
1 Hazel Female 18
2 Esther Female 22
3 Cavas Male 21
读取 CSV 数据
读取 CSV 文件就像调用read_csv
函数一样简单。默认情况下,read_csv
函数希望列分隔符是逗号,但是您可以使用sep
参数来更改它。以下代码显示了将 CSV 文件读入DataFrame 'df'
并使用head()
函数打印df
的前五行的语法:
df = pd.read_csv('data.csv')
print(df.head())
还有一组 writer 函数,用于将DataFrame
对象写成各种格式,如 CSV 文件、HTML 表格、JSON 等。以下代码行显示了将DataFrame
对象写入 CSV 文件的语法:
df.to_csv('path_to_file.csv')
读取 Excel 数据
Pandas 允许我们读写 Excel 文件,所以我们可以很容易地用 Python 从 Excel 中读取数据,然后将数据写回 Excel。读取 Excel 文件需要使用pip
命令安装的xlrd
库,如下所示:
pip install xlrd.
下面的代码说明了从 Excel 文件读取一个工作表到DataFrame df
的语法。用 Excel 文件的路径/文件名替换data.xlsx
来运行代码。
df = pd.read_excel('data.xlsx', 'Sheet1')
print(df.head())
类似地,来自DataFrame
对象的数据可以写入 Excel 文件,如下所示:
dataframe.to_excel('path_to_file.xlsx', index=False)
正在读取 URL 数据
read_table
函数可以用来直接从一个 URL 读取。以下代码说明了使用来自给定 URL 的原始数据创建的DataFrame
:
url = 'https://raw.github.com/gjreda/best-sandwiches/master/data/best-sandwiches-geocode.tsv'
from_url = pd.read_table(url, sep='\t')
from_url.head(3)
Output:
rank sandwich ... lat lng
0 1 BLT ... 41.895734 -87.679960
1 2 Fried Bologna ... 41.884672 -87.647754
2 3 Woodland Mushroom ... 41.890602 -87.630925
清理数据
在大多数数据分析项目中,可用数据并不总是完美的。除了有用的数据之外,原始数据总是容易被损坏或不准确的数据弄乱。因此,数据科学家必须处理这些杂乱的数据样本,以便将原始数据转换成可以工作的形式,并且他们要花相当长的时间来这样做。
数据清理是识别数据中不准确、不正确或不完整的部分,并通过替换、删除或修改数据来处理它们的过程。换句话说,它是通过处理原始数据中的所有不规则性来为分析准备数据的过程。在接下来的部分中,我们将讨论如何处理缺失值和异常值,填充不合适的值,以及删除重复的条目。
处理缺失值
缺失值在原始数据中很常见。假设输入数据由使用调查表收集的成千上万客户的产品反馈组成。客户在填写调查表时跳过一些条目是很常见的行为。例如,一些客户可能不会分享他们对该产品的体验,一些客户可能不会分享他们使用该产品的持续时间,还有一些客户可能不会填写他们的联系信息。在编辑这些调查表并将其转换成表格时,表格中肯定会有大量缺失值。
由于各种原因,如传感器节点临时断电、硬件故障、通信干扰等,来自传感器的数据也可能丢失数据。因此,处理这些缺失值是数据科学家在处理原始数据时的首要任务。以下代码说明了如何使用 NumPy 库中的random.randn
函数创建随机数数据库:
import pandas as pd
import numpy as np
df = pd.DataFrame(np.random.randn(6,4),
index = ['1','3','4','6','7','9'],
columns = ['a','b','c','d'])
从前面的代码中可以看出,行和列的索引是手动分配的。从分配给行的索引中可以看出,缺少索引 2、5 和 8。使用 Pandas 库中的reindex
函数,这些索引是用缺失的“非数字”(NaN)值创建的,如下所示:
df=df.reindex(['1','2','3','4','5','6','7','8','9'])
print(df)
Output:
a b c d
1 0.099344 0.293956 1.002970 0.516942
2 NaN NaN NaN NaN
3 1.608906 -1.748396 -1.013634 -0.651055
4 3.211263 -2.555312 -1.036068 -0.728020
5 NaN NaN NaN NaN
6 -0.101766 -0.205572 1.369707 -1.133026
7 0.062344 1.483505 0.026995 1.560656
8 NaN NaN NaN NaN
9 -0.324347 -0.342040 0.107224 0.272153
既然已经创建了包含缺失值的数据库,下一步就是处理这些值。在考虑处理这些值的选项之前,最重要的任务是检测丢失值的位置。Pandas 库中的isnull()
函数可用于检测包含缺失值的行,如下所示:
df1=df[df.isna().any(axis=1)]
print(df1)
Output:
a b c d
2 NaN NaN NaN NaN
5 NaN NaN NaN NaN
8 NaN NaN NaN NaN
前面的过程让我们大致了解了数据库中丢失的数据量。一旦检测到丢失的数据,下一步就是处理丢失的数据。有两种方法可以做到这一点:一种是用值填充缺失的数据,第二种是简单地删除缺失的数据。
Pandas 库中的fillna()
函数可以用来用用户指定的标量值填充缺失的值,如下所示。如图所示,第 2 行和第 5 行中缺少的值被替换为 0.000000。
df2=df.fillna(0)
print(df2.head())
Output:
a b c d
1 0.099344 0.293956 1.002970 0.516942
2 0.000000 0.000000 0.000000 0.000000
3 1.608906 -1.748396 -1.013634 -0.651055
4 3.211263 -2.555312 -1.036068 -0.728020
5 0.000000 0.000000 0.000000 0.000000
替换缺失值的另一种方法是使用 Pandas 库中的ffill
或bfill
函数。ffill
代表“向前填充”,通过重复出现在它们之前的值来填充缺失的值,bfill
代表“向后填充”,通过重复出现在它们之后的值来填充缺失的值。以下代码说明了填充缺失值的正向填充方法:
df3= df.fillna(method='ffill')
print(df3.head())
Output:
a b c d
1 0.099344 0.293956 1.002970 0.516942
2 0.099344 0.293956 1.002970 0.516942
3 1.608906 -1.748396 -1.013634 -0.651055
4 3.211263 -2.555312 -1.036068 -0.728020
5 3.211263 -2.555312 -1.036068 -0.728020
处理缺失值的第二种可能方式是使用 Pandas 库中的dropna
函数删除它们,如下所示:
df4=df.dropna()
print(df4)
Output:
a b c d
1 0.099344 0.293956 1.002970 0.516942
3 1.608906 -1.748396 -1.013634 -0.651055
4 3.211263 -2.555312 -1.036068 -0.728020
6 -0.101766 -0.205572 1.369707 -1.133026
7 0.062344 1.483505 0.026995 1.560656
9 -0.324347 -0.342040 0.107224 0.272153
我们创建了一个带有缺失值的简单数据集,以理解处理缺失值的概念。实际上,分析项目中使用的数据集很大,可能包含 500 到 1,000 行,甚至更多。我们鼓励您将从该示例中获得的知识应用到真实数据集上。处理缺失值的方法可能取决于应用的性质以及数据集中缺失值的数量或频率。
处理异常值
在数据集中,离群值是从所有其他观察值中脱颖而出的观察值(即数据)。换句话说,离群值是远离数据集中所有其他数据的数据点。异常值可能是由于测量/数据输入中的错误或由于数据中真正的极端值造成的。例如,考虑 112、123、120、132、106、26、118、140 和 125 这一系列数字。在这个数列中,除了 26 以外,所有的数字都接近 100。因此,26 是一个异常值,因为它与其他数字相距甚远。
可以通过两种方式检测异常值:使用可视化技术和使用数学方法。在本节中,我们将介绍两种识别数据中异常值的数学方法,即四分位数间距(IQR)和 Z 值。
四分位范围是对数据集中数据的可变性或分布的度量。数据首先被排序并分成四个季度。将总范围分成四个四分位数的值称为四分位数。因此,将有三个四分位数用于将数据分成四个四分位数。四分位数是 Q 1 ,Q 2 和 Q 3 ,其中 Q 2 是整个数据的中值,Q 1 是上半部分数据的中值,Q 3 是下半部分数据的中值。IQR 是第三个四分位数和第一个四分位数之差,即 Q3–Q1。
为了说明使用 IQR 移除离群值的过程,让我们首先创建一个包含离群值的 15 个条目的DataFrame
。
import pandas as pd
a={'Name':['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O'],
'Weight':[56,62,48,72,15,80,76,64,68,180,75,47,58,63,71]}
df=pd.DataFrame.from_dict(a)
print(df.head())
Output:
Name Weight
0 A 56
1 B 62
2 C 48
3 D 72
4 E 25
在前面的代码中,我们创建了一个包含 15 个成年人体重(公斤)的数据库。为了方便起见,我们用字母 A 到 M 来给成年人命名。15 公斤和 180 公斤的体重被列为异常值,因为健康成年人体重不太可能太轻或太重。为了检测这些异常值,我们需要分别计算 25%和 75%的四分位值,Q1 和 Q3。从这些值中,可以通过确定 Q3-Q1 的差值来计算 IQR 值。此过程如下所示:
Q1=df.Weight.quantile(0.25)
Q3=df.Weight.quantile(0.75)
IQR=Q3-Q1
print('Q1=',Q1,'Q3=',Q3,'IQR=',IQR)
Output:
Q1= 57.0 Q3= 73.5 IQR= 16.5
通过将DataFrame
对象中的条目与之前计算的四分位数进行比较,可以看到有四个值低于 Q1,七个值在 Q1 和 Q3 之间,四个值高于 Q3。但是我们知道只有一个异常值低于 Q1,一个异常值高于 Q3。为了检测这些异常值,我们需要形成一个区间,其下限远低于 Q1,上限远高于 Q3。一旦确定了这些限制,就可以放心地认为低于下限的值和高于上限的值将是异常值。下面的代码说明了这一点:
lower_limit = Q1 - 1.5 * IQR
upper_limit = Q3 + 1.5 * IQR
df1=df[(df.Weight < lower_limit) | (df.Weight > upper_limit)]
print(df1)
Output:
Name Weight
4 E 25
9 J 180
可以看出,使用 IQR 值创建的限制已经准确地检测到了我们数据中的异常值。现在,可以使用以下代码轻松过滤掉这些异常值:
df2=df.drop(df1.index)
print(df2)
Output:
Name Weight
0 A 56
1 B 62
2 C 48
3 D 72
5 F 80
6 G 76
7 H 64
8 I 68
10 K 75
11 L 47
12 M 58
13 N 63
14 o 71
z 分数
Z 分数,也称为标准分数,给出了一个数据点离平均值有多远的概念。从技术上来说,Z 得分符合正态分布的数据,并测量数据点相对于整个数据集平均值的标准偏差数,如图 5-1 所示。
图 5-1
基于 Z 得分的异常值检测数据的正态分布
图 5-1 显示每个数据点沿着以零均值为中心的正态分布绘制。离零均值太远的数据点被视为异常值。在大多数情况下,阈值固定为 3,任何超过 3σ或-3σ的数据点都被视为异常值。让我们使用上一节中使用的同一数据库,并使用 Z 分数来识别异常值。
import pandas as pd
from scipy import stats
import numpy as np
a={'Name':['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O'],
'Weight':[56,62,48,72,15,80,76,64,68,180,75,47,58,63,71,]}
df=pd.DataFrame.from_dict(a)
z = np.abs(stats.zscore(df.Weight))
print(z)
df1=df[z>3]
print(df1)
Output:
Name Weight
9 J 180
从前面的代码可以看出,对应于权重值 180 的 Z 分数超过了阈值 3,因此它被显示为异常值。不幸的是,权重值 15 没有被检测为异常值。其原因可以通过比较平均值和标准偏差来理解,这可以通过np.mean
和np.std
函数来实现,如下所示:
print(np.mean(df.Weight))
print(np.std(df.Weight))
Output:
67.0
33.448467827390836
让我们把标准差的值近似为 33.45。可以看出,权重值 180 和平均值之间的差为 111,大于标准偏差的三倍(> 3σ),而权重值 15 和平均值之间的差仅为 54,小于标准偏差的两倍(< 2σ)。克服这个问题的一种方法是降低阈值。让我们假设Threshold
值为 1。
df1=df[z>1]
print(df1)
Output:
Name Weight
4 E 15
9 J 180
从上图中可以看出,理想的阈值 3 可能并不适用于每个数据集,因此应该根据数据的分布来选择阈值。现在,与 IQR 的情况类似,可以使用以下代码简单地过滤掉这些异常值:
df2=df.drop(df.Name[z>1].index)
print(df2)
Output:
Name Weight
0 A 56
1 B 62
2 C 48
3 D 72
5 F 80
6 G 76
7 H 64
8 I 68
10 K 75
11 L 47
12 M 58
13 N 63
14 o 71
过滤掉不合适的值
在某些情况下,数据集可能包含一些与数据完全无关的不适当的值。在传感器数据的情况下尤其如此。传感器记录的数据通常是时间序列数据,每个数据点都有唯一的时间戳。在许多情况下,分析不需要这些时间戳,因此可以将其视为不适当的值。为了说明这个概念,我们创建了一个类似于传感器数据的时间序列温度数据,如下所示:
import pandas as pd
data={'Time':['12:00:05','12:08:33','12:25:12','12:37:53','12:59:08'],
'Temperature':['T=22','T=22','T=23','T=23','T=24']}
df=pd.DataFrame.from_dict(data)
print(df)
Output:
Time Temperature
0 12:00:05 T=22
1 12:08:33 T=22
2 12:25:12 T=23
3 12:37:53 T=23
4 12:59:08 T=24
现在每个数据点对应的时间戳和每个数据点中的头'T='
都要去掉。时间戳可以使用 Pandas 库中的drop
函数删除,而报头可以使用str.replace
函数删除。因为每个数据点中都有一个标题,所以数据最初存储为字符串数据类型。因此,在删除这些头之后,数据类型必须更改为int
或float
。这些程序说明如下:
df.drop('Time',inplace=True,axis=1)
df=df.Temperature.str.replace('T=','')
df=df.astype(float)
print(df)
Output:
0 22.0
1 22.0
2 23.0
3 23.0
4 24.0
Name: Temperature, dtype: float64
删除重复项
重复条目在数据科学中很常见,尤其是当我们从各种来源收集数据并整合它们进行处理时。根据我们分析的性质,这些重复可能会造成问题。因此,最好在分析数据之前删除这些重复项,如下所示:
import pandas as pd
a={'Name':['Alan','Joe','Jim','Tom','Alan','Anna','Elle','Rachel','Mindy'],
'Age':[22,24,25,24,22,23,21,22,23]}
df=pd.DataFrame.from_dict(a)
print('DATA\n',df)
print('DUPLICATES\n',df[df.duplicated()])
df1=df.drop_duplicates()
print('DATA AFTER REMOVING DUPLICATES\n',df1)
Output:
DATA
Name Age
0 Alan 22
1 Joe 24
2 Jim 25
3 Tom 24
4 Alan 22
5 Anna 23
6 Ellen 21
7 Rachel 22
8 Mindy 23
DUPLICATES
Name Age
4 Alan 22
DATA AFTER REMOVING DUPLICATES
Name Age
0 Alan 22
1 Joe 24
2 Jim 25
3 Tom 24
5 Anna 23
6 Ellen 21
7 Rachel 22
8 Mindy 23
如代码所示,从由几个人的姓名和年龄组成的字典中创建了一个DataFrame
,我们特意为姓名 Alan 创建了一个重复的条目。可以看出,Pandas 库中的复制函数清楚地标识了该名称的第二个条目。然后使用 Pandas 库中的drop_duplicates
函数删除这个重复的条目。
六、可视化数据
在前一章中,我们讨论了准备数据进行分析的一些步骤。在分析数据之前,必须了解我们正在处理的数据的性质。可视化数据可能会给我们一些关于数据本质的有用见解。这些洞察,例如数据中的模式、数据的分布、数据中存在的异常值等。,可以方便地确定用于分析数据的方法。此外,可视化可以在分析结束时用于向相关方传达调查结果,因为通过可视化技术传达分析结果可能比编写解释调查结果的文本内容更有效。在本章中,我们将了解 Python 的 Matplotlib 包提供的一些基本可视化绘图,以及如何定制这些绘图来传达不同数据的特征。
Matplotlib 程式库
Matplotlib 是一个绘图库,用于使用 Python 编程语言创建出版物质量的绘图。该软件包根据要传达的信息类型提供各种类型的绘图。这些情节带有交互式选项,如平移、缩放和子情节配置。这些图也可以保存为不同的格式,如 PNG、PDF 等。此外,Matplotlib 包为每种类型的绘图提供了许多定制选项,可用于有效地表示要传达的信息。
散点图
散点图是一种使用标记来指示数据点以显示两个变量之间关系的图。在进行数据分析时,散点图可以有多种用途。例如,当数据点被视为一个整体时,该图可以揭示数据的模式和趋势,这反过来可以帮助数据科学家了解两个变量之间的关系,从而使他们能够提出一种有效的预测技术。散点图也可用于识别数据中的聚类。它们还可以揭示数据中存在的异常值,这一点至关重要,因为异常值往往会极大地影响预测系统的性能。
创建散点图通常需要两列数据,图的每个维度一列。表格中的每一行数据将对应于图中的单个数据点。可以使用 Matplotlib 库中的scatter
函数创建散点图。为了演示散点图的有用性,让我们考虑一下可以从 Scikit-Learn 库中导入的波士顿住房数据集。这个数据集实际上来自于卡内基梅隆大学维护的 StatLib 图书馆。它由 506 个样本组成,具有 13 个不同的特征属性,如城镇人均犯罪率(CRIM)、每所住宅的平均房间数(RM)、径向公路可达性指数(RAD)等。此外,目标属性 MEDV 表示所有者自住房屋的中值,以千为单位。
以下代码演示了创建 Pandas 数据框架波士顿住房数据集的过程,该数据框架最初采用字典格式。为了方便起见,使用print
命令,在这段代码中只显示数据帧的前五行。
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from sklearn.datasets import load_boston
dataset = load_boston()
boston_data=pd.DataFrame(dataset.data,columns=dataset.feature_names)
print(boston_data.head())
Output:
CRIM ZN INDUS CHAS NOX ... RAD TAX PTRATIO B LSTAT
0 0.00632 18.0 2.31 0.0 0.538 ... 1.0 296.0 15.3 396.90 4.98
1 0.02731 0.0 7.07 0.0 0.469 ... 2.0 242.0 17.8 396.90 9.14
2 0.02729 0.0 7.07 0.0 0.469 ... 2.0 242.0 17.8 392.83 4.03
3 0.03237 0.0 2.18 0.0 0.458 ... 3.0 222.0 18.7 394.63 2.94
4 0.06905 0.0 2.18 0.0 0.458 ... 3.0 222.0 18.7 396.90 5.33
[5 rows x 13 columns]
房屋数据集原本是字典的形式,保存到变量dataset
。13 个特征属性分配给data
键,目标属性 MEDV 分配给target
键。然后,这 13 个特征被转换成 Pandas 数据帧。现在,特征变量 RM 相对于目标变量 MEDV 的散点图可以通过下面的代码获得。从图 6-1 中的曲线图可以看出,一套房子的价格随着房间数量的增加而增加。除了这一趋势,在图中还可以看到一些异常值。
图 6-1
房价与每套住宅平均房间数的关系图
plt.scatter(boston_data['RM'],dataset.target)
plt.xlabel("Average number of rooms per dwelling(RM)")
plt.ylabel("Median value of owner-occupied homes in $1000s(MEDV)")
plt.show()
线形图
折线图只不过是由一条线连接起来的一系列数据点,它可以用来表达一个变量在特定时间内的趋势。折线图通常用于可视化时间序列数据,以观察数据随时间的变化。它也可以作为分析过程的一部分,用于检查迭代过程中变量的变化。
可以使用 Matplotlib 包中的 plot 函数获得线图。为了演示折线图,让我们考虑一个时间序列数据集,该数据集由澳大利亚墨尔本市 10 年(1981-1990 年)的最低日温度(以 0 摄氏度为单位)组成。以下代码说明了加载包含数据集的.csv
文件、将其转换为 dataframe 并绘制 1981 年温度变化的过程。
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
dataset=pd.read_csv('daily-min-temperatures.csv')
df=pd.DataFrame(dataset,columns=['Date','Temp'])
print(df.head())
Output:
Date Temp
0 1981-01-01 20.7
1 1981-01-02 17.9
2 1981-01-03 18.8
3 1981-01-04 14.6
4 1981-01-05 15.8
plt.plot(df['Temp'][0:365])
plt.xlabel("Days in the year")
plt.ylabel("Temperature in degree celcius")
plt.show()
图 6-2 中的线形图清楚地显示了 1981 年墨尔本气温的逐日变化。
图 6-2
1981 年墨尔本的温度变化
Matplotlib 包还提供了子情节选项,其中可以在一个单一图形对象中创建子情节布局。在这个时间序列数据示例中,我们可以使用一个简单的for
循环来提取 10 年中每一年的数据,并将其绘制在单独的子图中,如以下代码所示:
y,k=0,1
x=np.arange(1,366)
for i in range(10):
plt.subplot(10,1,k)
plt.plot(x,df['Temp'][y:y+365])
y=y+365
k=k+1
plt.xlabel("Days in the year")
plt.show()
图 6-3 由 10 条副曲线组成,每条曲线显示了从 1981 年到 1990 年某一特定年份的温度变化。因此,多条副曲线的使用使我们能够比较墨尔本十年来的温度变化趋势。
图 6-3
墨尔本 10 年(1981 年至 1990 年)的温度变化
柱状图
直方图的工作原理是将变量中的数据分成不同的范围,称为区间;然后,他们计算每个箱中的数据点,并绘制成竖条。这些类型的图可以很好地给出数值数据的近似分布。箱的宽度,即每个箱中的值的范围,是一个重要的参数,必须通过尝试不同的值来选择最适合数据的一个。
为了演示直方图,让我们考虑一下 Scikit-Learn 库中提供的加利福尼亚住房数据集。该数据集来自 1990 年美国人口普查,每个人口普查区块组使用一行。街区组是美国人口普查局发布样本数据的最小地理单位(街区组通常有 600 到 3,000 人)。数据集由 8 个参数组成,如街区中位收入、街区中位房龄、平均房间数等。和一个目标属性,即加利福尼亚地区的中值房价。数据中共有 20,640 个数据点(行)。以下代码绘制了一个直方图,该直方图根据街区内房屋的中值年龄显示了街区的分布。图 6-4 显示了直方图。较低的数字通常意味着较新的建筑。
图 6-4
根据街区中房屋的中位年龄分配街区
import matplotlib.pyplot as plt
from sklearn.datasets import fetch_california_housing
import pandas as pd
dataset = fetch_california_housing()
df=pd.DataFrame(dataset.data,columns=dataset.feature_names)
print(df.head())
Output:
MedInc HouseAge AveRooms ... AveOccup Latitude Longitude
0 8.3252 41.0 6.984127 ... 2.555556 37.88 -122.23
1 8.3014 21.0 6.238137 ... 2.109842 37.86 -122.22
2 7.2574 52.0 8.288136 ... 2.802260 37.85 -122.24
3 5.6431 52.0 5.817352 ... 2.547945 37.85 -122.25
4 3.8462 52.0 6.281853 ... 2.181467 37.85 -122.25
plt.hist(df['HouseAge'],bins=20)
plt.xlabel("median age of houses")
plt.ylabel("Frequency")
plt.show()
从图 6-4 中的直方图可以看出,街区中的大多数房屋分布在中部,这表明新街区和非常老的街区的数量低于平均年龄的街区。
条形图
数据科学家经常在演示和报告中使用条形图,将分类数据表示为水平或垂直的矩形条,其长度或高度与它们所表示的数据值相对应。通常,其中一个轴代表数据的类别,而另一个轴代表相应的值。因此,条形图是比较不同类别数据的理想选择。条形图也可用于传达一个或多个变量在一段时间内的发展情况。
尽管条形图看起来类似于直方图,但它们之间还是有细微的差别。例如,直方图用于绘制变量的分布,条形图用于比较属于不同类别的变量。直方图将定量数据分组到有限数量的箱中,并绘制这些箱中数据的分布,而条形图用于绘制分类数据。
为了演示这个条形图,让我们考虑一下电信消费者投诉数据集,这是一家美国全球电信公司 Comcast 收到的投诉的集合。这家公司在 2016 年 10 月被罚款 230 万美元,原因是大量客户投诉称,他们被收取了从未使用过的服务费用。该数据集是 2,224 个此类投诉的集合,分为 11 列,如客户投诉、日期、城市、州、邮政编码、状态等。在下面的代码中,首先加载作为 Excel 表提供的数据集,并将其转换为 dataframe。然后,选择包含收到投诉的州的列,使用函数groupby()
将对应于相同州的多个条目组合成一个条目。通过使用函数size()
获得每个状态重复的次数计数,该计数对应于从每个状态收到的投诉数量。然后可以使用sort_values()
功能按计数值的降序对数据进行排序。图 6-5 显示了投诉数量最多的前 10 个州的图表,该图表清楚地显示了哪些州的客户遇到了更多的投诉。该图基本上是根据客户投诉的数量来比较公司在不同州的疑虑。
图 6-5
显示从不同州收到的投诉数量的条形图
import pandas as pd
import matplotlib.pyplot as plt
dataset=pd.read_excel('Comcast_telecom_complaints_data.csv.xlsx')
data=pd.DataFrame(dataset)
print(data.head(3))
Output:
Ticket # Customer Complaint ... Zip code Status
0 250635 Comcast Cable Internet Speeds ... 21009 Closed
1 223441 Payment disappear - service got disconnected ... 30102 Closed
2 242732 Speed and Service ... 30101 Closed
[3 rows x 11 columns]
a=data.groupby("State").size().sort_values(ascending=False).reset_index()
plt.bar(a['State'][0:10],a[0][0:10],align='center')
plt.show()
圆形分格统计图表
饼图通常用于显示数据在不同类别中的分布,以圆形比例段的形式表示占整个数据的百分比。换句话说,每个循环段对应一个特定的数据类别。通过查看饼图,用户可以通过可视化绘图快速掌握分类数据的分布,而不是像条形图那样看到数字百分比。饼图和条形图之间的另一个区别是,饼图用于比较每个数据类别对整体的贡献,而条形图用于比较不同数据类别之间的贡献。
为了演示饼图,让我们考虑一个包含 1980 年至 2013 年加拿大移民详细信息的数据集。该数据集包含每年进出加拿大的移民的各种属性。这些属性包括始发地/目的地名称、地区名称、区域名称等。基于移民的来源/目的地总共有 197 行数据。以下代码绘制了一个饼图,显示了从 1980 年到 2013 年按大陆分类的移民总数:
import pandas as pd
import matplotlib.pyplot as plt
df = pd.read_excel('Canada.xlsx',skiprows=range(20),skipfooter=2)
df.columns = list(map(str, df.columns))
df['Total']=df.sum(axis=1)
df_continents = df.groupby('AreaName', axis=0).sum().reset_index()
print(df_continents)
Output:
AreaName AREA REG ... 2012 2013 Total
0 Africa 48762 49242 ... 38083 38543 765660
1 Asia 45815 109147 ... 152218 155075 3516953
2 Europe 39044 39754 ... 29177 28691 1528488
3 Latin America and the Caribbean 29832 30395 ... 27173 24950 855141
4 Northern America 1810 1810 ... 7892 8503 246564
5 Oceania 12726 13210 ... 1679 1775 93736
数据集作为 Pandas 数据帧加载后,带有数字的列标题(表示数据的年份)被转换为字符串格式。这样做是为了确保当我们在下一步计算移民总数时,不会将这些头衔相加。这个移民总数保存在以名称Total
创建的附加列中。在计算出移民总数后,数据按照标题为AreaName
的列进行分组,该列包含了移民所在洲的详细信息。通过这样做,现在行数从 197 减少到 6,这表明整个数据集被分组到 6 个大洲。
现在,标题为Total
的一栏中给出的来自六大洲的移民总数可以绘制成如图 6-6 所示的饼状图。因此,饼图将包含对应于六大洲的六个圆形段。为了标记绘图中的这些段,标题为AreaName
的列中的洲名被转换为一个列表,并存储在一个变量中,作为绘图函数中的标签。这段代码如下所示:
图 6-6
从 1980 年到 2013 年,来自不同大陆的移民进出加拿大的饼图
t=list(df_continents.AreaName)
plt.pie(df_continents['Total'],labels=t,autopct='%1.1f%%',shadow=True)
plt.show()
其他地块和包装
除了本章讨论的基本图,Matplotlib 包中还有其他可用的图,如等高线图、河流图、3D 图等。,可以根据数据的性质或分析要求来使用。除了 Matplotlib 包,其他可用的包提供了更复杂的绘图,可用于增强不同类别数据的可视化。Seaborn 库就是这样一个包,它可以用来在 Python 中制作统计图形。Seaborn 库提供了更复杂的图形,如箱线图、热图、小提琴图、聚类图等。,可以提供增强的数据可视化。鼓励您探索这些其他类别的地块和库。
七、分析数据
探索性数据分析
探索性数据分析 (EDA)是通过总结数据的特征来理解数据的过程。在为机器学习建模数据之前,这一步很重要。从这种分析中,用户可以提取信息,确定数据中任何问题的根本原因,并找出启动任何开发策略的步骤。简而言之,这种类型的分析探索数据,以理解和识别其中的模式和趋势。做 EDA 没有通用的方法;这取决于我们正在处理的数据。为了简单起见,在本章中,我们将使用常见的方法和图来做 EDA。
选择数据集
为了进行 EDA,我们将使用可以从 Scikit-Learn 库中导入的波士顿住房数据集。该数据集已在第六章中描述。该数据集包含 13 个不同特征属性下的 506 个样本,如城镇人均犯罪率(CRIM)、每所住宅的平均房间数(RM)、放射状公路可达性指数(RAD)等。,目标属性 MEDV 表示业主自住房屋的中值,以千为单位。
- 导入所需的库。
第一步是加载进行 EDA 所需的库。在本章中,我们将使用 Pandas、NumPy 和 Matplotlib 等包进行绘图:
- 导入数据集。
import matplotlib.pyplot as plt
import numpy as np
from sklearn.datasets import load_boston
波士顿住房数据集可以从 Scikit-Learn 库中导入,并保存为boston_data
变量,如以下代码所示:
dataset = load_boston()
更重要的是,大多数开源数据都是以逗号分隔的格式存储的。这种逗号分隔的格式很难获取和分析数据。因此,可以使用 Python 中的 Pandas 包将逗号分隔的数据转换成 dataframe。
import pandas as pd
boston_data=pd.DataFrame(dataset.data,columns=dataset.feature_names)
如果数据集非常大,我们可以使用以下代码显示带有标题的顶部和底部五行:
- 检查数据集中的数据信息。
# To display top 5 rows of data
print(boston_data.head(5))
CRIM ZN INDUS CHAS NOX ... RAD TAX PTRATIO B LSTAT
0 0.00632 18.0 2.31 0.0 0.538 ... 1.0 296.0 15.3 396.90 4.98
1 0.02731 0.0 7.07 0.0 0.469 ... 2.0 242.0 17.8 396.90 9.14
2 0.02729 0.0 7.07 0.0 0.469 ... 2.0 242.0 17.8 392.83 4.03
3 0.03237 0.0 2.18 0.0 0.458 ... 3.0 222.0 18.7 394.63 2.94
4 0.06905 0.0 2.18 0.0 0.458 ... 3.0 222.0 18.7 396.90 5.33
# To display bottom 5 rows of data
print(boston_data.tail(5))
CRIM ZN INDUS CHAS NOX ... RAD TAX PTRATIO B LSTAT
501 0.06263 0.0 11.93 0.0 0.573 ... 1.0 273.0 21.0 391.99 9.67
502 0.04527 0.0 11.93 0.0 0.573 ... 1.0 273.0 21.0 396.90 9.08
503 0.06076 0.0 11.93 0.0 0.573 ... 1.0 273.0 21.0 396.90 5.64
504 0.10959 0.0 11.93 0.0 0.573 ... 1.0 273.0 21.0 393.45 6.48
505 0.04741 0.0 11.93 0.0 0.573 ... 1.0 273.0 21.0 396.90 7.88
在进行数据分析之前,检查数据类型和数据大小等信息、描述数据以及了解数据集中可用的数据量是非常重要的步骤,因为有时数据集中的数值可能存储为字符串数据类型。很难绘制和分析存储为字符串数据类型的数值,因此应该将数值字符串数据类型转换为整数,以便更好地进行分析。可以借助以下代码查看数据集的大小:
boston_data.shape
Output:
(506, 13)
该输出显示数据集有 506 行和 13 列。换句话说,我们可以说数据集有 506 个样本,包含 13 个特征。
然后,可以在以下代码的帮助下查看关于数据集的信息:
boston_data.info()
Output:
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 506 entries, 0 to 505
Data columns (total 13 columns):
# Column Count Non-Null Dtype
--- ------ -------------- -----
0 CRIM 506 non-null float64
1 ZN 506 non-null float64
2 INDUS 506 non-null float64
3 CHAS 506 non-null float64
4 NOX 506 non-null float64
5 RM 506 non-null float64
6 AGE 506 non-null float64
7 DIS 506 non-null float64
8 RAD 506 non-null float64
9 TAX 506 non-null float64
10 PTRATIO 506 non-null float64
11 B 506 non-null float64
12 LSTAT 506 non-null float64
dtypes: float64(13)
memory usage: 51.5 KB
boston_data.dtypes
Output:
CRIM float64
ZN float64
INDUS float64
CHAS float64
NOX float64
RM float64
AGE float64
DIS float64
RAD float64
TAX float64
PTRATIO float64
B float64
LSTAT float64
dtype: object
而且借助describe()
函数,可以看到最小值、最大值、均值等数据的分布情况。可以使用以下代码查看波士顿数据的描述:
boston_data.describe()
Output:
CRIM ZN INDUS ... PTRATIO B LSTAT
count 506.000000 506.000000 506.000000 ... 506.000000 506.000000 506.000000
mean 3.613524 11.363636 11.136779 ... 18.455534 356.674032 12.653063
std 8.601545 23.322453 6.860353 ... 2.164946 91.294864 7.141062
min 0.006320 0.000000 0.460000 ... 12.600000 0.320000 1.730000
25 percent 0.082045 0.000000 5.190000 ... 17.400000 375.377500 6.950000
50 percent 0.256510 0.000000 9.690000 ... 19.050000 391.440000 11.360000
75 percent 3.677083 12.500000 18.100000 ... 20.200000 396.225000 16.955000
max 88.976200 100.000000 27.740000 ... 22.000000 396.900000 37.970000
修改数据集中的列
如果数据集需要进行预处理,则需要对数据进行修改,例如删除不必要的列、添加虚拟列、删除重复的列、对列进行编码以及对数据进行规范化。当许多列不用于分析时,删除不必要的列更为重要。删除这些列是使数据更简洁、更可靠的更好的解决方案。可以使用以下代码删除波士顿数据集中不必要的列:
boston_data =boston_data.drop(['CRIM','ZN','LSTAT'])
print(boston_data.head(5))
Output:
INDUS CHAS NOX RM AGE DIS RAD TAX PTRATIO B
0 2.31 0.0 0.538 6.575 65.2 4.0900 1.0 296.0 15.3 396.90
1 7.07 0.0 0.469 6.421 78.9 4.9671 2.0 242.0 17.8 396.90
2 7.07 0.0 0.469 7.185 61.1 4.9671 2.0 242.0 17.8 392.83
3 2.18 0.0 0.458 6.998 45.8 6.0622 3.0 222.0 18.7 394.63
4 2.18 0.0 0.458 7.147 54.2 6.0622 3.0 222.0 18.7 396.90
在前面的代码中,删除了CRIM
、ZN
和LSTAT
列,只显示 10 列数据。
重命名列名有助于用户提高数据的可读性。在下面的代码中,列名DIS
被重命名为Distance
:
boston_data= boston_data.rename(columns={"DIS":"Distance"})
boston_data.head(5)
INDUS CHAS NOX RM AGE Distance RAD TAX PTRATIO B
0 2.31 0.0 0.538 6.575 65.2 4.0900 1.0 296.0 15.3 396.90
1 7.07 0.0 0.469 6.421 78.9 4.9671 2.0 242.0 17.8 396.90
2 7.07 0.0 0.469 7.185 61.1 4.9671 2.0 242.0 17.8 392.83
3 2.18 0.0 0.458 6.998 45.8 6.0622 3.0 222.0 18.7 394.63
4 2.18 0.0 0.458 7.147 54.2 6.0622 3.0 222.0 18.7 396.90
识别重复项、删除重复项和检测异常值已经在前面的章节中讨论过了。
统计分析
更好地理解手头的数据可以大大简化数据科学家的工作,这就是统计学派上用场的地方。统计学可以提供必要的工具来识别数据中的结构,并且这种见解可以证明在构建最适合我们的数据的模型中是有价值的。从简单的分析到创建自学模型,统计在数据方面的作用各不相同。在这一节中,我们将介绍各种类型的分布、数据的统计度量以及使数据符合分布的方法。
在讨论分布之前,我们先了解一下数据是如何与概率联系在一起的。当我们考虑一个数据集时,它通常代表总体中的一个样本。例如,如果我们有一个由一所学校所有学生的身高和体重组成的数据集,经过一些统计分析后,从该数据开发的模型可用于预测另一所学校学生的身高和体重。我们手里的数据集只是一个样本,而总体可能由许多学校组成。
我们遇到的数字数据在本质上可能是连续的,也可能是离散的。两者的区别在于,连续数据可以取任何值,而离散数据只能取某些值。例如,每天制造的汽车数量、从客户处收到的反馈数量等数据。实际上是离散的,而诸如身高、体重、湿度、温度等数据。,表示连续数据。
概率分布是统计学中的一个基本概念,它提供了一种方法来表示随机变量的可能值和相应的概率。概率质量函数 (PMF)表示离散概率分布,概率密度函数 (PDF)表示连续概率分布。下一节将讨论数据科学家需要了解的一些常见分布。
均匀分布
均匀分布,也称为矩形分布,具有恒定的概率。换句话说,所有的结果都有相同的发生概率。在均匀分布的情况下,结果的数量可能是无限的。均匀分布最常见的例子是掷骰子,所有六个结果的概率都是 1/6。让我们通过绘制公平骰子实验结果的概率来说明均匀分布。换句话说,骰子的每个面出现的概率是相等的。图 7-1 为分布图。
图 7-1
公平模具试验的均匀分布
import numpy as np
import matplotlib.pyplot as plt
probabilities = np.full((6),1/6)
events = [1,2,3,4,5,6]
plt.bar(events,probabilities)
plt.xlabel('Die roll events')
plt.ylabel('Event Probability')
plt.title('Fair die - Uniform Distribution')
plt.show()
如果通过将数值数据划分为多个条块来绘制数据集的直方图,并且发现所有条块都具有相等的分布,则可以说数据集是均匀分布的。
二项分布
顾名思义,这种分布用于只有两种可能结果的情况。遵循二项式分布的随机变量X
取决于两个参数:
-
二项分布情况下的试验次数
n
必须是固定的,试验被认为是相互独立的。换句话说,特定试验的结果并不取决于先前试验的结果。 -
每个事件只有两种可能的结果:成功或失败。比如说,每次试验的成功概率都是一样的。
因此,Python 中的二项式分布函数通常将两个值作为输入:试验次数n
和成功概率p
。为了理解二项分布,让我们来看看常见的抛硬币实验:
from scipy.stats import binom
import matplotlib.pyplot as plt
import numpy as np
n=15 # no of times coin is tossed
r_values = list(range(n + 1))
x=[0.2,0.5,0.7,0.9] #probabilities of getting a head
k=1
for p in x:
dist = [binom.pmf(r, n, p) for r in r_values ]
plt.subplot(2,2,k)
plt.bar(r_values,dist)
plt.xlabel('number of heads')
plt.ylabel('probability')
plt.title('p= percent.1f' percentp)
k+=1
plt.show()
在前面的代码中,我们有 15 次投掷硬币的尝试。每次试验得到人头的概率保持不变,并且每次试验的结果都独立于之前的结果。使用scipy
包的stats
模块中可用的binom.pmf
函数计算二项式分布。使用for
循环对不同的成功概率重复实验,图 7-2 显示了结果分布图。
图 7-2
抛 15 次硬币的二项分布
图 7-2 显示了不同成功概率的抛硬币实验的二项分布。第一个子图显示了当获得人头的概率为 0.2 时的二项式分布。这意味着有 20%的机会得到一个头。15 次投掷的 20%是 3,这意味着在 15 次投掷中有很高的概率得到 3 个头。因此,概率最大为 3。可以看出,二项分布具有钟形响应。当成功的概率较低时,响应向左侧倾斜,随着概率的增加,响应向右侧移动,如其余子图所示。
在数据科学的各个领域都会遇到二项分布。例如,当一家制药公司想要测试一种新疫苗时,那么只有两种可能的结果:疫苗有效或者无效。此外,单个患者的结果是一个独立的事件,不依赖于针对不同患者的其他试验。二项式分布也可以应用于各种商业问题。例如,考虑在销售部门工作的人整天打电话推销他们公司的产品。呼叫的结果是销售是否成功,并且结果独立于每个工人。类似地,在具有二元结果的商业中还有许多其他领域可以应用二项式分布,因此它在商业决策中起着重要的作用。
正态分布
正态分布,也称为高斯分布,通常是一条以平均值为中心的钟形曲线,在这里概率最大,我们离平均值越远,概率越小。这意味着越接近平均值的值出现的频率越高,而越远离平均值的值出现的频率越低。这种分布取决于两个参数:数据的平均值(μ)和标准差(σ)。正态分布的概率密度函数(pdf
)可以如下给出:
为了说明pdf
函数,考虑下面的代码。创建一个包含 100 个-10 到 10 范围内的值的数组x
,使用scipy
包的stats
模块中的norm.pdf
函数计算x
的pdf
函数。使用for
循环对平均值 0、2.5、5 和 7.5 的四个不同值计算pdf
函数。如果未给出平均值,norm.pdf
功能将采用默认值 0。
from scipy.stats import norm
import matplotlib.pyplot as plt
import numpy as np
mean=[0.0,2.5,5,7.5] # mean values for the normal distribution
x=np.linspace(-10,10,100) # array of 100 numbers in the range -10 to 10
for m in mean:
y=norm.pdf(x,loc=m)
plt.plot(x,y,label='mean= %.1f' %m)
plt.xlabel('x')
plt.ylabel('pdf(x)')
plt.legend(frameon=True)
plt.show()
图 7-3 显示正态分布产生一个以平均值为中心的钟形曲线。也就是说,曲线在均值点处达到最大值,当我们远离均值时,曲线开始向两边递减。请注意,我们没有指定标准差的值。在这种情况下,norm.pdf
函数采用默认值 1。
图 7-3
不同平均值的正态分布图
同样,让我们保持平均值不变,并使用以下代码绘制不同标准分布值的分布图:
from scipy.stats import norm
import matplotlib.pyplot as plt
import numpy as np
stdev=[1.0,2.0,3.0,4.0] # standard deviation values for the normal distribution
x=np.linspace(-10,10,100)
for s in stdev:
y=norm.pdf(x,scale=s)
plt.plot(x,y,label='stdev= %.1f' %s)
plt.xlabel('x')
plt.ylabel('pdf(x)')
plt.legend(frameon=True)
plt.show()
从图 7-4 中,我们可以看到所有四条曲线都以默认平均值零为中心。随着标准差σ值的增加,密度分布在较宽的范围内。换句话说,随着标准偏差值的增加,数据的分布更加远离平均值,并且很可能更多的观察值更加远离平均值。
图 7-4
不同标准偏差值的正态分布图
正态分布的一个重要属性使其成为数据科学家的重要统计分布,这就是经验规则。根据该规则,如果我们按照标准偏差在 x 轴上划分观察范围,则分别有大约 68.3%的值落在平均值的一个标准偏差内,95.5%的值落在两个标准偏差内,99.7%的值落在三个标准偏差内。如果数据可以符合正态分布,则此经验规则可用于识别数据中的异常值。这一原则被用于异常值检测的 Z 值中,我们在第五章中讨论过。
波士顿房价数据的统计分析
让我们以波士顿房价数据集为例,尝试根据要素的统计属性来确定可用于数据建模的最佳要素。正如我们已经讨论过的,波士顿数据集由 506 个案例(506 × 13)的 13 个不同特征组成。除了这些特征,由变量 MEDV 表示的自有住房的中值(以千计)被确定为目标。也就是说,给定 13 个不同的特征,房子的中值将被估计。首先使用 Pandas 包将数据集中的要素转换为数据帧。然后将目标变量添加到该数据帧的最后一列,使其维数为 506 × 14。下面的代码说明了这一点:
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from sklearn.datasets import load_boston
import matplotlib.pyplot as plt
dataset = load_boston()
boston_data=pd.DataFrame(dataset.data,columns=dataset.feature_names)
boston_data['MEDV'] = dataset['target']
print(boston_data.head())
CRIM ZN INDUS CHAS NOX ... TAX PTRATIO B LSTAT MEDV
0 0.00632 18.0 2.31 0.0 0.538 ... 296.0 15.3 396.90 4.98 24.0
1 0.02731 0.0 7.07 0.0 0.469 ... 242.0 17.8 396.90 9.14 21.6
2 0.02729 0.0 7.07 0.0 0.469 ... 242.0 17.8 392.83 4.03 34.7
3 0.03237 0.0 2.18 0.0 0.458 ... 222.0 18.7 394.63 2.94 33.4
4 0.06905 0.0 2.18 0.0 0.458 ... 222.0 18.7 396.90 5.33 36.2
[5 rows x 14 columns]
一旦我们手头有了数据,最好的方法就是绘制所有特征的直方图,这样我们就可以了解它们分布的性质。Pandas 包中的hist
函数可用于一次性绘制所有要素的直方图,而不是单独绘制每个要素的直方图,如下图所示:
fig, axis = plt.subplots(2,7,figsize=(16, 16))
boston_data.hist(ax=axis,grid=False)
plt.show()
从图 7-5 中,我们可以看到目标变量 MEDV 的分布像正态分布。
图 7-5
波士顿数据集要素的直方图
此外,如果我们观察所有其他参数,参数 RM(其表示每个住所的平均房间数量)的分布也类似于目标 MEDV。因此,RM 绝对可以用于数据集建模。此外,参数 DIS(到五个波士顿就业中心的距离的加权平均值)和 LSTAT(较低地位人口的百分比)具有类似的分布。参数年龄的分布(1940 年以前建造的业主自住单元的比例)与这两个参数正好相反。与目标参数相比,其余参数的分布不太显著。由于这三个参数似乎正相关或负相关,因此使用这三个参数来构建模型是没有意义的。所以,我们必须看看这三个参数中,哪一个与我们的目标变量 MEDV 相关。最好的方法是使用 Pandas 包中的corr
函数来测量这些参数之间的相关性,如下所示:
cols=['RM','AGE','DIS','LSTAT','MEDV']
print(boston_data[cols].corr())
RM AGE DIS LSTAT MEDV
RM 1.000000 -0.240265 0.205246 -0.613808 0.695360
AGE -0.240265 1.000000 -0.747881 0.602339 -0.376955
DIS 0.205246 -0.747881 1.000000 -0.496996 0.249929
LSTAT -0.613808 0.602339 -0.496996 1.000000 -0.737663
MEDV 0.695360 -0.376955 0.249929 -0.737663 1.000000
从这些结果可以看出,对角线元素都是 1,这意味着最大相关,它们代表自相关值。如果我们查看与我们的目标参数 MEDV 对应的行,我们可以看到 RM 与 MEDV 正相关,正如我们之前查看直方图分布时所判断的那样。还可以看出,参数 LSTAT 与 MEDV 负相关更大,这意味着这两个参数之间将存在反比关系。RM 和 LSTAT 分别与 MEDV 的散点图可以让我们更好地理解这种关系,如下图所示:
plt.subplot(1,2,1)
plt.scatter(list(boston_data['RM']),list(boston_data['MEDV']))
plt.xlabel('RM')
plt.ylabel('MEDV')
plt.subplot(1,2,2)
plt.scatter(list(boston_data['LSTAT']),list(boston_data['MEDV']))
plt.xlabel('LSTAT')
plt.ylabel('MEDV')
plt.show()
图 7-6 证实了我们使用分布图和相关值得出的结论。可以看出,RM 和 MEDV 是正相关的;也就是说,房主自住住房的中值随着每套住房平均房间数的增加而增加。同样,可以看出 LSTAT 和 MEDV 是负相关的;也就是说,自住住房的中值随着较低地位人口百分比的增加而下降。因此,这两个参数是构建波士顿住房数据集模型的良好选择。从图中还可以看出,RM 与 MEDV 图中存在一些异常值,在进一步处理之前,可以使用第五章中讨论的技术进行处理。
图 7-6
RM 和 LSTAT 与 MEDV 的散点图
八、从数据中学习
从数据中学习意味着从数据中提取信息,并使用它进行预测/预报,以便根据它做出明智的决策。这一领域正变得越来越受欢迎,因为它适用于不同行业的各种应用,如金融、医疗保健、教育、计算机视觉、政治等。
从数据中学习用于各种情况,例如不需要解析解,或者没有关于问题的清晰模型,或者需要基于先前的信息进行预测,等等。基本上,有三种学习技术:监督学习、非监督学习和强化学习。监督学习利用对过程的观察来开发模型。基于过程的输入和输出观察(即,输入和输出数据)来训练监督学习模型。在无监督学习中,训练数据没有任何关于输出的信息。无监督模型根据数据的特征对模型进行分类。此外,无监督模型可用于发现数据中的模式、通过聚类相似数据来检测异常值、发现数据的结构等。强化学习模型也没有利用关于输出的正确信息。但是,它可能会输出一些关于输出质量的信息。
这一章重点描述利用波士顿数据集开发学习模型的技术。然后,我们将在 Raspberry Pi 中实现学习模型,并分析从传感器获取的行业数据。该实施将在第九章作为案例研究进行讨论。
使用回归根据数据进行预测
回归寻找数据集中变量之间的关系。回归用于确定一个变量对另一个变量的影响。此外,它还可用于根据变量以前的数据预测变量。回归模型可用于许多领域,例如预测经济趋势、预测商业销售、预测某些政策的影响以及预测医疗保健应用中的血压水平。
在回归中,开发模型需要两种变量:输入和输出。输入变量是数据集中用于预测输出变量的变量。线性回归中的输入变量通常表示为 x。一个输出变量是用于预测的变量,表示为 y。方程 8-1 显示了线性回归的方程。
Ye=α+βX … (8-1)
这里,Ye 是估计输出变量,Y 是实际输出变量,α和β是线性回归模型的参数。例如,如果我们想买一台电视,并试图估计电视的成本(即输出变量),我们使用输入变量,如电视的大小。现在,α、β和 Y 分别被(随机)选为 2、5 和€170。电视机的尺寸(即输入变量)为 32 英寸,估计电视机成本的线性回归模型的估计输出如等式 8-2 所示。
叶=2+5 *32
= €162 …(8-2)
因此,根据等式 8-2 ,当电视尺寸为 32 英寸时,电视的成本为€162,这更接近电视的实际成本:€170。如果我们将参数α和β分别修改为 0.1 和 0.5,则电视的估计成本计算如下:
叶=0.1+0.5 *32
= €16.1
电视的价格急剧变化为€16.1 英镑。这表明α和β的选择在预测输出变量中是重要的。因此,开发线性回归模型的目的是通过最小化实际输出 Y 和估计输出 Ye 之间的差异来找到α和β。有许多方法可以找到α和β的最佳参数。然而,普通的最小二乘法通常用于寻找α和β的最佳参数。
OL 方法使用输入变量的协方差和方差来识别参数α和β,如等式 8-3 所示。
……(8-3)
这里,是实际输出和输入变量的平均值。
现在让我们考虑波士顿数据集。RM 变量用于表示每个住宅的平均房间数,目标变量(即输出变量)MEDV 用于表示房主自住房屋的中值(以千为单位)。对于线性回归建模,我们将 RM 视为输入变量,将 MEDV 视为输出变量。因为 RM 和 MEDV 彼此紧密相连,所以可以使用下面的代码实现这些变量的线性回归模型。为了识别α和β参数,使用普通的最小二乘法。
from sklearn.linear_model import LinearRegression
import matplotlib.pyplot as plt
import numpy as np
from sklearn.datasets import load_boston
import pandas as pd
dataset = load_boston()
boston_data=pd.DataFrame(dataset.data,columns=dataset.feature_names)
Target=pd.DataFrame(dataset.target,columns=['target'])
# two variable for regression model
X1=boston_data['RM']
X=X1.to_numpy() # dataframe is converted in to array for arithmetic operations
Y=dataset.target
xmean=np.mean(X)
ymean=np.mean(Y)
xcov=np.multiply((X-xmean),(Y-ymean))
xvar=(X-xmean)**2
# linear regression model
beta=xcov.sum()/xvar.sum()
alpha=ymean-(beta*xmean)
print(beta)
print(alpha)
OLS 方法的α和β值输出如下所示:
Beta value is 9.10210898118031
Alpha value is -34.67062077643857
线性回归模型可通过使用先前的α和β值来开发,如等式 8-4 中所示。
叶=-34.6706+9.1021*X … (8-4)
这里,X 是输入变量 RM。可以使用以下代码实现它:
# prediction model
ye=alpha+beta*X
让我们绘制实际输出变量 Y 和估计模型 Ye,这清楚地显示了它们之间的关系,并可使用以下代码绘制(见图 8-1 ):
图 8-1
实际产出变量与估计线性回归模型
# plot
plt.figure(figsize=(12,6))
plt.plot(X1,ye)
plt.plot(X,Y,'ro')
plt.title('Actual Vs Predicted')
plt.xlabel('X')
plt.ylabel('Y')
plt.show()
使用 Scikit-Learn 进行线性回归
在前面的示例中,线性回归预测器使用一个输入变量来预测输出。基于给定的线性回归,可以用多个变量预测输出(见等式 8-5 )。
ye =α+β+β
**方程 8-5 使用了 n 个输入变量来预测输出变量 Ye。如果我们考虑波士顿数据集的所有输入变量(总共 13 个输入变量)和输出变量(MEDV),则使用多个变量的回归模型可以使用 Scikit-Learn 实现,并在以下代码中给出:
from sklearn.linear_model import LinearRegression
import matplotlib.pyplot as plt
import numpy as np
import seaborn as sns
from sklearn.datasets import load_boston
import pandas as pd
dataset = load_boston()
boston_data=pd.DataFrame(dataset.data,columns=dataset.feature_names)
Target=pd.DataFrame(dataset.target,columns=['target'])
# two variable for regression model
X=boston_data
Y=Target
lm=LinearRegression()
model=lm.fit(X,Y)
print(f'alpha={model.intercept_}')
print(f'beta={model.coef_}')
Ye=model.predict(X)
Y1=Y.to_numpy()
E=np.mean(Y1-Ye)
MSE=E**2
print(MSE)
# plot
plt.figure(figsize=(12,6))
plt.scatter(Y1,np.arange(0,len(Y)),color='red')
plt.title('Actual')
plt.xlabel('No of samples')
plt.ylabel('Y')
#plt.figure(figsize=(12,6))
plt.scatter(Ye,np.arange(0,len(Y)),color='blue')
plt.legend(['Actual output data','Estimated Linear regression model', ])
plt.show()
Output:
For α and β values
alpha=[36.45948839]
beta=[[-1.08011358e-01 4.64204584e-02 2.05586264e-02 2.68673382e+00
-1.77666112e+01 3.80986521e+00 6.92224640e-04 - 1.47556685e+00
3.06049479e-01 -1.23345939e-02 -9.52747232e-01 9.31168327e-03
-5.24758378e-01]]
为了评估模型的质量,我们可以使用均方差(MSE)度量。MSE 计算实际输出和预测输出之间的误差平方的平均值。
1.8463848451630152e-29
图 8-2 比较实际输出(Y)和预测输出(Ye)。
图 8-2
使用线性回归将实际输出数据与预测输出数据进行比较
主成分分析
主成分分析是一种统计方法,用于提取大型数据集中的强特征。换句话说,可以通过从数据集中提取重要特征来降低数据集的维度。PCA 使用标准化来识别特征之间的距离,并实现协方差信息来识别特征之间的任何关系。然后,借助于特征向量和特征值,计算主成分。主成分用于提取强特征,即降低数据的维度。此外,主成分被用于优化 k-means 聚类技术的聚类数目,并且波士顿数据集被用于这项工作。波士顿数据集有 13 个要素。在第一步中,使用以下代码在 PCA 的帮助下识别波士顿数据集中的强特征:
from sklearn.decomposition import PCA
from sklearn.preprocessing import StandardScaler
#config InlineBackend.figure_format='retina'
# Load in the data
from sklearn.datasets import load_boston
dataset = load_boston()
df=pd.DataFrame(dataset.data,columns=dataset.feature_names)
#df = pd.read_csv('2013_2014_cleaned.csv')
# Standardize the data to have a mean of ~0 and a variance of 1
X_std = StandardScaler().fit_transform(df)
# Create a PCA instance: pca
pca = PCA(n_components=13)
principalComponents = pca.fit_transform(X_std)
# Plot the explained variances
features = range(pca.n_components_)
plt.bar(features, pca.explained_variance_ratio_, color='black')
plt.xlabel('PCA features')
plt.ylabel('variance %')
plt.xticks(features)
plt.show()
# Save components to a DataFrame
PCA_components = pd.DataFrame(principalComponents)
从图 8-3 中,我们可以看到前三个特征在数据集中给出了很好的方差。
图 8-3
数据集中与方差相关的要素
因此,可以选择五个特征进行聚类。对于聚类,可以使用 k-means 聚类。为了确定最佳的聚类数目,PCA 与 k-means 聚类算法相适应,并利用所选择的主成分计算聚类模型的惯性。下面的代码标识了聚类模型的惯性,并绘制了具有惯性的聚类数(即 k)(该代码延续了前面的 PCA 代码)。图 8-4 显示了惯性与簇数(k)的关系图。从图 8-4 可以得出结论,在集群数(k = 5)后,惯性没有发生显著变化。因此,对于给定的数据集,可以选择五个作为簇头的最佳数量。
图 8-4
集群数量与惯性的关系
ks = range(1, 10)
inertias = []
for k in ks:
# Create a KMeans instance with k clusters: model
model = KMeans(n_clusters=k)
# Fit model to samples
model.fit(PCA_components.iloc[:,:3])
# Append the inertia to the list of inertias
inertias.append(model.inertia_)
plt.plot(ks, inertias, '-*', color='blue')
plt.xlabel('number of clusters, k')
plt.ylabel('inertia')
plt.xticks(ks)
plt.show()
基于 K-均值聚类的离群点检测
聚类是一种用于无监督学习问题的探索性数据分析技术,即当没有关于数据的先验知识时。聚类背后的思想是将数据集中的数据点分组为多个子组,称为簇。每个聚类中的数据点比其他聚类中的数据点更类似于同一聚类中的其他点。
广泛用于聚类操作的技术是基于质心的方法,称为 k 均值聚类,这是一种迭代算法,将数据集分成 k 个不重叠的聚类,其中每个数据点仅分配给一个聚类。将数据点分配给聚类的条件是数据点到聚类质心的重叠聚类距离的平方和最小。k-means 算法的工作原理如下:
-
指定分类的数量。
-
为每个簇随机选择中心点,也称为质心。
-
计算每个数据点与聚类质心之间的距离,并将这些点分配给距离最小的聚类。
-
通过取分配给聚类的所有数据点的平均值,重新计算每个聚类的质心。
-
重复步骤 3 和 4,直到质心没有变化。
除了对数据进行聚类,k-means 算法还可用于识别数据中存在的异常值。这种方法背后的思想是以升序对从每个数据点到聚类质心的距离进行排序,并将距离质心最大的一部分数据点视为异常值。
为了说明这种方法,让我们看一下波士顿住房数据集。正如我们在第七章中所讨论的,每套住宅的平均房间数(RM)和数千套自有住房的中值(MEDV)高度相关。因此,这两个参数被视为聚类分析算法的二维数据,如以下代码所示:
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from sklearn.cluster import KMeans
from sklearn.preprocessing import scale
from numpy import sqrt, random, array, argsort
from sklearn.datasets import load_boston
dataset = load_boston()
boston_data=pd.DataFrame(dataset.data,columns=dataset.feature_names)
x=boston_data['RM']
y=dataset.target
x=x.to_numpy() # convert pandas series data to numpy array
x=x.reshape(x.shape[0],1)
x=scale(x)
y=y.reshape(y.shape[0],1)
y=scale(y)
X=np.zeros((np.shape(x)[0],2))
X[:,0]=x[:,0]
X[:,1]=y[:,0]
数据集的要素首先被加载到数据帧中。对应于特征 RM 的列从数据帧移动到变量x
。因为存储在变量x
中的 RM 特征是 Pandas 系列格式,所以使用to_numpy
函数将它转换为 NumPy 数组,使其适用于 k-means 算法。然后,这个数组被重新整形,因为它必须与目标变量 MEDV 一起存储在一个二维数组中。然后通过使用sklearn
包中的scale
函数对参数进行额外的缩放。这样做是为了在特定范围内对数据进行标准化。以类似的方式,目标 MEDV 特征(默认为 NumPy 数组)也存储在变量y
中,进行整形和缩放。然后将两个变量x
和y
组合在变量X
中,从而使其成为一个二维变量。下面的代码演示了对该变量应用 k-means 算法的过程:
km=KMeans(n_clusters=1).fit(X)
distance = km.transform(X)
indexes = np.argsort(distance.ravel())[::-1][:20]
从sklearn
包中导入的Kmeans
函数可以用来实现聚类算法。这个函数可以接受输入,比如聚类数、最大迭代次数等等。在我们的代码中,我们给定了一个输入值,表示集群的数量。换句话说,我们将把所有数据点分组到一个集群中。由于没有指定最大迭代次数,该函数采用默认值 300 次迭代。在将 k-means 算法拟合到我们的数据之后,下一步是计算每个数据点到聚类质心的距离。这是通过使用sklearn
包中的transform
功能完成的。产生的距离变量也是一个 n 维 NumPy 数组。因此,首先使用ravel
函数将其展平,然后将展平后的数组按降序排序。这意味着数组从距离聚类中心较远的数据点开始,到距离聚类中心较近的数据点结束。这种排序是使用argsort
函数完成的,该函数提供与排序后的数据点相对应的索引。
我们知道离群值是远离数据集中其他数据点的异常数据点。但是什么被认为是不正常的是留给知道分析要求的分析师去做的。在波士顿住房数据的情况下,异常值是房间较少的家庭的高中值(定价过高),房间较多的家庭的低中值(错误),以及超出特定限制的房间组合数量的中值,这取决于要求。为了检测这些异常值,我们从排序的索引数组中随机选取前 20 个索引,并在所有数据点的散点图中标记与这些索引对应的数据点,如下所示:
f,ax=plt.subplots()
ax.scatter(X[:,0],X[:,1])
ax.scatter(X[indexes][:,0],X[indexes][:,1],edgecolors='r',
facecolors='none', s=100)
plt.xlabel('MEDV')
plt.ylabel('RM')
f.show()
图 8-5 显示了每套住宅的平均房间数与业主自住房屋的中值的散点图。数据中的异常值由周围有红圈的点表示。
图 8-5
使用 k-means 聚类算法检测异常值**
九、案例研究
本章介绍了实施数据科学概念的真实案例研究。考虑了三种场景:利用脑电信号进行人类情感分类的数据科学概念、图像数据和工业 4.0。
对于人类情绪分类,使用 NeuroSky MindWave 移动套件提取人类的 EEG 信号,并在 Raspberry Pi 中接收和分析 EEG 信号。NeuroSky MindWave 移动套件和 Raspberry Pi 可以通过蓝牙连接。在图像数据中,应用数据科学步骤对图像数据进行预处理以供进一步分析。在工业 4.0 案例研究中,树莓 Pi 充当了一个本地化的云。在这里,许多传感器连接到 Raspberry Pi,来自传感器的信号被转换为结构化数据,以便进一步分析和可视化。
案例研究 1:人类情感分类
情绪是一种以强烈的大脑活动为特征的感觉。大量的研究集中在识别人类情感的广泛应用上,例如医疗、健康、机器人和脑机接口(BCI)应用。有许多识别人类情感的方法,例如面部情感识别、从语音信号中识别音调、从 EEG 信号中识别情感等。其中,从脑电信号中进行分类是一种简单方便的方法。此外,EEG 信号具有关于人类情绪的有用信息。因此,许多研究人员致力于使用 EEG 信号对人类情绪进行分类。通过在头皮上放置电极来测量电信号,EEG 信号被用于记录人类大脑的活动。
让我们考虑一个简单的情绪识别系统,该系统使用单个电极设备,即 NeuroSky MindWave 设备,用于从参与者获取 EEG 信号,并在机器学习算法(即 k-nearest neighborhood(k-NN)和神经网络(NNs))的帮助下将他们的情绪分类为快乐、害怕或悲伤。
方法学
被包括的参与者来自不同的年龄组,他们分别接受了来自世界公认的数据库日内瓦情感图片数据库(GAPED)的不同类别的图片的实验。这些图像包括婴儿、快乐场景、虐待动物、人类关切、蛇和蜘蛛的图像,每一个都点燃参与者不同的情绪。然后,为所有参与者获取与记录的 EEG 信号相对应的特征数据集,然后这些特征接受像 k-NN 和 NN 这样的机器学习模型,该模型将每个信号分类为三种情绪之一:高兴、害怕或悲伤。
资料组
用于数据收集的两个设备是 NeuroSky MindWave 移动设备和 Raspberry Pi 3 板。NeuroSky MindWave 设备可用于安全记录脑电图信号。该设备由耳机、耳夹和传感器(电极)臂组成。耳机的接地电极在耳夹上,而 EEG 电极在传感器臂上,戴上耳机后,传感器臂将放在眼睛上方的前额上。该设备使用单节 AAA 电池,可持续八小时。
这个设备通过蓝牙连接到一个 Raspberry Pi 3 板上,如图 9-1 所示。它是第三代 Raspberry Pi 型号,配有四核处理器、1GB 内存和多个用于连接各种设备的端口。它还配有无线局域网和蓝牙支持,可以帮助连接无线设备,如我们的 MindWave Mobile。NeuroSky 设备供应商提供的软件安装在 Pi 板上,用于从设备获取串行数据。
图 9-1
Raspberry Pi 与通过蓝牙连接的 MindWave 手机
通过蓝牙连接 Raspberry Pi 和 MindWave Mobile
有两种方法可以将 MindWave 移动设备与 Raspberry Pi 连接。第一个是将 MindWave 手机与 Raspberry Pi 桌面连接起来。最初打开 Raspberry Pi,启动进入 Raspberry Pi 操作系统,然后打开 MindWave 移动蓝牙。然后点击 Raspberry Pi 操作系统中的蓝牙符号,这将显示准备与 Raspberry Pi 配对的设备。在列表中,可以选择 MindWave Mobile,并且可以使用供应商规定的配对密码 0000。现在,MindWave 移动设备与 Pi 配对,如图 9-2 所示。
图 9-2
树莓桌面与 MindWave Mobile 配对
可以通过蓝牙连接提取来自 MindWave 移动设备的信号。将覆盆子与 MindWave 连接的另一种方法是使用 Pypi 0.1.0。在 https://github.com/cttoronto/python-MindWave-mobile
对步骤进行了说明。该链接提供了脑电波信号的阿尔法、贝塔和伽马值的数据。然而,在这项工作中,数据集是从脑电图信号发展而来。
数据收集过程
参与者坐在一个黑暗的小房间里,房间也是无线电静默的,以防止他们受到听觉和视觉的干扰。实验前会解释条款和条件,如果他们有任何不适,就会被告知停止测试。还向参与者提供了一份人工评分表,以评估他们在每张照片中的情绪。总共有 15 名参与者,记录了三种不同情绪的 15 个信号,从而得到总共 15 × 3 = 45 个 EEG 信号。这些情绪是快乐的、害怕的和悲伤的。
最初,使用 NeuroSky 设备从用户处获取原始 EEG 信号。从大脑中提取的原始 EEG 信号不能直接用于进一步处理。当受试者在特定的持续时间内受到基于视觉输入的情绪刺激时,所产生的情绪反应将是时变的。因此,重要的是识别大脑峰值活动的持续时间,并且仅提取该持续时间的特征,以便增强分类结果。为了实现这一点,记录在实验开始一分钟后开始,这样就有足够的时间使用与特定情绪相对应的图像幻灯片来模拟参与者的情绪。此外,为了避免处理大数据,只考虑每秒 512 个样本的 15 秒数据,从而将数据大小减少到 15 × 512 =7680 个样本,如图 9-3 所示。图 9-3 显示整个记录持续时间的信号,红色表示大脑活动高峰期的信号,图 9-4 单独显示这部分。
图 9-4
大脑峰值活动时提取的脑电信号
图 9-3
整个记录期间的 EEG 信号样本
从脑电波信号中提取的特征
脑电信号是大脑功能信息的丰富来源。为了从脑电信号中获取有意义的信息,需要提取信号的不同属性。从脑电信号中共提取了 9 个不同的时域属性,这些特征说明如下。
延迟与振幅比(LAR)被定义为最大信号时间与最大信号振幅之比;见方程 9-1。
(9-1)
这里,tsmax= {t|s(t)=smax}为最大信号值出现的时间,smax= max {s(t)}为最大信号
峰峰值信号值(PP)定义为最大信号值和最小信号值之差,如公式 9-2 所示。
sPP=smax-smin(9-2)
这里, s max 和 s min 分别是信号最大值和最小值。
峰峰值时间窗(PPT)定义为最大信号时间和最小信号时间之差,如公式 9-3 所示。
tPP=tsmax—tsmin(9-3)
这里, t s max 和 t s min 是最大和最小信号值出现的时间。
峰峰值斜率(PPS)定义为峰峰值信号值(PP)与峰峰值时间值(PPT)之比,如公式 9-4 所示。
(9-4)
这里, s pp 为峰间信号值, t pp 为峰间时间窗。
信号功率(P)定义为恒定振幅下无限时间存在的信号。信号功率如公式 9-5 所示。
(9-5)
信号的平均值(μ)定义为所选区域端点之间数据样本的平均值,并显示平均值。方程 9-6 给出了信号的平均值。
(9-6)
其中 N 是信号中的样本总数。
峰度(K)是频率分布曲线峰值的锐度,在方程 9-7 中给出。
(9-7)
这里,m?? 4 和m2 是信号的四阶矩和方差。
迁移率(M)定义为信号的一阶方差与信号方差之比,在等式 9-8 中给出。
(9-8)
复杂度(C)被定义为流度的一阶导数除以流度,在等式 9-9 中给出。
(9-9)
九个时域特征的所有这些公式的 Python 代码被编写为单个函数,稍后在主程序中调用该函数。这个函数在大脑情绪活动的高峰期取 15 秒的 EEG 信号和相应的时间样本,图示如下:
def eegfeat(ynew,tnew):
from scipy.stats import kurtosis
# latency to amplitude ratio
smax=max(ynew)
locmax=np.where(ynew==smax)
tsmax=tnew[locmax]
lar1=tsmax/smax
lar=lar1[0]
# peak to peak signal value
smin=min(ynew)
locmin=np.where(ynew==smax)
tsmin=tnew[locmin]
spp=smax-smin
# peak to peak time window.
tpp1=tsmax+tsmin
tpp=tpp1[0]
# peak to peak slope
spps=spp/tpp
# mean value of signal
m=np.mean(ynew)
# kurtosis
k=kurtosis(ynew)
# mobility and complexity
n=ynew.shape[0]
dynew=np.diff(ynew)
ddynew=np.diff(dynew)
mx2=np.mean(np.power(ynew,2))
mdx2=np.mean(np.power(dynew,2))
mddx2=np.mean(np.power(ddynew,2))
mob=mdx2/mx2
complexity=np.sqrt(mddx2/(mdx2-mob))
mobility=np.sqrt(mob)
# signal power
tt=np.power(ynew,2)
s=0
for i in np.arange(0,tt.shape[0]):
s=s+tt[i]
signalpower=s/ynew.shape[0]
feat = [lar, spp, tpp, spps, m, k, complexity, mobility, signalpower]
return feat
非结构化数据到结构化数据集
既然我们有了从 EEG 信号中提取特征的函数,下一步就是开发代码来获得结构化数据集。首先,在一个for
循环中使用pd.read_csv
函数逐一加载对应于三种不同情绪的所有 15 名参与者的 EEG 信号。EEG 信号作为 dataframe 加载后,首先删除时间戳,然后将剩余列中的振幅值转换为 NumPy 数组。然后,将每次迭代中获得的阵列叠加到新的变量上,从而提供由对应于 45 个不同 EEG 信号的 45 列组成的最终阵列。然后,该数组的每一列被传递给前面创建的eegfeat
函数,该函数通过提供一个大小为 9×45 的最终特征数组来提供与每一列(每个信号)相对应的九个特征。数据集在表 9-1 中给出,并作为emotion_data1.xls
保存在 Excel 表格中。最后,使用sklearns
模块中的StandardScaler
和拟合功能缩放特征。这种缩放的工作原理是,首先计算所有 45 个信号的每个特征的平均值和标准偏差,然后从所有值中减去平均值,并将该差值除以标准偏差。以下代码说明了特征提取过程:
表 9-1
人类情感数据集特征
|利比亚
|
包裹邮递(Parcel Post)
|
演示文档
|
再附言
|
力量
|
平均
|
峭度
|
机动性
|
复杂性
|
标签
|
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| 0.016024 | Six hundred and seventy-eight | 11.18505 | 60.61663 | -0.04396 | 6.543546 | 0.864608 | 0.272718 | 3095.76805 | 快乐 |
| 0.021638 | Eight hundred and five | 17.95937 | 44.8234 | -0.13187 | 1.147168 | 0.908352 | 0.323672 | 8861.53844 | 快乐 |
| 0.013645 | One thousand one hundred and fifty-six | 18.50241 | 62.47835 | -0.13599 | 11.54561 | 0.909418 | 0.253198 | 5615.14591 | 快乐 |
| 0.020861 | Nine hundred and thirteen | 20.6941 | 44.11885 | -0.19559 | 4.77647 | 0.869794 | 0.274665 | 7488.51785 | 快乐 |
| 0.027464 | One thousand and fifty-one | 29.44133 | 35.69811 | 0.073972 | -0.04979 | 0.920326 | 0.739543 | 17478.1566 | 快乐 |
| 0.003051 | One thousand and fifty-six | 3.215262 | 328.4335 | 0.555873 | -0.70347 | 0.829795 | 0.648545 | 26836.2039 | 快乐 |
| 0.009142 | Seven hundred and eight | 5.996875 | 118.0615 | -1.4202 | 1.801014 | 0.807949 | 0.203007 | 5224.79068 | 快乐 |
| 0.044871 | Five hundred and seventy-seven | 27.64032 | 20.8753 | 0.774106 | 5.355427 | 0.872217 | 0.221742 | 3616.08585 | 快乐 |
| 0.025742 | One thousand and seventeen | 25.99948 | 39.11617 | -0.02595 | 15.2165 | 0.909882 | 0.275513 | 4281.25747 | 快乐 |
| 0.037152 | Five hundred and ninety-five | 19.31892 | 30.79882 | 0.490321 | 2.851322 | 0.908083 | 0.295561 | 3403.52707 | 快乐 |
| 0.017313 | Nine hundred and forty | 15.40826 | 61.00625 | 0.107773 | 0.582757 | 0.772671 | 0.179429 | 14818.1692 | 快乐 |
| 0.015074 | One thousand six hundred and twenty-six | 22.58107 | 72.00723 | -2.5419 | 2.847854 | 0.813119 | 0.216638 | 25075.0479 | 快乐 |
| 0.034336 | Eight hundred and twelve | 24.72197 | 32.84528 | 0.310597 | 9.089532 | 0.908852 | 0.326948 | 3481.33912 | 快乐 |
| 0.012292 | Nine hundred and eighteen | 12.0211 | 76.36575 | -0.1356 | 12.5699 | 0.88202 | 0.218335 | 4644.36149 | 快乐 |
| 0.001722 | Three thousand and sixty | 4.613882 | 663.2159 | -0.01663 | 34.31637 | 0.827843 | 0.097603 | 30433.0506 | 快乐 |
| 0.018688 | Four hundred and two | 8.89569 | 45.19043 | 0.032993 | 4.738717 | 0.882602 | 0.423034 | 1124.38087 | 恐惧 |
| 0.040525 | Five hundred and seventy-nine | 26.50345 | 21.84621 | 0.254913 | 2.882232 | 0.906008 | 0.304122 | 4124.12924 | 恐惧 |
| 0.020358 | One thousand five hundred and seventeen | Twenty-one point six two | 70.1665 | -0.11243 | 40.33238 | 0.916268 | 0.270259 | 7677.61001 | 恐惧 |
| 0.057451 | Three hundred and eighty-three | 22.63576 | 16.92013 | 0.012297 | 0.515585 | 0.915245 | 0.524744 | 1586.28054 | 恐惧 |
| 0.02732 | Seven hundred and thirty-five | 23.11238 | 31.80113 | -0.4819 | 2.371013 | 0.840896 | 0.430931 | 5550.91016 | 恐惧 |
| 0.010694 | One thousand five hundred and sixty-seven | 16.40448 | 95.52269 | 0.170683 | -0.28034 | 0.906462 | 0.697989 | 35493.9062 | 恐惧 |
| 0.027347 | Three hundred and seventy-eight | 10.88423 | 34.72915 | 0.02714 | 0.021038 | 0.779173 | 0.311332 | 2214.34304 | 恐惧 |
| 0.038418 | Seven hundred and seventeen | 29.58198 | 24.23773 | -0.75375 | 2.735193 | 0.886821 | 0.155092 | 7204.2341 | 恐惧 |
| 0.023423 | One thousand one hundred and fifteen | 25.76507 | 43.27564 | -0.3602 | 2.435107 | 0.860817 | 0.38882 | 12420.8748 | 恐惧 |
| 0.002859 | Four thousand four hundred and twenty | 12.57976 | Three hundred and fifty-one point three five eight | 4.408442 | 5.755933 | 0.6055 | 0.123552 | Two hundred and ninety-six thousand nine hundred and seventy-eight point zero six nine | 恐惧 |
| 0.025219 | Nine hundred and seventy-one | 24.71416 | 39.28922 | -0.08303 | 1.857694 | 0.766682 | 0.165919 | Eleven thousand four hundred and twenty-five point eight one four | 恐惧 |
| 0.015038 | Two thousand five hundred and sixteen | 21.53405 | 116.8382 | -2.2011 | 30.45224 | 0.93143 | 0.384335 | 22246.7576 | 恐惧 |
| 0.017566 | Eight hundred and thirty-three | 14.40422 | 57.83028 | -0.42794 | 2.695262 | 0.842994 | 0.172786 | 8374.92373 | 恐惧 |
| 0.019647 | Nine hundred and thirty-five | 20.27608 | 46.11346 | 0.316469 | 3.61666 | 0.9339 | 0.34069 | 8803.85773 | 恐惧 |
| 0.006667 | One thousand four hundred and four | 12.45475 | 112.7281 | 0.157155 | 27.96396 | 0.854443 | 0.211712 | 6280.25928 | 恐惧 |
| 0.01213 | Nine hundred and ninety-two | 14.45891 | 68.6082 | -0.29278 | 7.918369 | 0.826067 | 0.157843 | 8135.80814 | “悲伤” |
| 0.016787 | One thousand one hundred and eighty-seven | 19.33846 | 61.38029 | 0.17177 | 5.274371 | 0.862185 | 0.195176 | 16224.2062 | “悲伤” |
| 0.025382 | One thousand and seventeen | 24.46803 | 41.56444 | 0.228652 | 14.78168 | 0.863634 | 0.195593 | 5841.32003 | “悲伤” |
| 0.012709 | One thousand five hundred and twenty-four | 18.68212 | 81.57532 | -0.20364 | 19.9148 | 0.873179 | 0.190631 | 10495.4369 | “悲伤” |
| 0.047707 | Four hundred and ninety-nine | 24.13986 | 20.6712 | 0.102337 | 3.259416 | 0.864654 | 0.309553 | 2265.85228 | “悲伤” |
| 0.006046 | One thousand nine hundred and thirty-three | 10.67717 | 181.0405 | 0.758343 | 2.349937 | 1.003761 | 0.682323 | 24010.3048 | “悲伤” |
| 0.020863 | One thousand three hundred and five | 24.40943 | 53.46295 | 0.003427 | 0.833297 | 0.768599 | 0.488095 | 22671.4565 | “悲伤” |
| 0.020863 | One thousand three hundred and five | 24.40943 | 53.46295 | 0.003427 | 0.833297 | 0.768599 | 0.488095 | 22671.4565 | “悲伤” |
| 0.033872 | Eight hundred and sixty-three | 25.47207 | 33.88025 | 0.105906 | 9.95777 | 0.858691 | 0.220224 | 5482.11932 | “悲伤” |
| 0.02912 | Five hundred and thirty-five | 15.78331 | 33.89658 | -0.01854 | 0.234449 | 0.896769 | 0.619883 | 4619.19634 | “悲伤” |
| 0.000649 | Five thousand and seventy | 3.141034 | One thousand six hundred and fourteen point one one eight | -4.11542 | 6.964611 | 0.795685 | 0.173283 | Two hundred and thirty-one thousand six hundred and thirty-eight point nine nine six | “悲伤” |
| 0.015449 | Eight hundred and fifty-six | 14.58393 | 58.69474 | 0.157962 | 1.97371 | 0.786113 | 0.225146 | 6764.73669 | “悲伤” |
| 0.005224 | Three thousand eight hundred | 20.26826 | 187.4852 | 0.570607 | 22.94134 | 0.791691 | 0.094596 | 63679.0805 | “悲伤” |
| 0.016787 | One thousand one hundred and eighty-seven | 19.33846 | 61.38029 | 0.17177 | 5.274371 | 0.862185 | 0.195176 | 16224.2062 | “悲伤” |
| 0.008937 | Four hundred and ninety-four | 4.879542 | One hundred and one point two three nine | 0.109418 | 0.696421 | 0.769311 | 0.304871 | 3185.67894 | “悲伤” |
import pandas as pd
import numpy as np
from sklearn.preprocessing import StandardScaler
F=512
a=np.zeros([(75*F)-(60*F),1])
for i in np.arange(1,4):
for j in np.arange(1,16):
filename = 'G:/Anand-EEG/EEG Database/Dataset/user'+str(j)+'_'+str(i)+'.csv'
s=pd.read_csv(filename)
s.drop('Time',inplace = True, axis=1)
s1=s[' Value'][60*F:75*F]
a=np.column_stack((a,s1.to_numpy()))
a=np.delete(a,0,1)
tnew=np.linspace(0,15,a.shape[0])
features=np.zeros([9,45])
for i in np.arange(0,a.shape[1]):
parameters=eegfeat(a[:,i],tnew)
for j in np.arange(0,features.shape[0]):
features[j,i]=parameters[j]
scaler = StandardScaler()
features=scaler.fit(features)
脑电图数据的探索性数据分析
要读取emotion_data.xls
文件,使用以下代码:
import pandas as pd
emotion_data= pd.read_excel('\file_path\emotion_data1.xls')
To show the keys and first 5 dataset using the below code
print(emotion_data.keys())
Output:
Index(['LAR', 'PP', 'PPT', 'PPS', 'Power', 'Mean', 'Kurtosis', 'Mobility',
'Complexity', 'Label'],
dtype='object')
print(emotion_data.head(5))
Output:
LAR PP PPT PPS ... Kurtosis Mobility Complexity Label
0 0.016024 678 11.18505 60.61663 ... 0.864608 0.272718 3095.76805 'Happy'
1 0.021638 805 17.95937 44.82340 ... 0.908352 0.323672 8861.53844 'Happy'
2 0.013645 1156 18.50241 62.47835 ... 0.909418 0.253198 5615.14591 'Happy'
3 0.020861 913 20.69410 44.11885 ... 0.869794 0.274665 7488.51785 'Happy'
4 0.027464 1051 29.44133 35.69811 ... 0.920326 0.739543 17478.15660 'Happy'
通过使用以下代码,可以查看最后五个数据点:
print(emotion_data.tail(5))
LAR PP PPT ... Mobility Complexity Label
40 0.000649 5070 3.141034 ... 0.173283 231638.99600 'Sad'
41 0.015449 856 14.583930 ... 0.225146 6764.73669 'Sad'
42 0.005224 3800 20.268260 ... 0.094596 63679.08050 'Sad'
43 0.016787 1187 19.338460 ... 0.195176 16224.20620 'Sad'
44 0.008937 494 4.879542 ... 0.304871 3185.67894 'Sad'
[5 rows x 10 columns]
要检查数据的形状,请使用以下代码:
print(emotion_data.shape)
Output:
(45, 10)
通过使用下面的代码,可以显示情感数据中的数据类型。
print(emotion_data.dtypes)
Output:
LAR float64
PP int64
PPT float64
PPS float64
Power float64
Mean float64
Kurtosis float64
Mobility float64
Complexity float64
Emotion Label object
dtype: object
数据集中的修改包括删除列和使用第八章中的探索性数据分析部分更改数据。
图 9-5 显示了情感数据集中平均数据的直方图。
图 9-5
每种情绪的均值直方图
使用学习模型对情绪进行分类
提取特征后的下一步是应用分类算法来识别对应于信号的情感。由于我们已经知道与我们所使用的每个信号相对应的情绪,因此使用监督学习算法进行分类显然更好。在此之前,另一个重要的任务是将我们的数据拆分成训练和测试数据。在每种情绪的 15 个信号中,让我们考虑对应于用于训练的前 12 个信号的数据和对应于用于测试的剩余 3 个信号的数据。此外,应该创建与训练和测试数据相对应的标签。为此,我们将把情绪快乐标记为 1,恐惧标记为 2,悲伤标记为 3。下面的代码演示了数据和标签的这种拆分:
- k-神经网络
m1=np.ones((15,),dtype=int)
ids=np.concatenate((m1,2*m1,3*m1),axis=0)
x_train=np.concatenate((features[:,0:12],features[:,15:27],features[:,30:42]),axis=1)
x_test=np.concatenate((features[:,12:15],features[:,27:30],features[:,42:45]),axis=1)
y_train=np.concatenate((ids[0:12],ids[15:27],ids[30:42]))
y_test=np.concatenate((ids[12:15],ids[27:30],ids[42:45]))
让我们首先使用 k-NN 算法根据数据对情绪进行分类。k-NN 是一种简单的监督机器学习算法,它对可用数据进行分类,并根据相似性得分将新数据分配到特定类别。k-NN 算法的工作原理是找出测试数据和训练数据之间的距离。找到到每个训练数据的距离后,训练数据按距离值的升序排序。在该有序数据中,选择前 k 个数据,并且该算法将在该数据中出现最频繁的标签分配给测试数据。欧几里德距离是 k-NN 算法最常用的距离度量,两个数据点 x i 和 y i 之间的距离由以下表达式给出:
使用sklearn
Python 模块中的KNeighborsClassifier
包实现 k-NN 分类。使用此包的情感分类代码如下所示:
from sklearn.neighbors import KNeighborsClassifier
from sklearn.metrics import confusion_matrix, classification_report
classifier = KNeighborsClassifier(n_neighbors=16)
classifier.fit(x_train.T, y_train)
y_pred = classifier.predict(x_test.T)
cm=confusion_matrix(y_test, y_pred)
print("confusion matrix\n",cm)
print("Accuracy:",(sum(np.diagonal(cm))/9)*100)
Output:
confusion matrix
[[1 0 2]
[1 2 0]
[2 0 1]]
Accuracy: 44.44444444444444
前面代码中的参数n_neighbors
表示 k 的值,我们选择为 16。因此,考虑 16 个邻居来做出分类决定。首先,计算测试数据和所有其他训练数据之间的距离。然后,训练数据点按照计算距离的升序排序。在排序的数据中,考虑对应于前 16 个数据的标签,并且将 16 个数据中出现较多的标签分配给测试数据。对所有九个测试信号重复这一过程(每种情绪三个),使用混淆矩阵显示结果,使用表 9-2 中的信息可以更好地理解混淆矩阵。
表 9-2
基于 k-NN 的情感分类混淆矩阵
| |快乐
|
恐惧
|
悲伤
|
| --- | --- | --- | --- |
| 幸福的 | one | Zero | Two |
| 害怕 | one | Two | Zero |
| 悲哀的 | Two | Zero | one |
在混淆矩阵中,行标题可以被视为输入,列标题可以被视为输出。例如,如果我们考虑第一行,对应于“快乐”情绪的三个 EEG 信号中只有一个被正确识别,而剩余的两个信号被错误地分类为“悲伤”情绪。类似地,在第二行中,对应于“恐惧”情绪的两个信号被正确分类,而在第三行中,对应于“悲伤”情绪的一个信号被正确识别。为了更好地理解,混淆矩阵中的对角线元素表示分类正确的数据,其余元素表示分类错误。总的来说,九个测试信号中有四个被正确分类。使系统的准确率达到 44.44%。
案例研究 2:影像数据的数据科学
虽然今天可用的数字设备可以捕捉比人类视觉更高分辨率和更多细节的图像,但计算机只能将这些图像视为代表颜色的数值阵列。计算机视觉是指能够使计算机理解数字图像和视频的技术。计算机视觉系统可以被视为人类视觉系统的复制,使计算机能够像人类一样处理图像和视频。计算机视觉系统被用于许多应用中,例如人脸识别、自动驾驶车辆、医疗保健、安全、增强现实等。
任何计算机视觉系统的第一步都是捕捉感兴趣的图像。这可以通过许多手段来完成,如照相机、显微镜、x 光机、雷达等。,取决于应用的性质。然而,捕获的原始图像不能直接使用,需要进一步处理。由于各种原因引入的噪声,原始图像可能不具有期望的质量。因此,在进一步处理之前,增强捕获的原始图像是至关重要的。为了使计算机能够从图像中学习,有时有必要使用分析技术从图像中提取有用的信息。在本节中,我们将了解如何使用与 Raspberry Pi 板接口的摄像机捕捉图像,并讨论为进一步处理准备原始图像所涉及的步骤。
第一步是将 USB 网络摄像头连接到我们的 Raspberry Pi 板,如图 9-6 所示。
图 9-6
带网络摄像头的树莓派
为此,我们必须在 Pi 配置设置中启用 SSH 和 Camera。安全外壳(SSH)有助于通过本地网络远程连接 Raspberry Pi,而启用摄像头配置有助于将网络摄像头与 Pi 板连接。这可以通过以下步骤完成:
图 9-8
支持摄像头和 SSH 的接口选项
图 9-7
软件配置工具窗口
-
在 Raspberry Pi 操作系统的终端窗口中键入命令
sudo raspi-config
。这将打开软件配置工具窗口,如图 9-7 所示。 -
进入接口选项,如图 9-8 所示,同时启用 SSH 和 Camera。
-
重启 Raspberry Pi 设备。
重新启动完成后,在终端窗口中运行lsusb
命令,并检查连接的 USB 网络摄像头是否列出。然后打开 Python IDE,键入以下代码,使用网络摄像头捕捉并保存图像:
import cv2
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
camera=cv2.VideoCapture( )
ret, img = camera.read( )
cv2.imwrite('image.png',img)
img= cv2.cvtColor(img,cv2.COLOR_BGR2RGB)
plt.imshow(img)
plt.axis('off')
plt.show( )
如代码所示,OpenCV
包用于处理 Python 中的图像。为了捕捉图像,首先创建一个VideoCapture
对象。read()
函数用于使用创建的对象捕捉图像,然后存储在变量'img'
中。然后可以使用imwrite()
功能保存拍摄的图像。OpenCV
以 BGR 格式而非标准 RGB 格式显示图像。因此,在显示之前,首先使用cv2.color
功能将图像转换为 RGB 图像。要显示图像,可以使用 Matplotlib 包中的imshow()
函数。由于使用此软件包创建的图默认情况下启用了轴值,因此在显示图像时必须移除轴。这可以通过将 Matplotlib 包中的axis
函数设置为off
状态来实现。图 9-9 显示了使用先前代码捕获的样本图像。
图 9-9
使用连接到 Raspberry Pi 板的网络摄像头捕获的图像
探索性图像数据分析
这张图片显示了一些静止的物体躺在白纸上。为了理解采集的图像数据,最好打印图像的数据类型和大小,如下所示:
print(type(img))
print(img.shape)
Output:
<class 'numpy.ndarray'>
(719, 1206, 3)
捕获的图像是一个 NumPy 数组。使用网络摄像头捕捉的图像通常是 RGB 格式,其中有三个像素平面:红色、蓝色和绿色。换句话说,图像中的每个像素由三个值组成,这三个值代表红色、蓝色和绿色的比例,从而导致可见光谱中的各种颜色。印刷图像形状中的数字 3 表示三个平面;即,图像由对应于 RGB 的三个平面组成,每个平面的大小为 719× 1206 像素。在许多应用中,其他细节如边缘、形状等。,在图像中比颜色信息更重要。例如,如果我们的目标是识别给定图像中的静止物体,物体的形状将比颜色更重要。在这种情况下,可以使用以下代码将三平面 RGB 图像转换为单平面灰度图像:
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
plt.imshow(gray,cmap= 'gray')
plt.axis('off')
plt.show( )
print(gray.shape)
Output:
(719, 1206)
图 9-10 显示了一个单平面灰度图像,其中图像中的颜色被移除。这可以从前面代码中打印的图像大小看出来。现在灰度图像的尺寸在单个平面上仅为 719×1206。在一些情况下,捕获的图像可能具有由图像传感器中的缺陷引起的一些缺失值。这些值也可以反映在灰度图像中,并且可以通过将图像转换为数据帧来检测和处理这些值,如下所示:
df=pd.DataFrame(gray)
s=df.isnull( ).sum( ).sum( )
print(s)
if s!=0:
df=df.ffill(axis=0)
gray=df.to_numpy( )
Output:
0
isnull( )
功能可用于检测图像的行和列中是否存在缺失值。sum( )
函数可用于计算数据帧中沿行和列的缺失值的数量。如果sum( )
函数的结果不等于零,那么图像由缺失值组成,它们可以使用ffill( )
函数进行处理,该函数将每个缺失值替换为其上的像素。这种向前填充或向后填充的方法不会导致图像中任何可见的变化,因为除了图像的边缘之外,像素值通常被紧密地放置在图像中。如前面的代码所示,缺失值的数量为 0;即图像中没有丢失的值。一旦检查了图像并处理了缺失值,就可以使用 Pandas 中的to_numpy( )
将数据帧转换回 NumPy 数组。因为像素值被紧密放置,所以在图像中的许多区域可能存在相同像素值的重复。由于这个属性,重复值的识别在图像数据的情况下是不相关的。
图 9-10
图像转换为灰度
在自然光下使用 USB 网络摄像头或 Pi 摄像头通常会导致图像质量不佳。因此,处理缺失值后的下一步是绘制图像的直方图。直方图将给出图像对比度的概念,如图 9-11 所示。下面的代码说明了这一点:
图 9-11
灰度图像的直方图
plt.hist(gray.ravel( ),bins=256)
plt.xlabel('bins')
plt.ylabel('No of pixels')
plt.show( )
灰度图像中的像素值范围从 0(代表黑色)到 255(代表白色)。前面代码中的hist( )
函数绘制了该范围内每个像素值计数的条形图。这个图显示了我们正在处理的图像的对比度。图 9-7 显示了我们的灰度图像的直方图。可以看出,大多数像素都在范围(120,160)内。如果像素的分布集中在较低的面元中,那么我们有一个低对比度的图像,反之亦然。因此,根据该图,可以决定图像是否需要对比度调整。
图像质量差的另一个原因可能是由各种因素引起的噪声的存在。当观察捕获的图像时,这些噪声可以以颗粒的形式被视觉感知。在这种情况下,在进行进一步处理之前,必须消除这些噪声。有许多不同种类的噪声,如高斯噪声、椒盐噪声等。,有许多不同类型的过滤器可以用来消除那些超出本书范围的噪音。让我们来看看图像处理中经常使用的一种特殊滤波器,称为平均滤波器。它是一种低通滤波器,可用于去除数字图像中的高频内容。这种过滤的工作原理是,在图像的各个维度上传递一个特定大小(比如 3×3)的内核,取内核区域下所有像素的平均值,并用该平均值替换中心元素。整体效果是营造一种模糊的效果。下面的代码说明了我们的图像平均过滤器的实现。图 9-12 显示了滤波后的图像。
图 9-12
平均滤波得到的图像
blur=cv2.blur(gray,(3,3))
plt.imshow(blur)
plt.axis('off')
plt.show( )
为模型准备图像数据
一旦预处理步骤完成,下一步就是为学习模型分析或准备图像。这可以通过两种方式实现。第一种方法是提取代表有用信息的特征,并将其用于建模。提取的特征可以是另一个变换图像,或者它们可以是从原始图像提取的属性。有许多可以从图像中提取的特征,并且特定特征的选择取决于我们的应用的性质。对这些众多特性的讨论超出了本书的范围。相反,我们将讨论一个特殊的功能:边缘检测。
边缘代表图像中的高频内容。Canny 边缘检测是一种使用多阶段方法来检测图像中各种边缘的算法。可以通过使用OpenCV
中的Canny( )
函数在 Python 中实现,如下面的代码所示。图 9-13 显示了边缘检测处理后的图像。
图 9-13
边缘检测后的图像
edge_img=cv2.Canny(gray,100,200)
plt.imshow(edge_img,cmap='gray')
plt.axis('off')
plt.show( )
第二种方式是直接将图像馈送给深度学习模型。深度学习是一种流行的机器学习方法,越来越多地用于分析和学习图像。这种方法可以直接从图像中学习有用的信息,不需要任何特征提取。可以将图像调整到不同的形状,然后馈送到学习模型,或者可以将图像阵列转换成一维向量,然后馈送到模型。
使用深度神经网络的对象检测
对象检测是一种用于识别真实世界中的对象的技术,例如椅子、书、汽车、电视、花、动物、人等等。,来自图像或视频。这项技术可以检测、识别和辨认图像中的多个对象,以便更好地理解或从现实环境中提取信息。对象检测在计算机视觉应用中起着重要的作用,如自动驾驶汽车、监控、工业自动化和视障人士的辅助设备。Python 环境中有许多模块可用于对象检测,如下所示:
-
基于特征的目标检测
-
Viola Jones 对象检测
-
具有猪特征的 SVM 分类
-
深度学习对象检测
-
单次多盒探测器(SSD)物体探测
-
你只看一次(YOLO)模型物体探测
-
基于区域的卷积神经网络(R-CNN)
-
更快的 R-CNN
这里,我们使用了单次多盒检测器来识别图像或视频中的多个对象。C. Szegedy 等人在 2016 年 11 月提出了单触发多盒探测器。SSD 可以解释如下:
-
单镜头:在这个阶段,图像的定位和分类是在单个前向传递网络的帮助下完成的。
-
Multibox :这表示在一个图像中绘制多个对象的边界框。
-
检测器:这是一个物体检测器,对图像或视频中的物体进行分类。
图 9-14 显示了单触发多盒探测器的架构。
在该架构中,输入图像的尺寸被认为是 300×300×3。VGG-16 架构用作基础网络,完全连接的网络被丢弃。VGG-16 体系结构是流行的,并且利用迁移学习技术具有很强的分类能力。这里,VGG-16 架构的卷积层的一部分被用在早期阶段。固态硬盘的详细说明可在 https://towardsdatascience.com/understanding-ssd-multibox-real-time-object-detection-in-deep-learning-495ef744fab
找到。
图 9-14
单触发多盒探测器( https://towardsdatascience.com/understanding-ssd-multibox-real-time-object-detection-in-deep-learning-495ef744fab
)架构
多框体系结构是一种用于识别边界框坐标的技术,并且基于两个损失函数,例如置信度损失和位置损失。置信损失使用分类熵来测量为边界框识别对象的置信水平。位置损失测量边界框的距离,它远离图像中的对象。为了测量距离,使用了 L2 范数。多盒损耗可借助以下公式测量:
多盒损失=置信度损失+α*位置损失
这提供了关于边界框离预测对象有多远的信息。以下代码使用 DNN 权重实现 SSD 配置文件,以检测 COCO 名称中的对象。可以从 https://github.com/AlekhyaBhupati/Object_Detection_Using_openCV
下载用于检测可可名中物体的 DNN 权重(即frozen_inference_graph.pb
)的 SSD 配置文件(即ssd_mobilenet_v3_large_coco_2020_01_14.pbtxt
)。
可可名字在这种情况下被称为常见对象,可可名字的数据集可在官方网站获得: https://cocodataset.org/#home
。COCO 分割了椅子、汽车、动物、人类等常见对象。,并且这些分割的图像可以用于训练深度神经网络。参见图 9-15 和图 9-16 。
图 9-15
用于对象识别的输入图像
代码如下:
图 9-16
输出带有已识别对象的图像
import cv2
thres = 0.5# Threshold to detect object
cap = cv2.VideoCapture(0)
cap.set(3,1280)
cap.set(4,720)
cap.set(10,70)
classNames= []
classFile = 'coco.names'
with open(classFile,'rt') as f:
classNames = f.read().rstrip('\n').split('\n')
configPath = 'ssd_mobilenet_v3_large_coco_2020_01_14.pbtxt'
weightsPath = 'frozen_inference_graph.pb'
net = cv2.dnn_DetectionModel(weightsPath,configPath)
net.setInputSize(320,320)
net.setInputScale(1.0/ 127.5)
net.setInputMean((127.5, 127.5, 127.5))
net.setInputSwapRB(True)
print('1st done')
while True:
success, img = cap.read()
classIds, confs, bbox = net.detect(img, confThreshold=thres)
print(classIds, bbox)
if len(classIds) != 0:
for classId, confidence,box in zip(classIds.flatten(),confs.flatten(),bbox):
cv2.rectangle(img,box,color=(0,255,0),thickness=2)
cv2.putText(img,classNames[classId-1].upper(),(box[0]+10,box[1]+30),
cv2.FONT_HERSHEY_COMPLEX,1,(0,255,0),2)
cv2.putText(img,str(round(confidence*100,2)),(box[0]+200,box[1]+30),
cv2.FONT_HERSHEY_COMPLEX,1,(0,255,0),2)
cv2.imshow("Output",img)
# Hit 'q' on the keyboard to quit!
当代码被执行时,来自网络摄像头的视频帧被使用OpenCV capture
函数捕获。然后,每一帧都被插入到已经训练好的 SSD-DNN 模型中,用于识别对象。SSD-DNN 模型基于可可名称对对象进行分类,并在检测到的图像上创建具有可可名称标签和准确性的边界框。图 9-15 的视频文件作为输入输入到之前的程序中。该图形中有椅子、书和鼠标等物体。从图 9-16 可以清楚地得出结论,基于 SSD 的 DNN 模型识别三个物体的准确率为椅子 72.53%,书 67.41%,鼠标 81.52%。
案例研究 3:工业 4.0
工业 4.0 代表了制造业的第四次革命。工业的第一次革命(即工业 1.0)是在蒸汽动力的帮助下创造机械能,以提高装配线的生产率。第二次革命(即工业 2.0)将电力纳入流水线,提高生产率。第三次革命(即工业 3.0)结合了计算机来实现工业过程的自动化。目前,工业 4.0 正在采用计算机、数据分析和机器学习工具,以便在传感器获取的数据的帮助下做出智能决策或监控过程。物联网(IoT)最近在获取数据和传输数据以进行远程访问方面发挥了重要作用。
图 9-17 描述了工业 4.0 中的基本工艺流程。最初,物理系统的数据是在传感器的帮助下收集的,并被制成数字记录。然后,物理系统的数字记录被发送到服务器系统进行实时数据处理和分析。数据科学技术应用于预处理和准备数据的阶段。然后现代学习算法可以通过用学习的模型预测输出来用于智能决策。此外,可视化技术被用来监控物理系统的实时数据。在这里,Raspberry Pi 可以用作服务器或本地化的云来进行实时数据处理。
图 9-17
工业 4.0 框图
树莓 Pi 作为工业 4.0 的本地化云
要实现工业 4.0,需要一台精密的计算机来连接设备,收集数据,处理数据。收集的数据可以存储在云服务中,以供进一步处理。然而,如今,云服务的订阅费用更高,适合高利润的公司。小规模的公司会希望实现一个本地化的云来进行实时处理。此外,本地化云方法可以提供数据安全性,因为它是在现场的,攻击者无法通过远程访问进行入侵。
正如第三章所讨论的,树莓 Pi 可以作为一个本地化的云,可以连接传感器、物联网设备、其他附近的计算机和手机,如图 9-18 所示。复杂的计算机也可以充当本地化的云,但是它们占据了很大的空间。此外,很难在偏远地区安装计算机。树莓派的优点是占用空间少,可以在偏远地区实现。基于此,树莓 Pi 作为工业 4.0 框架的本地化云,如图 9-19 所示。
图 9-19
工业 4.0 框架与树莓派
图 9-18
作为本地化云的树莓派
树莓派的工业 4.0 框架中有三个模块可用。这些模块正在收集来自传感器的数据,使用相机收集信息,并将 Raspberry Pi 与其他计算机连接。
从传感器收集数据
我们将使用温度和湿度传感器来测量温度和湿度。将 DHT 11/22 传感器模块连接到 Raspberry Pi,如第三章所示。以下代码收集 100 秒内的温度和湿度百分比,并将收集的数据存储为 CSV 文件。
import Adafruit_DHT
import time
from datetime import datetime
DHT_SENSOR = Adafruit_DHT.DHT11
DHT_PIN = 17
data = []
while _ in range(100):
humidity, temperature = Adafruit_DHT.read(DHT_SENSOR, DHT_PIN)
if humidity is not None and temperature is not None:
now = datetime.now()
dt_string = now.strftime("%d/%m/%Y %H:%M:%S")
data.append(dt_string,humidity,temperature)
time.sleep(60*5)
df = pd.DataFrame(data)
df.to_csv('data.csv',index=None,header=None)
CSV 文件将类似于表 9-3 。
表 9-3
湿度和温度传感器的时间戳数据
| 17/05/2020 01:05:14 | Twenty-six point two four | Sixty-nine point nine one | | 17/05/2020 01:10:14 | Twenty-six point two four | Seventy point six five | | 17/05/2020 01:15:14 | Twenty-six point two two | Sixty-eight point eight seven | | 17/05/2020 01:20:14 | Twenty-six point one five | Seventy point one one | | 17/05/2020 01:25:14 | Twenty-six point one one | Sixty-nine point zero two |在 Raspberry Pi 中准备行业数据
我们将使用由两列数据组成的数据集,这两列数据来自连接到 Raspberry Pi 板的温度和湿度传感器;在 28 小时内,每 5 分钟记录一次数据。因此,数据集本质上是.csv
格式的时间序列数据。在进行预处理之前,最好先了解数据集。因此,第一步是读取文件并打印内容,如下所示:
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
dataset=pd.read_csv('datasets_384649_744229_log_temp.csv')
print(dataset.head())
Output
Date Time Temperature Humidity
0 3/14/19 19:33:07 T=22.0 H=20.0
1 3/14/19 19:38:10 T=22.0 H=20.0
2 3/14/19 19:43:11 T=22.0 H=26.0
3 3/14/19 19:48:14 T=22.0 H=26.0
4 3/14/19 19:53:15 T=22.0 H=20.0
从打印的数据集的前五个条目来看,很明显,在我们开始分析数据之前,需要清理数据。分析不需要由条目的日期和时间组成的前两列,因此可以删除这两列。由实际数据组成的第三和第四列是字符串和数字的混合。我们必须过滤掉这些不合适的值,并将数据集从string
转换为float
。这两个操作可以如下图所示执行:
# drop the date and time column
drop=['Date','Time']
dataset.drop(drop,inplace=True,axis=1)
# remove the 'T=' and 'H=' string
dataset['Temperature']=dataset['Temperature'].str.replace('T=','')
dataset['Humidity']=dataset['Humidity'].str.replace('H=','')
dataset=dataset.astype(float)
print(dataset.head())
Output:
Temperature Humidity
0 22.0 20.0
1 22.0 20.0
2 22.0 26.0
3 22.0 26.0
4 22.0 20.0
下一步是检查两列中缺失的数据。如前所述,丢失的数据通常是 NaN 的形式,Pandas 包中的函数isna()
可以用来检测这种数据的存在。NumPy 数据中的函数where()
可以与函数isna()
一起使用,以获得相应列中缺失值的位置,如下所示:
print(np.where(dataset['Temperature'].isna()))
print(np.where(dataset['Humidity'].isna()))
Outpu:
(array([206, 207, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224,
225, 226, 227], dtype=int64),)
(array([206, 207, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224,
225, 226, 227], dtype=int64),)
从前面的结果可以看出,温度列和湿度列中都有缺失数据,并且缺失数据的位置在这两列中是相同的。下一步将是处理丢失的值。处理缺失值的方法可能因数据的性质而异。在我们的数据集中,因为我们每五分钟测量一次温度和湿度值,所以可以有把握地假设在缺失值的范围内不会有太大的变化。因此,可以使用ffill
方法填充缺失的值,这代表“向前填充”,其中缺失的值被前一行中的值替换。这可以使用 Pandas 包中的fillna()
函数来实现。在实现这个填充过程之后,可以使用isna().any()
函数来验证这一点,如果任何一列中没有缺失值,该函数将返回 false,如下所示:
dataset['Temperature']=dataset['Temperature'].fillna(axis=0,method='ffill')
dataset['Humidity']=dataset['Humidity'].fillna(axis=0,method='ffill')
print(dataset.isna().any())
Output:
Temperature False
Humidity False
dtype: bool
既然已经处理了缺失值,下一步就是寻找数据中的异常值。为此,让我们使用我们之前讨论过的 Z 分数。在计算 Z 得分之前,应将数据集中的条目转换为整数。以下代码说明了如何使用 Z 值来检测和移除异常值:
from scipy import stats
z=np.abs(stats.zscore(dataset))
df1=dataset[z>3]
print(df1)
dataset=dataset[(z<3).all(axis=1)]
Output:
Temperature Humidity
47 9.0 140.0
157 37.0 12.0
从上图可以看出,有两个异常值对应于行索引 47 和 57。我们保留所有 Z 得分小于 3 的数据点,而不是删除对应于 Z 得分大于 3 的数据点的异常值。
实时传感器数据的探索性数据分析
我们讨论了数据科学家经常使用的一些基本图,并用一些现成的数据集演示了每个图。在本节中,我们将使用实时传感器数据展示一些曲线图。让我们以在第五章中使用的相同温度和湿度传感器数据来讨论准备数据的概念。由于我们已经完成了该章中的所有数据清理步骤,这里提供了相同的代码,用于在绘制之前准备数据:
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
from scipy import stats
dataset=pd.read_csv('datasets_384649_744229_log_temp.csv')
# drop the date and time column
drop=['Date','Time']
dataset.drop(drop,inplace=True,axis=1)
# remove the string header'T=' and 'H='
dataset['Temperature']=dataset['Temperature'].str.replace('T=','')
dataset['Humidity']=dataset['Humidity'].str.replace('H=','')
dataset=dataset.astype(float)
print('After removing inappropriate data\n',dataset.head())
# detect the location of missing data, if any
print('Missing values in temperature\n',np.where(dataset['Temperature'].isna()))
print('Missing values in humidity\n',np.where(dataset['Humidity'].isna()))
# filling the missing values using forward fill
dataset['Temperature']=dataset['Temperature'].fillna(axis=0,method='ffill')
dataset['Humidity']=dataset['Humidity'].fillna(axis=0,method='ffill')
# detect and remove outliers using z-score
z=np.abs(stats.zscore(dataset))
df1=dataset[z>3]
dataset=dataset[(z<3).all(axis=1)]
print(dataset.head())
Output:
After removing inappropriate data
Temperature Humidity
0 22.0 20.0
1 22.0 20.0
2 22.0 26.0
3 22.0 26.0
4 22.0 20.0
Missing values in temperature
(array([206, 207, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224,
225, 226, 227], dtype=int64),)
Missing values in humidity
(array([206, 207, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224,
225, 226, 227], dtype=int64),)
Temperature Humidity
0 22.0 20.0
1 22.0 20.0
2 22.0 26.0
3 22.0 26.0
4 22.0 20.0
可视化实时传感器数据
既然数据清理过程已经完成,下一步就是绘制数据。使用的绘图类型取决于数据的性质以及分析程序的要求。因为我们已经测量了 28 小时内的温度和湿度,所以理想的做法是绘制出它们相对于时间的曲线图。但是,为了更好地理解这两个参数的变化,每四个小时取一次平均值,并使用条形图绘制这些平均值。如果我们希望可视化整个持续时间内的温度和湿度分布,而不是它们的变化,那么可以将温度和湿度的范围划分为多个区间,每个区间中的值的计数可以用于制作饼图。这三种类型的图如下所示:
# Taking average over every 4 hours
a=dataset.shape[0]
b=[]
c=[]
for i in np.arange(0,a-(a%12),48):
b.append(np.mean(dataset.Temperature[i:i+47]))
c.append(np.mean(dataset.Humidity[i:i+47]))
# Temperature vs Time over 28 hours
plt.subplot(221)
plt.plot(np.linspace(0,28,a),dataset.Temperature)
plt.title('Temperature vs Time')
# Humidity vs Time over 28 hours
plt.subplot(222)
plt.plot(np.linspace(0,28,a),dataset.Humidity)
plt.title('Humidity vs Time')
#Bar plot of average temperature over every 4 hours during the 28 hours
plt.subplot(223)
x=['1','2','3','4','5','6','7']
plt.bar(x,b)
plt.title('Average temperature over every 4 hours')
#Bar plot of average humidity over every 4 hours during the 28 hours
plt.subplot(224)
plt.bar(x,c)
plt.title('Average humidity over every 4 hours')
#Pie chart for temperature distribution
d=pd.DataFrame(dataset.Temperature.value_counts(bins=4))
plt.subplot(235)
plt.pie(d.Temperature,labels=d.index)
plt.title('Temperature distribution')
#Pie chart for humidity distribution
e=pd.DataFrame(dataset.Humidity.value_counts(bins=4))
plt.subplot(236)
plt.pie(e.Humidity,labels=e.index)
plt.title('Humidity distribution')
plt.show()
在图 9-20 中,前两个图显示了温度和湿度的分布,其中每个数据样本沿时间轴绘制,以小时表示。我们可以看到温度和湿度和预期成反比。但是,通过每四个小时取一次样本的平均值,并将数据绘制成条形图,可以更好地表达随时间的分布,如第三和第四个图所示。第五个和第六个图显示的饼状图关注的是温度和湿度的分布,而不是它们随时间的变化。由于传感器数据仅记录 28 小时,因此数据中不会有大的变化,因此仅使用四个箱来绘制分布。从这两个数字,我们可以看到,在这 28 小时内,温度大多在 15 至 20 之间,湿度大多在 19 至 25 之间。
图 9-20
温度和湿度的变化和分布
使用视觉摄像头读取条形码生成报告
如今,许多行业都借助条形码和二维码来记录产品。关于产品的信息可以印在产品上,以便于识别和记录。市场上有专用的条形码/二维码扫描仪,但需要人工扫描产品上的条形码/二维码。这可能会降低装配线的生产率。如今,视觉系统被用来自动扫描产品上的条形码/二维码。这将通过消除人力和减少装配线上的时间来提高生产率。因此,摄像头可以与 Raspberry Pi 接口,以扫描装配线上产品的条形码/QR 码。
我们已经在本章的案例研究 2 中讨论了如何在 Raspberry Pi 上启用摄像头(请参考案例研究 2,了解将网络摄像头与 Raspberry Pi 连接的步骤)。以下代码[30]连续采集产品在装配线上的图像,识别图像中的条码/QR 码,解码条码/QR 码中的信息,并将解码后的信息显示在图像屏幕上。
# import the required packages
from imutils.video import VideoStream
from pyzbar import pyzbar
import argparse
import datetime
import imutils
import time
import cv2
# construct the argument parser and parse the arguments
ap = argparse.ArgumentParser()
ap.add_argument("-o", "--output", type=str, default="barcodes.csv",
help="path to output CSV file containing barcodes")
args = vars(ap.parse_args())
# initialize the video stream and allow the camera sensor to warm up
print("[INFO] starting video stream...")
vs = VideoStream(src=0).start()
#vs = VideoStream(usePiCamera=True).start()
time.sleep(2.0)
# open the output CSV file for writing and initialize the set of
# barcodes found thus far
csv = open(args["output"], "w")
found = set()
# loop over the frames from the video stream
while True:
# grab the frame from the threaded video stream and resize it to
# have a maximum width of 400 pixels
frame = vs.read()
frame = imutils.resize(frame, width=400)
# find the barcodes in the frame and decode each of the barcodes
barcodes = pyzbar.decode(frame)
# loop over the detected barcodes
for barcode in barcodes:
# extract the bounding box location of the barcode and draw
# the bounding box surrounding the barcode on the image
(x, y, w, h) = barcode.rect
cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 0, 255), 2)
# the barcode data is a bytes object so if we want to draw it
# on our output image we need to convert it to a string first
barcodeData = barcode.data.decode("utf-8")
barcodeType = barcode.type
# draw the barcode data and barcode type on the image
text = "{} ({})".format(barcodeData, barcodeType)
cv2.putText(frame, text, (x, y - 10),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 2)
# if the barcode text is currently not in our CSV file, write
# the timestamp + barcode to disk and update the set
if barcodeData not in found:
csv.write("{},{}\n".format(datetime.datetime.now(),
barcodeData))
csv.flush()
found.add(barcodeData)
# show the output frame
cv2.imshow("Barcode Scanner", frame)
key = cv2.waitKey(1) & 0xFF
# if the `q` key was pressed, break from the loop
if key == ord("q"):
break
# close the output CSV file do a bit of cleanup
print("[INFO] cleaning up...")
csv.close()
cv2.destroyAllWindows()
vs.stop()
前面的代码使用网络摄像头获取图像,并使用while
循环捕捉每一帧。此外,在无限while
循环的帮助下,帧被连续显示。'q'
键用于打破无限的while
循环。然后,在cap.release
的帮助下,可以释放图像采集。在该程序中,每个采集到的帧都被馈送到pyzbar
模块,以识别图像中的条形码/QR 码,并解码条形码/QR 码中的数据【30】。解码的信息显示在相应的帧中。图 9-21 显示了程序的输出。
图 9-21
条形码和 QR 码扫描仪的输出
将文件或数据从 Raspberry Pi 传输到计算机
在某些情况下,Raspberry Pi 中的数据需要与附近的计算机共享。此外,如果 Raspberry Pi 在其他地方,需要通过远程访问来访问它。有许多方法可以将数据从 Raspberry Pi 传输到其他计算机。最简单、最有效的方法之一是使用 VNC 查看器来共享数据和进行远程访问。VNC 是一个图形桌面共享应用程序,允许你通过远程访问从一个系统控制另一个系统。本节讨论了使用 VNC 从远程桌面计算机共享文件和控制 Raspberry Pi 的 VNC 查看器的安装过程和用法。
为了在 Pi 中安装 VNC,在 Raspberry Pi 的命令窗口中使用了以下代码,如图 9-22 所示:
图 9-22
在树莓码头安装 VNC 浏览器
sudo apt update
sudo apt install realvnc-vnc-server realvnc-vnc-viewer
与此同时,VNC 浏览器需要安装在远程桌面计算机上。如果远程桌面计算机有不同的操作系统,VNC 与所有操作系统兼容。在 Pi 上安装了 VNC 之后,我们必须在 Raspberry Pi 中启用 VNC 服务器。通过以下步骤,可以在 Raspberry Pi 中图形化地启用 VNC 服务器:
图 9-23
以图形方式在 Pi 上启用 VNC 服务器
-
转到 Raspberry Pi 图形桌面,选择菜单➤首选项➤ Raspberry Pi 配置。将打开 Raspberry Pi 配置窗口,如图 9-23 所示。
-
在 Raspberry Pi 配置窗口中,选择接口选项,并确保启用了 VNC。如果未启用 VNC,请选择窗口中的“启用”按钮。
-
之后,启用 VNC 服务器,点击树莓 Pi 图形桌面右上角的 VNC logo 。将打开 VNC 查看器应用程序窗口。其中显示树莓派的 IP 地址,如图 9-8 所示。只有当 Raspberry Pi 连接到网络时,IP 地址才会出现。在这里,Raspberry Pi 使用 WiFi 加密狗/手机热点通过 WiFi 网络连接。
这些步骤用于在远程桌面和 Raspberry Pi 之间创建私有连接。为了创建私有连接,远程桌面和 Raspberry Pi 都连接在同一个网络中。这将只在公司园区内创建连接。如果用户想要将数据上传到云中,那么用户需要登录 VNC 浏览器,将 Pi 与远程桌面连接起来,远程桌面可以在世界上的任何地方。
通过在另一个远程桌面打开 VNC 浏览器,如图 9-24 所示,在提供的空白处输入树莓派的 IP 地址,VNC 服务器建立电脑与树莓派的连接。登录窗口将打开,如图 9-25 所示,要求输入用户名和密码。
图 9-25
使用 VNC 浏览器建立从桌面到 Pi 的连接
图 9-24
《覆盆子里的 VNC 观众》
通常,Raspberry Pi 的用户名和密码是 pi 。输入 pi 作为用户名和密码,树莓 pi 桌面就会出现在远程桌面电脑上,如图 9-26 所示。
图 9-26
远程计算机上的 Raspberry Pi 图形桌面
现在,Raspberry Pi 桌面可以远程访问其他计算机。此外,可以使用 VNC 浏览器中的文件共享选项来共享 Raspberry Pi 中的文件和数据,如图 9-27 所示。
图 9-27
从远程桌面上的 Raspberry Pi 传输文件
标签:树莓,Pi,教程,plt,科学,print,import,Raspberry,数据 From: https://www.cnblogs.com/apachecn/p/18448116