初识数据处理

本文介绍了数据分析的基础知识,包括需求分析、数据采集、数据清洗、数据分析和数据可视化。重点讲解了numpy库,阐述了如何创建和操作多维数组,如加减乘除、广播机制、轴操作、索引和分片。此外,还探讨了统计学中的集中趋势和离中趋势指标,以及如何在numpy中计算。最后提到了数据读取方法genfromtxt()。通过学习,读者可以掌握numpy进行数据处理的基本技能。
摘要由CSDN通过智能技术生成

数据处理总体介绍


要掌握数据分析需要三方面的知识:分析工具、统计学知识、行业基本知识。

第一步,分析需求,明确目标。我们要确定进行数据分析任务要解决什么问题,这个目标非常重要,因为它是我们整个过程的导向。然后进行问题拆解,包括我们从哪些角度来分析这个问题,采用哪些方法和数据指标;

第二步,采集第一步确定的数据,常用的数据获取方法包括从公开数据库、自有数据库中获取,以及使用爬虫来爬取各种网站上的数据;

第三步,整理数据,也就是数据清洗。把上一步获取的杂乱无章的数据按照我们需要的维度进行整理。数据是数据分析过程里的证据,所以数据的获取和清洗非常重要;

第四步,分析数据,采用数学方法、统计方法等对数据进行分析操作;

第五步,数据可视化,将分析结果以直观的形式进行展示。

在这里插入图片描述
NumPy 是一个用于数值运算的 Python 库,专门对数值运算进行优化,最大的优势是运行高效。
Matplotlib 是常用的数据可视化的工具包,用来绘制各种图表,更好地展示数据。

NumPy

NumPy,全称是 Numerical Python,它是目前 Python 数值计算中最重要的基础模块。NumPy 是针对多维数组的一个科学计算模块,这个模块封装了很多数组类型的常用操作。
要创建一个多维数组很简单,将一个列表作为参数传入 numpy 中的 array() 方法即可:

import numpy as np

data = np.array([1, 2, 3])
print(data)
# 输出:[1 2 3]
print(type(data))
# 输出:<class 'numpy.ndarray'>

除了使用 np.array() 方法来创建一个多维数组,numpy 还提供了两个实用的方法——np.ones() 和 np.zeros()。

从这两个方法的名称就能猜出它们的作用是什么——分别生成元素全为 1 和 0 的多维数组。

ones = np.ones(3)
print(ones)
# 输出:[1. 1. 1.]

zeros = np.zeros(3)
print(zeros)
# 输出:[0. 0. 0.]

生成出来的不是 1 和 0,而是 1. 和 0.。这是因为默认生成的是浮点数,numpy 会省略小数点后的 0,因此 1.0 和 0.0 变成了 1. 和 0.。

如果我们想要生成整数的话,可以传入 dtype 参数来指定类型。

ones = np.ones(3, dtype='int')
print(ones)
# 输出:[1 1 1]
zeros = np.zeros(3, dtype='int')
print(zeros)
# 输出:[0 0 0]

多维数组的加减乘除

列表间只有加法操作,作用是将两个列表的元素合并在一起。而多维数组间可以进行加减乘除的四则运算,运算规则也很简单:将两个数组中对应位置的元素一一进行运算。

data = np.array([1, 2])
ones = np.ones(2)
print(data + ones)
# 输出:[2. 3.]

在这里插入图片描述多维数组和数字的四则运算会作用在数组中的每个元素上,这在 numpy 中被称为 Broadcasting。Broadcasting 的本义为“大范围传播、投射”。用在媒体中,就是我们熟知的“广播”(广泛将信息传播给大众),用在数组运算中,就是“大范围地将运算应用到全部元素”的意思。因为 Broadcasting 规则,[1 2] + 1 相当于 [1 2] + [1 1],因此结果为 [2 3]。
在这里插入图片描述上述效果用列表来实现也是可以的,一般我们会这样写:

data = []
for i in [1, 2]:
  data.append(i + 1)
print(data)
# 输出:[2, 3]

用列表就必须用到循环,而 numpy 中这种不用编写循环就可以对数据进行批量运算的方式叫做 矢量化。numpy 中的矢量化操作把内部循环委托给高度优化的 C 和 Fortran 函数,从而实现更清晰,更快速的 Python 代码。
numpy 中多维数组的索引也是从 0 开始,以多维数组的长度减 1 结束。写法也和列表索引一样,列表中很实用的反向索引在多维数组中也同样适用,我们可以直接使用 data[-1] 获取数组中的最后一个元素。
多维数组的分片和列表的分片也是基本类似的,形如 data[m:n]。分片是左闭右开区间,即包含 m 不包含 n,也就是获取索引为 m 到 n-1 之间的元素(包含 m 和 n-1)。

data = np.array([1, 2, 3])
print(data[0:2])  # 获取索引为 01 的元素
# 输出:[1 2]

分片有几个小技巧。冒号前后的值是可以省略的。省略后冒号前默认为 0,冒号后默认为列表的长度。这同样适用于多维数组,所以通过冒号前后值的省略,有如下分片小技巧:

data = np.array([1, 2, 3])
# 获取前 2 个元素
print(data[:2])
# 输出:[1 2]

# 获取后 2 个元素
print(data[-2:])
# 输出:[2 3]

# 获取所有元素
print(data[:])
# 输出:[1 2 3]

需要注意的是,列表分片是将分片后的数据复制了一份,而多维数组的分片则是返回原数据其中的一块,并没有复制数据。

因此,对列表分片后的数据进行更改不会影响原数据,但对多维数组分片后的数据进行更改会影响到原数据。

# 列表
lst_data = [1, 2, 3]
lst_data2 = lst_data[:]
lst_data2[0] = 6
print(lst_data)
# 输出:[1, 2, 3]

# 多维数组
arr_data = np.array([1, 2, 3])
arr_data2 = arr_data[:]
arr_data2[0] = 6
print(arr_data)
# 输出:[6 2 3]

numpy 为什么会这样?是为了性能。

numpy 设计的目的是处理大数据,所以你可以想象一下,在处理几百万甚至几千万条数据时,每进行一次分片操作就将数据复制一遍将会产生何等的性能和内存问题。所以,numpy 中的切片默认不会复制一份副本,而是返回原数据中的一块,被称为视图(View)。如果你确实想要得到一份副本,则需要手动调用 copy() 方法进行复制,例如: arr_data[:].copy()

还有一个小技巧是分片支持传入第三个参数——步长。即分片时每隔几个数据取一次值,步长的默认值为 1。

data = np.array([1, 2, 3, 4, 5, 6])
print(data[::2])  # 省略前两个参数
# 输出:[1 3 5]

上述例子中省略了前两个参数,默认为 0 和 6,步长为 2。所以 data[::2] 等价于 data[0:6:2],作用是索引在 0 到 5 之间的元素,且索引每次加 2。最终将得到索引为 0、2、4 的元素,结果为 [1 3 5]。

当步长为负数时,会将顺序反转。我们可以利用这个特性来实现列表或多维数组的快速反转。

data = np.array([1, 2, 3, 4, 5, 6])
print(data[::-1])  # 省略前两个参数
# 输出:[6 5 4 3 2 1]

统计学知识

集中趋势

集中趋势所反映的是一组数据所具有的共同趋势,它代表了一组数据的总体水平。其常用指标有 平均数、中位数 和 众数。

求平均数很简单,就是把所有数据加起来,再除以这些数据的个数。但平均数对异常数值并不敏感,容易得到误导性的结论。比如,你和马云的身价求平均,你也是亿万富翁。

所以不能只看平均数,还有中位数和众数。中位数是指数据排序后处于中间的那个数。众数是指一组数据中出现次数最多的数。

离中趋势

离中趋势是指一组数据中各数据值以不同程度的距离偏离其中心(平均数)的趋势。其常用指标有 极差、方差 和标准差。

极差是一组数据的最大值减去最小值得到的,反应了数据变动的最大范围。

方差的计算方式是:将一组数据中的每个数减去这组数据的平均数,然后将得到的结果进行平方求和,最后再除以数据的个数。

而方差的平方根则是标准差。因为方差是对数据进行平方得到的,所以量纲(单位)和原数据不一致。对方差进行开根号后得到的标准差量纲和原数据一致,使用起来更方便。

方差和标准差都能反映数据的离散程度,也就是数据的波动程度。方差和标准差的值越小,说明数据越稳定。

除了众数则需要我们自己动手写代码计算,numpy 中没有直接计算的方法之外,其他的指标在 numpy 中都有对应的方法进行计算,对应的方法如下图所示:在这里插入图片描述
这些方法有两种使用方式,一种是直接在多维数组上调用,如 data.std();另一种是在 numpy 上调用并传入数据,如:np.std(data)。

需要注意的是,求中位数的 median() 方法只有 numpy 上有,只能使用 np.median(data) 来求中位数。

多维数组

一维数组只有行,二维数组相比一维数组多了列这个维度,而三维数组则类似多个二维数组堆叠在一起,形如一个立方体
在这里插入图片描述
理论上可以往更多的维度延伸,但最常用的还是二维数组。

还是和列表进行类别,二维数组相当于单层的嵌套列表。并且我们可以将单层嵌套列表传入 np.array() 方法创建一个二维数组

# 单层嵌套列表
nested_list = [[1, 2], [3, 4]]
print(nested_list)
# 输出:[[1, 2], [3, 4]]

# 二维数组
data = np.array(nested_list)
print(data)
# 输出:
# [[1 2]
#  [3 4]]

嵌套列表就是列表中的元素也是列表的列表。可以看到,通过嵌套列表创建的二维数组也是用空格分隔的,并且分成了两行。列表中的第一个元素 [1, 2] 在第一行,第二个元素 [3, 4] 在第二行。
同样,ones() 和 zeros() 方法同样也能快速创建元素全为 1 和 0 的二维数组。与之前的区别在于,创建二维数组要传入一个包含行和列信息的元组。比如:np.ones((m, n)) 表示创建一个 m 行 n 列且元素全为 1 的二维数组。

ones = np.ones((3, 2))
print(ones)
# 输出:
# [[1. 1.]
#  [1. 1.]
#  [1. 1.]]

zeros = np.zeros((3, 2))
print(zeros)
# 输出:
# [[0. 0.]
#  [0. 0.]
#  [0. 0.]]

在这里插入图片描述
更多维的数组的创建,只要传入嵌套层数更多的列表即可。创建三维数组的方式如下:
在这里插入图片描述
ones() 方法和 zeros() 方法也是如此。创建三维数组只需传入一个长度为 3 的元组,分别指定了每个维度上的元素个数。
在这里插入图片描述
接下来我们来认识几个描述多维数组的属性:

ndim:多维数组维度的个数。例如:二维数组的 ndim 为 2;
shape:多维数组的形状。它是一个元组,每个元素分别表示每个维度中数组的长度,
       对于 m 行和n列的的数组,它的 shape 将是 (m, n)。因此,shape 元组的长度(元素个数)就是 ndim 的值;
size:多维数组中所有元素的个数。shape 元组中每个元素的乘积就是 size 的值;
dtype:多维数组中元素的类型。

注意:
len(shape) = ndim
m!=ndim
(m, n) 的长度是 2 ,(m,n,p)就是3
假设一个3维数组的shape是(3,4,5),可以理解成一个长宽高是3,4,5的长方体.
对于 [1 2 3] 这样的一维数组,它的 shape 是 (3,),表示有三个元素。在元组中只有一个元素时,元素后面的逗号是不能省略的,否则会被认为是加了括号的 3,而不是元组。

