跟着教程学习了一段时间数据分析,越学感觉坑越多。于是花了一个星期仔细看了下《利用Python进行数据分析》。写在这里主要是记录下,方便自己查看。
import matplotlib.pyplot as plt
import numpy as np
最简单的方法使用array函数,输入一个序列即可,比如list
# 随机生成数据
data = np.random.randn(2, 3)
data
array([[ 1.8878077 , -0.09071248, -0.17286876],
[-1.39093014, -1.82384125, 0.82493751]])
每一个数组都有一个shape,来表示维度大小。而dtype,用来表示data type:
# 多维数组
data2 = [[1, 2, 3, 4], [5, 6, 7, 8]]
arr2 = np.array(data2)
arr2
array([[1, 2, 3, 4],
[5, 6, 7, 8]])
print('维度大小:',arr2.shape)
print('维度值:', arr2.ndim)
print('数据类型:',arr2.dtype)
维度大小: (2, 4)
维度值: 2
数据类型: int32
除非主动声明,否则np.array会自动给data搭配适合的类型,并保存在dtype里
也可以指定数据类型
arr1 = np.array([1, 2, 3], dtype=np.float64)
arr2 = np.array([1, 2, 3], dtype=np.int32)
print('arr1数据类型:',arr1.dtype)
print('arr2数据类型:',arr2.dtype)
arr1数据类型: float64
arr2数据类型: int32
用astype来转换类型,astype总是会返回一个新的数组
int_arr1 = arr1.astype(np.int)
int_arr1.dtype
dtype('int32')
除了np.array,还有一些其他函数能创建数组。比如zeros,ones,另外还可以在一个tuple里指定shape
np.zeros((3, 6))
array([[0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0.]])
# np.empty并不能保证返回所有是0的数组,某些情况下,会返回为初始化的垃圾数值,如下面
np.empty((2, 3, 2))
array([[[5.63e-322, 0.00e+000],
[0.00e+000, 0.00e+000],
[0.00e+000, 0.00e+000]],
[[0.00e+000, 0.00e+000],
[0.00e+000, 0.00e+000],
[0.00e+000, 0.00e+000]]])
np.arange(10)
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
数组之所以重要,是因为不用写for循环就能表达很多操作,这种特性叫做vectorization(向量化)。任何两个大小相等的数组之间的运算,都是element-wise(点对点)
data = np.arange(5)
print('*:', data*10)
print('+:',data+data)
*: [ 0 10 20 30 40]
+: [0 2 4 6 8]
# 两个数组的比较会产生布尔数组:
arr1 = np.array([[1., 2., 3.], [4., 5., 6.]])
arr2 = np.array([[0., 4., 1.], [7., 2., 12.]])
arr2 > arr1
array([[False, True, False],
[ True, False, True]])
一维数组操作同list,array的切片后的结果只是一个views(视图),用来代表原有array对应的元素,而不是创建了一个新的array。但list里的切片是产生了一个新的list
arr2d = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
arr2d[2]
array([7, 8, 9])
arr2d[2][1]
8
arr2d[:2]
array([[1, 2, 3],
[4, 5, 6]])
# 前两行,第二列之后
arr2d[:2, 1:]
array([[2, 3],
[5, 6]])
arr2d[:2, 2]
array([3, 6])
# 冒号表示提取整个axis(轴)
arr2d[:, :1]
array([[1],
[4],
[7]])
# 多维数组
arr3d = np.array([[[1, 2, 3], [4, 5, 6]], [[7, 8, 9], [10, 11, 12]]])
arr3d
array([[[ 1, 2, 3],
[ 4, 5, 6]],
[[ 7, 8, 9],
[10, 11, 12]]])
arr3d[1, 0] # 该操作相当于 x = arr3d[1], x[0]的结果
array([7, 8, 9])
data = np.random.randn(7, 4)
data
array([[-1.33904841, -1.4637553 , -1.377123 , 0.09439955],
[ 1.40173772, 2.63681109, -2.11408761, -1.19171294],
[-1.58056578, 0.22323485, 1.85082002, -0.76870622],
[ 0.18076579, -0.24595339, 1.21591539, 0.30617942],
[-0.80319239, 0.64991022, -0.46159542, -1.23788867],
[ 1.16571902, 0.98157039, -1.3452736 , 0.22940809],
[-0.01013532, -0.31661346, 1.57364789, 0.05199267]])
# 让所有负数变为0
data[data < 0] = 0
data
array([[0. , 0. , 0. , 0.09439955],
[1.40173772, 2.63681109, 0. , 0. ],
[0. , 0.22323485, 1.85082002, 0. ],
[0.18076579, 0. , 1.21591539, 0.30617942],
[0. , 0.64991022, 0. , 0. ],
[1.16571902, 0.98157039, 0. , 0.22940809],
[0. , 0. , 1.57364789, 0.05199267]])
# ~表示反转
data[~(data < 0)] = 0
data
array([[0., 0., 0., 0.],
[0., 0., 0., 0.],
[0., 0., 0., 0.],
[0., 0., 0., 0.],
[0., 0., 0., 0.],
[0., 0., 0., 0.],
[0., 0., 0., 0.]])
不论数组有多少维,花式索引的结果总是一维,要记住,fancy indexing和切片不同,得到的是一个新的array。
# 创建一个8 x 4的空数组
arr = np.empty((8, 4))
for i in range(8):
arr[i] = i
arr
array([[0., 0., 0., 0.],
[1., 1., 1., 1.],
[2., 2., 2., 2.],
[3., 3., 3., 3.],
[4., 4., 4., 4.],
[5., 5., 5., 5.],
[6., 6., 6., 6.],
[7., 7., 7., 7.]])
# 按一定顺序选出几行,索引值
arr[[4, 3, 0, 6]]
array([[4., 4., 4., 4.],
[3., 3., 3., 3.],
[0., 0., 0., 0.],
[6., 6., 6., 6.]])
# 负号来从后选择
arr[[-3, -5, -7]]
array([[5., 5., 5., 5.],
[3., 3., 3., 3.],
[1., 1., 1., 1.]])
arr = np.arange(32).reshape((8, 4))
arr
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11],
[12, 13, 14, 15],
[16, 17, 18, 19],
[20, 21, 22, 23],
[24, 25, 26, 27],
[28, 29, 30, 31]])
# 可以看到[ 4, 23, 29, 10]分别对应(1, 0), (5, 3), (7, 1), (2, 2)
arr[[1, 5, 7, 2], [0, 3, 1, 2]]
array([ 4, 23, 29, 10])
arr[[1, 5, 7, 2]][:, [0, 3, 1, 2]]
array([[ 4, 7, 5, 6],
[20, 23, 21, 22],
[28, 31, 29, 30],
[ 8, 11, 9, 10]])
上面的意思是,先从arr中选出[1, 5, 7, 2]这四行:
array([[ 4, 5, 6, 7],
[20, 21, 22, 23],
[28, 29, 30, 31],
[ 8, 9, 10, 11]])
然后[:, [0, 3, 1, 2]]表示选中所有行,但是列的顺序要按0,3,1,2来排。于是得到:
array([[ 4, 7, 5, 6],
[20, 23, 21, 22],
[28, 31, 29, 30],
[ 8, 11, 9, 10]])
转置也是返回一个view,而不是新建一个数组。有两种方式,一个是transpose方法,一个是T属性:
arr = np.arange(15).reshape((3, 5))
arr
array([[ 0, 1, 2, 3, 4],
[ 5, 6, 7, 8, 9],
[10, 11, 12, 13, 14]])
arr.T
array([[ 0, 5, 10],
[ 1, 6, 11],
[ 2, 7, 12],
[ 3, 8, 13],
[ 4, 9, 14]])
arr = np.arange(8).reshape((4, 2))
print('转换后',arr.T)
print('原始值',arr)
转换后 [[0 2 4 6]
[1 3 5 7]]
原始值 [[0 1]
[2 3]
[4 5]
[6 7]]
np.dot(arr.T, arr)
array([[56, 68],
[68, 84]])
上面的例子是 (2x4) x (4x2) = (2x2)。得到的结果是2x2维,就是普通的矩阵乘法。
对于多维数组,transpose会接受由轴数字组成的tuple,来交换轴:
arr = np.arange(16).reshape((2, 2, 4))
arr
array([[[ 0, 1, 2, 3],
[ 4, 5, 6, 7]],
[[ 8, 9, 10, 11],
[12, 13, 14, 15]]])
arr.transpose((1, 0, 2))
array([[[ 0, 1, 2, 3],
[ 8, 9, 10, 11]],
[[ 4, 5, 6, 7],
[12, 13, 14, 15]]])
这里,secode axis(1)被设为第一个,first axis(0)第二个,最后的axis没边。
使用.T
来转置swapping axes(交换轴)的一个特殊情况。ndarray有方法叫做swapaxes, 这个方法取两个axis值,并交换这两个轴:
arr
array([[[ 0, 1, 2, 3],
[ 4, 5, 6, 7]],
[[ 8, 9, 10, 11],
[12, 13, 14, 15]]])
# 直交换second axis和last axis
# swapaxes也是返回view,不生成新的data。
arr.swapaxes(1, 2)
array([[[ 0, 4],
[ 1, 5],
[ 2, 6],
[ 3, 7]],
[[ 8, 12],
[ 9, 13],
[10, 14],
[11, 15]]])
arr = np.arange(10)
arr
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
# 取根号
np.sqrt(arr)
array([0. , 1. , 1.41421356, 1.73205081, 2. ,
2.23606798, 2.44948974, 2.64575131, 2.82842712, 3. ])
# 指数
np.exp(arr)
array([1.00000000e+00, 2.71828183e+00, 7.38905610e+00, 2.00855369e+01,
5.45981500e+01, 1.48413159e+02, 4.03428793e+02, 1.09663316e+03,
2.98095799e+03, 8.10308393e+03])
x = np.random.randn(8)
y = np.random.randn(8)
print('x=',x)
print('y=',y)
x= [ 1.62629116 0.50042105 0.88487288 -0.51454008 -0.89496779 -0.08716105
-0.89026276 -0.15930902]
y= [ 1.20995668 1.57812313 0.75357992 0.27981496 -1.21836378 -2.21635872
0.46375206 0.12810278]
# 取最大值
np.maximum(x, y)
array([ 1.62629116, 1.57812313, 0.88487288, 0.27981496, -0.89496779,
-0.08716105, 0.46375206, 0.12810278])
尽管不常见,但ufunc也能返回多个数组。例如modf,这是一个向量版的divmod(python内建函数),modf会返回小数部分和整数部分:
本函数是实现a除以b,然后返回商与余数的元组。如果两个参数a,b都是整数,那么会采用整数除法,结果相当于(a//b, a % b)。如果a或b是浮点数,相当于(math.floor(a/b), a%b)。
arr = np.random.randn(7) * 5
arr
array([ 0.23231361, 1.94750024, -5.65093763, -7.51480076, -0.34334917,
6.57644177, 1.85733655])
remainder, whole_part = np.modf(arr)
# 返回小数部分
remainder
array([ 0.23231361, 0.94750024, -0.65093763, -0.51480076, -0.34334917,
0.57644177, 0.85733655])
# 返回整数部分
whole_part
array([ 0., 1., -5., -7., -0., 6., 1.])
之所以称之为伪随机数,是因为随机数生成算法是根据seed来生成的。也就是说,只要seed设置一样,每次生成的随机数是相同的:
相对的,python内建的random模块一次只能生成一个样本。在生成大量样本方法,numpy.random是非常快的:
np.random.seed(2018)
samples = np.random.normal(size=(4, 4))
samples
array([[-0.2767676 , 0.581851 , 2.14839926, -1.279487 ],
[ 0.50227689, 0.8560293 , -0.14279008, 0.11007867],
[-0.68806479, 0.43356408, 0.510221 , -0.16513097],
[-1.35177905, 0.54663075, 1.23065512, 1.0764461 ]])
seed是全局的,如果想要避免全局状态,可以用numpy.random.RandomState来创建一个独立的生成器
rng = np.random.RandomState(2017)
samples = np.random.normal(size=(4, 4))
samples
array([[-1.21062488, -0.30667657, -1.05741884, 0.40205692],
[ 0.28916512, 1.28273322, -1.0656958 , -1.70663287],
[-0.17279739, 0.06371017, 0.37062839, -1.60454294],
[-2.16572937, 0.38037013, -0.27650109, -0.57568194]])
下面是是写numpy.random里的函数:
# [X,Y] = meshgrid(x,y) 将向量x和y定义的区域转换成矩阵X和Y
# 其中矩阵X的行向量是向量x的简单复制,而矩阵Y的列向量是向量y的简单复制
m, n = (5, 3)
x = np.linspace(0, 1, m) # linspace() 线性等分向量(linear space)
y = np.linspace(0, 1, n)
X, Y = np.meshgrid(x, y)
print('x=',x)
print('y=',y)
print()
print('X=',X)
print('Y=',Y)
x= [0. 0.25 0.5 0.75 1. ]
y= [0. 0.5 1. ]
X= [[0. 0.25 0.5 0.75 1. ]
[0. 0.25 0.5 0.75 1. ]
[0. 0.25 0.5 0.75 1. ]]
Y= [[0. 0. 0. 0. 0. ]
[0.5 0.5 0.5 0.5 0.5]
[1. 1. 1. 1. 1. ]]
np.where(a,b,c)
np.where
中第二个和第三个参数不用必须是数组
- a为真返回b
- a为假返回c
xarr = np.array([1.1, 1.2, 1.3, 1.4, 1.5])
yarr = np.array([2.1, 2.2, 2.3, 2.4, 2.5])
cond = np.array([True, False, True, True, False])
result = np.where(cond, xarr, yarr)
result
array([1.1, 2.2, 1.3, 1.4, 2.5])
# 把所有的正数变为2,所有的负数变为-2
arr = np.random.randn(4, 4)
print('arr= ', arr)
np.where(arr > 0, 2, -2)
arr= [[-1.69790361 1.14692981 2.01866833 1.19685501]
[-1.29768641 0.96410493 0.51591302 -0.04725618]
[ 0.97338591 -0.88312949 0.53144571 1.0647404 ]
[-0.60385003 -1.21294763 0.70985062 0.23991391]]
array([[-2, 2, 2, 2],
[-2, 2, 2, -2],
[ 2, -2, 2, 2],
[-2, -2, 2, 2]])
# 只把整数变为2,其他仍未原来的数字
np.where(arr > 0, 2, arr)
array([[-1.69790361, 2. , 2. , 2. ],
[-1.29768641, 2. , 2. , -0.04725618],
[ 2. , -0.88312949, 2. , 2. ],
[-0.60385003, -1.21294763, 2. , 2. ]])
arr = np.random.randn(5, 4)
arr
array([[ 0.42185431, 0.7047803 , 0.04540116, 0.53069007],
[ 0.13812536, 0.37046313, -0.0538778 , 1.072796 ],
[ 0.41525259, -0.79155701, -0.80337487, 0.00400008],
[ 0.8381884 , 0.04708372, -2.22118689, 0.44879045],
[-0.01343378, 0.78704248, 0.18833414, -1.58023087]])
print('平均值:', arr.mean())
print('平均值:', np.mean(arr)) # 同上
print('中位数:', np.median(arr))
print('求和:', arr.sum())
print('方差:',arr.var())
print('标准差', arr.std())
print('最小值:',arr.min())
print('最大值:',arr.max())
print('最大值与最小值之间的差值:', np.ptp(arr))
print()
print('数组中最小元素的索引值:',arr.argmin())
print('数组中最大元素的索引值:',arr.argmax())
print('\n绝对值:\n', np.abs(arr))
print()
print('返回一个由相邻数组元素的差值构成的数组:\n', np.diff(arr))
平均值: 0.02745704812129939
平均值: 0.02745704812129939
中位数: 0.16322975085704675
求和: 0.5491409624259878
方差: 0.6344954097327102
标准差 0.7965522015114328
最小值: -2.2211868856516417
最大值: 1.0727959997704182
最大值与最小值之间的差值: 3.29398288542206
数组中最小元素的索引值: 14
数组中最大元素的索引值: 7
绝对值:
[[0.42185431 0.7047803 0.04540116 0.53069007]
[0.13812536 0.37046313 0.0538778 1.072796 ]
[0.41525259 0.79155701 0.80337487 0.00400008]
[0.8381884 0.04708372 2.22118689 0.44879045]
[0.01343378 0.78704248 0.18833414 1.58023087]]
返回一个由相邻数组元素的差值构成的数组:
[[ 0.28292599 -0.65937913 0.48528891]
[ 0.23233777 -0.42434094 1.1266738 ]
[-1.2068096 -0.01181786 0.80737495]
[-0.79110468 -2.26827061 2.66997733]
[ 0.80047625 -0.59870834 -1.76856501]]
# axis作为参数来计算统计数字
print('横向:',arr.mean(axis=1)) # 横向
print('横向:',arr.mean(1))
print()
print('纵向:',arr.mean(axis=0)) # 纵向
print('纵向:',arr.mean(0))
print()
print('横向:',arr.sum(axis=1)) # 横向
print('纵向:',arr.sum(0)) # 纵向
横向: [-1.47618477 -0.40924536 0.27245489 0.22881218 1.1980545 ]
横向: [-1.47618477 -0.40924536 0.27245489 0.22881218 1.1980545 ]
纵向: [-0.22275301 0.02190233 -0.6217432 0.67370703]
纵向: [-0.22275301 0.02190233 -0.6217432 0.67370703]
横向: [-5.90473907 -1.63698144 1.08981957 0.91524871 4.79221799]
纵向: [-1.11376504 0.10951164 -3.10871598 3.36853515]
# 元素累计和
arr = np.array([0, 1, 2, 3, 4, 5, 6, 7])
arr.cumsum()
array([ 0, 1, 3, 6, 10, 15, 21, 28], dtype=int32)
arr = np.array([[0, 1, 2], [3, 4, 5], [6, 7, 8]])
arr
array([[0, 1, 2],
[3, 4, 5],
[6, 7, 8]])
# 纵向相加
arr.cumsum(axis=0) # 纵向
array([[ 0, 1, 2],
[ 3, 5, 7],
[ 9, 12, 15]], dtype=int32)
# 横向相乘
arr.cumprod(axis=1) # 横向
array([[ 0, 0, 0],
[ 3, 12, 60],
[ 6, 42, 336]], dtype=int32)
# 数组中大于0的个数
arr = np.random.randn(100) # 返回符合正态分布的数值
(arr > 0).sum()
56
bools = np.array([False, False, True, False])
print('any结果:', bools.any())
print('all结果:', bools.all())
any结果: True
all结果: False
arr = np.random.randn(10)
print('排序前:\n', arr)
arr.sort()
print('排序后:\n', arr)
排序前:
[ 1.59249466 0.6515314 -0.22653 -2.09396182 1.98428654 -0.41047994
0.4461693 0.53309987 0.50317313 0.45538713]
排序后:
[-2.09396182 -0.41047994 -0.22653 0.4461693 0.45538713 0.50317313
0.53309987 0.6515314 1.59249466 1.98428654]
如果是多维数组,还可以按axis来排序:
arr = np.random.randn(5, 3) # 5行3列
arr
array([[ 0.64807367, 0.64734467, -0.57956506],
[ 1.30009271, -1.0096845 , -0.90608046],
[ 0.96544172, -0.82934629, -0.1372017 ],
[ 1.2585547 , 0.90157093, 1.55362495],
[-1.22715482, -1.68016336, -1.31421393]])
# arr.sort(1) # 横向
# arr.sort(0) # 纵向
arr.sort(axis=1) # 同上
arr
array([[-0.57956506, 0.64734467, 0.64807367],
[-1.0096845 , -0.90608046, 1.30009271],
[-0.82934629, -0.1372017 , 0.96544172],
[ 0.90157093, 1.2585547 , 1.55362495],
[-1.68016336, -1.31421393, -1.22715482]])
# 一个计算分位数的快捷方法是先给数组排序,然后选择某个排名的值:
large_arr = np.random.randn(1000)
large_arr.sort()
large_arr[int(0.05 * len(large_arr))] # 5% quantile
-1.5947780268732517
Numpy也有一些基本的集合操作用于一维数组。np.unique
,能返回排好序且不重复的值:
names = np.array(['Bob', 'Joe', 'Will', 'Bob', 'Will', 'Joe', 'Joe'])
np.unique(names)
array(['Bob', 'Joe', 'Will'], dtype='
ints = np.array([3, 3, 3, 2, 2, 1, 1, 4, 4])
np.unique(ints)
array([1, 2, 3, 4])
# 矩阵乘法
x = np.array([[1., 2., 3.], [4., 5., 6.]])
y = np.array([[6., 23.], [-1, 7], [8, 9]])
print('x:\n',x)
print('y:\n',y)
# x.dot(y)等同于np.dot(x, y)
x.dot(y)
x:
[[1. 2. 3.]
[4. 5. 6.]]
y:
[[ 6. 23.]
[-1. 7.]
[ 8. 9.]]
array([[ 28., 64.],
[ 67., 181.]])
一个二维数组和一个一维数组的矩阵乘法,得到一个一维数组:
np.dot(x, np.ones(3))
# 这里应该是用狂了boradcasting,x中的每一行与[1, 1, 1]点对点乘积后求和
array([ 6., 15.])
# @作为一个中缀计算符,也能实现矩阵乘法:
x @ np.ones(3)
array([ 6., 15.])
np.linalg
能用来做矩阵分解,以及比如转置和求秩之类的事情:
from numpy.linalg import inv, qr
# X = np.round(np.random.randn(5, 5), 3) # 这里我们用np.round控制小数点后的位数,看起来更舒服一些
X = np.random.randn(5, 5)
X
array([[-0.24821301, -0.73158913, 1.89565751, -1.23795063, -0.63359619],
[ 0.29635489, 0.50478366, -0.31098479, -1.34502933, 0.17482175],
[ 2.02590827, -1.15869345, -0.8894822 , 0.266318 , 1.07333646],
[-1.31303883, -1.40763291, -0.4048509 , -1.92894314, -0.86348757],
[ 0.2866783 , 0.73102305, 0.11381171, -1.74766983, 0.77151067]])
# X.T.dot(X)计算的是X和X的转置的矩阵乘法。
mat = X.T.dot(X)
np.round(mat, 2)
array([[ 6.06, 0.04, -1.8 , 2.48, 3.74],
[ 0.04, 4.65, 0.14, 1.36, 1.09],
[-1.8 , 0.14, 4.66, -1.58, -1.77],
[ 2.48, 1.36, -1.58, 10.19, 1.15],
[ 3.74, 1.09, -1.77, 1.15, 2.92]])
np.round(inv(mat), 2)
array([[ 2.22, 0.9 , -0.58, -0.37, -3.38],
[ 0.9 , 0.62, -0.3 , -0.18, -1.48],
[-0.58, -0.3 , 0.45, 0.13, 1.07],
[-0.37, -0.18, 0.13, 0.17, 0.55],
[-3.38, -1.48, 1.07, 0.55, 5.65]])
np.round(mat.dot(inv(mat)), 2)
array([[ 1., 0., 0., 0., -0.],
[-0., 1., 0., 0., 0.],
[ 0., -0., 1., 0., 0.],
[-0., -0., 0., 1., -0.],
[ 0., 0., -0., -0., 1.]])
q, r = qr(mat)
np.round(r, 2)
array([[-7.75, -0.98, 3.85, -6.13, -5.12],
[ 0. , -4.87, -0.19, -3.13, -0.96],
[ 0. , 0. , -3.97, 1.72, 0.59],
[ 0. , 0. , 0. , -8.08, 0.83],
[ 0. , 0. , 0. , 0. , 0.15]])
一些常用的numpy.linalg函数: