树莓派数据科学教程(二)

原文:Data Science with Raspberry Pi

协议:CC BY-NC-SA 4.0

六、可视化数据

在前一章中,我们讨论了准备数据进行分析的一些步骤。在分析数据之前,必须了解我们正在处理的数据的性质。可视化数据可能会给我们一些关于数据本质的有用见解。这些洞察,例如数据中的模式、数据的分布、数据中存在的异常值等。,可以方便地确定用于分析数据的方法。此外,可视化可以在分析结束时用于向相关方传达调查结果,因为通过可视化技术传达分析结果可能比编写解释调查结果的文本内容更有效。在本章中,我们将了解 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 表示业主自住房屋的中值,以千为单位。

  1. 导入所需的库。

第一步是加载进行 EDA 所需的库。在本章中,我们将使用 Pandas、NumPy 和 Matplotlib 等包进行绘图:

  1. 导入数据集。
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)

如果数据集非常大,我们可以使用以下代码显示带有标题的顶部和底部五行:

  1. 检查数据集中的数据信息。
# 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

在前面的代码中,删除了CRIMZNLSTAT列,只显示 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函数计算xpdf函数。使用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 算法的工作原理如下:

  1. 指定分类的数量。

  2. 为每个簇随机选择中心点,也称为质心

  3. 计算每个数据点与聚类质心之间的距离,并将这些点分配给距离最小的聚类。

  4. 通过取分配给聚类的所有数据点的平均值,重新计算每个聚类的质心。

  5. 重复步骤 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中,进行整形和缩放。然后将两个变量xy组合在变量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 maxs min 分别是信号最大值和最小值。

峰峰值时间窗(PPT)定义为最大信号时间和最小信号时间之差,如公式 9-3 所示。

tPP=tsmaxtsmin(9-3)

这里, t s maxt s min 是最大和最小信号值出现的时间。

峰峰值斜率(PPS)定义为峰峰值信号值(PP)与峰峰值时间值(PPT)之比,如公式 9-4 所示。

)(9-4)

这里, s pp 为峰间信号值, t pp 为峰间时间窗。

信号功率§定义为恒定振幅下无限时间存在的信号。信号功率如公式 9-5 所示。

)(9-5)

信号的平均值(μ)定义为所选区域端点之间数据样本的平均值,并显示平均值。方程 9-6 给出了信号的平均值。

)(9-6)

其中 N 是信号中的样本总数。

峰度(K)是频率分布曲线峰值的锐度,在方程 9-7 中给出。

)(9-7)

这里,m?? 4 和m2 是信号的四阶矩和方差。

迁移率(M)定义为信号的一阶方差与信号方差之比,在等式 9-8 中给出。

)(9-8)

复杂度©被定义为流度的一阶导数除以流度,在等式 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。下面的代码演示了数据和标签的这种拆分:

  1. 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

软件配置工具窗口

  1. 在 Raspberry Pi 操作系统的终端窗口中键入命令sudo raspi-config。这将打开软件配置工具窗口,如图 9-7 所示。

  2. 进入接口选项,如图 9-8 所示,同时启用 SSH 和 Camera。

  3. 重启 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 服务器

  1. 转到 Raspberry Pi 图形桌面,选择菜单➤首选项➤ Raspberry Pi 配置。将打开 Raspberry Pi 配置窗口,如图 9-23 所示。

  2. 在 Raspberry Pi 配置窗口中,选择接口选项,并确保启用了 VNC。如果未启用 VNC,请选择窗口中的“启用”按钮。

  3. 之后,启用 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 传输文件

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值