data = np.array([[1, 2, 3], [4, 5, 6]])

print('ndim:', data.ndim)
print('shape:', data.shape)
print('size:', data.size)
print('dtype:', data.dtype)
# 输出:
# ndim: 2
# shape: (2, 3)
# size: 6
# dtype: int64

下面看一个例子理解一下

import numpy as np

data = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9], [1, 2, 3], [4, 5, 6],
                 [7, 8, 9], [1, 2, 3], [4, 5, 6], [7, 8, 9], [1, 2, 3],
                 [4, 5, 6], [7, 8, 9], [1, 2, 3], [4, 5, 6], [7, 8, 9]])

print('data 的行数为:', data.shape[0])
print('data 的元素个数为:', data.size)
print(data.ndim)
print(data.shape)
#输出
#data 的行数为: 15
#data 的元素个数为: 45
#2
#(15, 3)

相当于是个2维数组,但是每一行是个3列的列表,一共15行,长15宽3,几个 [ 就是几维

广播

二维数组间的加减乘除和一维数组间的并无大致,也是对应位置的元素进行计算。这里不再赘述,我们直接来看图解:
在这里插入图片描述
减法、乘法和除法也都是如此。二维数组和数学中的 矩阵 很相似,常被用于进行矩阵间的运算。但二维数组间直接用 * 进行计算的方式和矩阵乘法计算的方式并不相同,应该用 @ 符号进行矩阵间的乘法计算。

维度一样的数组间可以进行计算的条件是形状(shape)一样,形状不一样的数组元素无法一一对应,因此无法计算,导致报错。

除了维度相同的数组间的计算,如果将二维数组和一维数组放到一起进行计算,这样可以吗?当然也是可以的,这得益于numpy 中的 广播规则 。它是指较小维度的数组在较大维度的数组上进行”广播“,以便它们具有兼容的形状。

当运算中的 2 个数组的形状不同时,numpy 将自动触发广播机制,它具体的规则是:在较小维度数组的 shape 元组前补 1,直到两个数组的 shape 元组长度相同。接着将元素在值为 1 的维度上进行复制,直到两个数组的形状相同。

如果无法使两个数组的形状相同,则会抛出
ValueError: operands could not be broadcast together的异常。

这个规则可能难以理解,我们直接来看个例子:

data = np.array([[1, 2], [3, 4], [5, 6]])
ones = np.ones(2)
print(data.shape)
print(ones.shape)
# 输出:
# (3, 2)
# (2,)

data 的形状是 (3, 2),ones 的形状是 (2,)。根据规则在较小维度的 shape 元组前补 1,直到和较大维度的 shape 元组长度相同,补 1 后 ones 的形状是 (1, 2)。

接着将元素在值为 1 的维度上进行复制,最后 ones 的形状变成了 (3, 2),和 data 的形状相同,因此可以进行计算。

图中浅色的 1 就是被复制出来的元素。
在这里插入图片描述
那什么时候无法进行广播呢?我们再看一个例子:

data = np.array([[1, 2], [3, 4], [5, 6]])
ones = np.ones(3)
print(data.shape)
print(ones.shape)
# 输出:
# (3, 2)
# (3,)

当 ones 的形状是 (3,) 时,补 1 后变成 (1, 3),最后变成 (3, 3)。形状与 data 不相同,无法进行计算。
在这里插入图片描述

axis轴

因为二维数组多了个维度,所以它的通用方法可以更加的灵活。不仅可以对所有数据进行计算,还可以针对某个维度上的数据进行计算。

这里就要引入一个概念——轴(axis)。轴和维度的概念是类似的,一维数组有 1 个轴,二维数组有 2 个轴,三维数组有 3 个轴等等。

在 numpy 中,我们可以用 axis 参数来指定轴,从 0 开始依次增加递增的数分别对应着不同的轴。

在一维数组中,axis=0 就代表着它唯一的轴;二维数组中 axis=0 和 axis=1 代表其中的行轴和列轴;在三维数组中,axis=0、axis=1 和 axis=2 分别代表对应的三条轴。下图清晰的展示了 axis 和对应轴的关系:
在这里插入图片描述在一维数组中因为只有一个轴,一般用不到 axis。需要注意的是,在二维数组中 axis=0 的轴是向下的,和一维数组中有所不同,千万不要混淆。

在通用方法中,通过 axis 参数可以指定计算方向。以二维数组中的 max() 方法为例,指定 axis=0 将会在行轴方向求最大值,指定 axis=1 将会在列轴方向求最大值。

data = np.array([[1, 2], [5, 3], [4, 6]])

# 不指定 axis
print(data.max())
# 输出:6

# axis=0
print(data.max(axis=0))
# 输出:[5 6]

# axis=1
print(data.max(axis=1))
# 输出:[2 5 6]

在这里插入图片描述

二维数组的索引和分片

二维数组的索引和分片同样和一维数组类似,只是在行索引的基础上再加上列索引。形如 data[m, n],其中 data 是二维数组,m 是行索引或分片,n 是列索引或分片。

那么,data[0, 1] 就表示获取 data 中第一行第二列的元素。如果省略第二个参数 n 的话表示获取所有列,data[0] 就表示获取整个第一行,相当于 data[0, :]。

如果想要获取第一列则可以写成 data[:,0];如果想获取 2、3 两行可以写成 data[1:3],相当于 data[1:3, :]。你可以将索引和分片结合起来,以获取二维数组中你想要的任意数据。

data = np.array([[1, 2], [3, 4], [5, 6]])

print(data[0, 1])
# 输出:2

print(data[:, 0])
# 输出:[1 3 5]

print(data[1:3])
# 输出:
# [[3 4]
#  [5 6]]

可以看到,在二维数组中,当行和列都是索引时,结果是具体的元素;当行和列中一个是索引,一个是分片时,结果是一维列表;当行和列都是分片时,结果为二维数组。

例如,data[0:2, 0] 和 data[0:2, 0:1] 获取的都是 1 和 3 这两个元素,但其结果一个是 [1 3],一个是 [[1] [3]],实际上并不相同。
numpy 中的高级索引分为 布尔索引 和 花式索引。
在这里插入图片描述

布尔索引

顾名思义就是用布尔值作为索引去获取需要的元素。我们先来看个例子:

data = np.array([[1, 2], [3, 4], [5, 6]])
print(data > 3)
#[[False False]
 # [False  True]
 #[ True  True]]

print(data[data > 3])
# 输出:[4 5 6]
print(data[(data > 3) & (data < 5)])
# 输出:[4]

data = np.array([[1, 2], [3, 4], [5, 6]])
# 大于 3 或者小于 2
print(data[(data > 3) | (data < 2)])
# 输出:[1 4 5 6]

# 大于 3 或者不小于 2(即大于等于 2print(data[(data > 3) | ~(data < 2)])
# 输出:[2 3 4 5 6]

# 等于 3
print(data[data == 3])
# 输出:[3]

# 不等于 3
print(data[data != 3])
# 输出:[1 2 4 5 6]

区别在于:and 改用 &,or 改用 |,not 改用 ~,并且每个条件要用括号括起来。

arange(),random(),genfromtxt()

numpy 中的 arange() 方法和 Python 中的 range() 用法类似,不同之处在于 arange() 方法生成的是数组,而 range() 方法生成的是 range 类型的序列。

# 生成 1-9 的数组
print(np.arange(1, 10))
# 输出:[1 2 3 4 5 6 7 8 9]

# 生成 0-9 的数组
print(np.arange(10))
# 输出:[0 1 2 3 4 5 6 7 8 9]

# 生成 1-9 的数组,步长为 2
print(np.arange(1, 10, 2))
# 输出:[1 3 5 7 9]

Python 中有 random 模块来生成随机数,numpy 针对多维数组也集成了 random 模块,并且更加方便好用。这里只介绍其中的 rand() 方法和 randint() 方法,更多方法大家可以在需要时查询使用。

numpy 中的 np.random.rand() 方法和 Python 中 random.random() 方法类似,都是生成 [0, 1) 之间的随机小数。不同的是,numpy 中的 np.random.rand() 方法可以生成多个 [0, 1) 之间的随机小数,只需我们传入要生成的随机数组的形状(shape)即可。
同理,numpy 中的 np.random.randint() 方法和 Python 中的 random.randint() 类似,不同之处在于,random.randint(m, n) 生成的是 [m, n] 之间的整数,而 np.random.randint(m, n) 生成的是 [m, n) 之间的整数,这点一定注意要区分。

除此之外,np.random.randint() 方法第三个参数支持传入生成的数组的形状,例子如下:

# 不传参数时
print(np.random.rand())
# 输出:0.1392571183916036

# 传入一个参数时
print(np.random.rand(3))
# 输出:[0.7987698  0.52115291 0.70452156]

# 传入多个参数时
print(np.random.rand(2, 3))
# 输出:
# [[0.08539006 0.97878203 0.23976172]
#  [0.34301963 0.48388704 0.63304024]]

# 不传入形状时
print(np.random.randint(0, 5))
# 输出:3

# 形状为一维数组时
print(np.random.randint(0, 5, 3))
# 输出:[4 0 1]

# 形状为二维数组时
print(np.random.randint(0, 5, (2, 3)))
# 输出:
# [[0 2 1]
#  [4 2 0]]

genfromtxt() 方法用于文件的读取。我们学习 numpy 是要到实际生活中应用的,而生活中我们的数据来源通常是一个文件,例如 CSV 文件。

genfromtxt() 方法常用的参数有两个,分别是数据源和分隔符。假设我们要用 numpy 读取一个以逗号分隔的 CSV 文件,可以这样写:

data = np.genfromtxt(‘data.csv’, delimiter=’,’)
第一个参数是数据源,可以是本地文件的路径,也可以是网络文件的地址。delimiter 参数用于指定分隔符,CSV 文件一般是用逗号作为分隔符,当遇到其他符号分隔的文件时,用 delimiter 参数进行指定即可。

genfromtxt() 方法的返回值是一个多维数组,之后就可以预处理了。

小结

numpy 的基本用法我们已经学完,一般的数据处理我们都可以完成了。numpy 在科学计算、神经网络、机器学习等领域深受欢迎。这需要我们拥有较好数学功底,配合 numpy 中数学计算的方法,可以快捷方便地完成这些领域的计算工作。

在机器学习中,均方误差常被作为模型的损失函数,用来预测和回归,它的公式如下
在这里插入图片描述
n 是数据集的个数,Y_prediction 是模型预测的结果集,Y 是实际的数据集。将预测的结果和实际的值作差后进行平方求和,最后除以数据集的个数,得到的就是均方误差。均方误差越小,说明模型预测的越准确,反之则越不准确。
给定 Y_prediction 和 Y,根据均方误差公式,想想用 Python 来进行计算是不是很麻烦?

如果用 numpy 就简单多了,numpy 的代码几乎和数学公式一致。
在这里插入图片描述
np.sum() 是求和方法,np.square() 是计算平方的方法。predictions 是预测结果的多维数组,labels 是实际值的多维数组。

predictions = np.array([1, 1, 1])
labels = np.array([1, 2, 3])
error = (1 / labels.size) * np.sum(np.square(predictions - labels))
print(error)
# 输出:1.6666666666666665

先到这里吧,后续还会持续更新。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值