本案例是Pandas数据分析课程【第一章】NumPy基础配套案例。案例使用葡萄酒品质数据,我们将结合数据使用NumPy进行简单分析。
本次案例使用葡萄酒品质数据,该数据集包含1599种红葡萄酒的各种信息,如酒的固定酸度、挥发性酸度和pH值等测量值,也包括一个酒的品质得分,该得分是至少三类口味测试者给该款酒打分的平均值。该数据来源于公开数据库UCI,更多详细信息可以查看https://archive.ics.uci.edu/ml/datasets/Wine+Quality 。
数据中变量有:
变量名称 | 含义说明 |
---|---|
fixed acidity | 固定酸度 |
volatile acidity | 挥发性酸度 |
pH | 酸碱值 |
alcohol | 酒精度数 |
quality | 品质得分 |
import numpy as np
我们使用NumPy中的genfromtxt
导入数据,由于数据为csv格式即逗号分隔符文件,故设定参数delimiter
为","。
wines1=np.genfromtxt("./input/winequality-red.csv",delimiter=",")
查看数据
wines1
数据中第一行为缺失值,这是因为数据第一行为各变量名,是字符串格式,而genfromtxt
默认导入的数据格式为浮点型,故会被读成缺失值。为了防止缺失值的产生,我们可以调整参数skip_header
为1,在读取时跳过第一行。
wines1=np.genfromtxt("./input/winequality-red.csv",delimiter=",",skip_header=1)
wines1
但这样我们就失去了各变量名的数据,我们也可以通过设定names
为True
来处理这个问题。
wines2=np.genfromtxt("./input/winequality-red.csv",delimiter=",",names=True)
wines2
这样,就把首行读取为了每一列的列名,同时默认了各列的数据类型都是浮点型'f8'。但这两种方法读入的数据有所不同。我们可以通过shape
查看两份数据的维度。
wines1.shape
wines2.shape
可以看到,通过第一种方法读入的数据是一个二维数组,而通过第二种方法读入的数据是一个一维数组。我们可以通过dtype
查看各这两个数组的数据类型。
wines1.dtype
wines2.dtype
第一种方法获得的数组数据类型都是浮点型,第二种方法获得的数组则可以针对不同的列指定不同的数据类型。
使用索引选取数据
和Python的列表一样,NumPy也是零索引的,即第一行的索引为0,第一列的索引为0。例如,我们想获取第三种红葡萄酒的品质得分,即第三行第五列的数据,对应的行索引为2,列索引为4。
wines1[2,4]
使用切片选取数据
如果我们想要从第四列中选择前三项,我们可以使用冒号(:)来完成。冒号表示我们要从起始索引中选择所有元素,但不包括结束索引。
wines1[0:3,3]
就像列表切片一样,可以省略0。
wines1[:3,3]
当不指定开始或结束索引,只用冒号制定时,将选择所有数据。例如,我们选取第四列的所有数据。
wines1[:,3]
同样的,我们也可以提取整行,例如我们提取第一种红葡萄酒的所有数据。
wines1[0,:]
对数组进行转置
将二维数组进行翻转可以使用transpose
和T
。区别在于transpose
是NumPy中的一个函数,而T
是一个方法。
np.transpose(wines1)
wines1.T
将多维数组降为一维
我们可以使用ravel
和flatten
将多维数据降为一维。
wines1.ravel()
wines1.flatten()
默认的降维方式会将每一行接在前一行后,也可以修改参数order
为'F'按列进行降维,将每一列接在前一列后。两个函数都有这一参数,我们以ravel
为例。
wines1.ravel(order='F')
两个函数的差别在于,ravel
返回的结果是一个视图,对此进行修改会改变原始数据集,而flatten
返回的是一个拷贝,对其进行修改不会影响原始数据集。
将数组修改为特定行列数
我们可以使用reshape
和resize
将数据重新排列为指定的行列形状。例如,我们将降成一维的数据再变回原来的样子。
test=wines1.flatten()
test
test.reshape(1599,5)
这里同样可以通过调整order
参数修改数据排列方法,默认的排序方法为'C',将按行排,排满一行后排下一行。可以调整为'F',这将按列排,排满一列后排下一列。
test.reshape(1599,5,order='F')
与reshape
相比,resize
在参数上一致,但没有返回值,即会直接对原始数据进行修改。
test.resize(1599,5)
此时原始数据直接被修改了。
test
使用hstack
拼接新列
假设我们计划引入一种新的葡萄酒品质打分方法,同时保留原来的品质得分,则我们要添加一列新值在原始数组后。
假设初始的新得分都为零值,我们使用zeros
生成一列长度为1599,值全为0的数组。
np.zeros((1599,1))
再使用hstack
将这一列拼接在原来的数组后。
np.hstack((wines1,np.zeros((1599,1))))
使用vstack
拼接新行
另一种情况,假设有一种新的红葡萄酒的各指标数据,我们要将其拼接在原始数组后成为新的一行。
同样假设新的数据为零值,我们使用zeros
生成一行长度为5,值全为零的数组。
np.zeros((1,5))
再使用vstack
将这一行拼接在原来的数据后
np.vstack((wines1,np.zeros((1,5))))
使用concatenate
进行拼接
使用concatenate
可以实现hstack
和vstack
的功能,只需要通过调整参数axis
即可。设定axis
为1,将拼接新列,等同于hstack
。
np.concatenate((wines1,np.zeros((1599,1))),axis=1)
设定axis
为0,按拼接新行,等同于vstack
。
np.concatenate((wines1,np.zeros((1,5))),axis=0)
无论是使用vstack
,hstack
还是concatenate
进行拼接都要注意,传入的数组必须在指定轴上有相同的维度。例如,要拼接新行到葡萄酒数据中,新的数据必须也为5列,行数上则没有要求。同理,若要拼接新列到葡萄酒数据中,则新数据的行数必须为1599,列数上则没有要求。
单列运算
当我们对某个数组和值进行基本数学运算时(如$+$、$-$、$*$、$/$),NumPy会将该运算应用于数组中的每个元素。例如,我们认为葡萄酒数据中的品质得分普遍偏低,我们打算将所有葡萄酒的品质得分加10。
wines1[:,4] + 10
这不会对原始数据进行修改,如果要直接修改原始数据,可以使用$+=$运算。
wines1[:,4] += 10
wines1[:,4]
与之对应,我们也可以使用$*=、/=、-=$。例如我们想将红葡萄酒的品质得分再翻倍。
wines1[:,4] *= 2
wines1[:,4]
多列运算
也可以在数组之间进行数学运算,这会将操作应用于对应元素。例如,还是想将红葡萄酒的品质得分翻倍,也可以使用品质得分列加品质得分列实现。
wines1[:,4] + wines1[:,4]
假设我们想要选择最大化酒精含量和品质的葡萄酒(我们想喝醉,但我们想喝好酒)。我们可以计算各葡萄酒酒精度数乘以品质得分的值。
wines1[:,3] * wines1[:,4]
NumPy数组方法
除算术运算之外,NumPy提供了许多方法来解决数组中更复杂的计算。例如sum
、std
、mean
、min
和max
等。例如,我们想计算所有品质得分的平均数。
wines1[:,4].mean()
计算品质得分数据的标准差。
wines1[:,4].std()
我们也可以计算整个数组的标准差。
wines1.std()
但这样的计算并没有实际意义,我们可能更想了解每一个变量对应数据的标准差,可以通过设定参数axis
为0来实现。
wines1.std(axis=0)
数组比较
NumPy可以使用比较操作(如$<、>、> =、<=和==$)测试是否匹配某些值。例如,我们想要查看哪些葡萄酒的酒精度数高于10。
wines1[:,3]>10
我们得到一个布尔数组,告诉我们哪种葡萄酒的酒精度数大于10。我们可以使用sum
统计有多少葡萄酒的酒精度数高于10。
a = wines1[:,3]>10
a.sum()
使用布尔索引筛选子集
我们可以使用这个布尔数组作为索引,查看酒精度数高于10的葡萄酒数据。
wines1[wines1[:,3]>10]