本篇文章是python数据探索系列扫盲文章的第一篇,主要和各位朋友探讨一下numpy,后续文章会陆续介绍pandas以及Matplotlib。如有疏漏不当的地方,请留言或私信我。共同进步!
在处理一些数据时,我们常常会需要处理数组,而使用原生的array显然会比较慢。numpy有许多用 C 语言实现的底层函数,所以我们选择使用numpy进行数据探索。
一、数组基础
首先,我们明确一下研究对象——数组。在numpy里我们操作的对象就是ndarrays多维数组。我们假想一个使用环境,比如读取一个股票数据。为了
更快的使用体验,我们决定使用numpy。那么我们怎么读取这份数据呢?这涉及到numpy创建数组吧!我们来讲一下:
# 1D Array
a = np.array([0, 1, 2, 3, 4])
b = np.array((0, 1, 2, 3, 4))
c = np.arange(5)
d = np.linspace(0, 2*np.pi, 5)
print(a) # >>>[0 1 2 3 4]
print(b) # >>>[0 1 2 3 4]
print(c) # >>>[0 1 2 3 4]
print(d) # >>>[ 0. 1.57079633 3.14159265 4.71238898 6.28318531]
print(a[3]) # >>>3
我们还可以使用dtype为数据指定类型。比如:
a = np.array([2,23,4],dtype=np.int)
print(a.dtype)
# int 64
可以指定如float(64位)int32、int32等等数据类型。
还有一些特殊的数组,比如全零
a = np.zeros((3,4)) # 数据全为0,3行4列
"""
array([[ 0., 0., 0., 0.],
[ 0., 0., 0., 0.],
[ 0., 0., 0., 0.]])
"""
创建全一数组, 同时也能指定这些特定数据的 dtype:
a = np.ones((3,4),dtype = np.int) # 数据为1,3行4列
"""
array([[1, 1, 1, 1],
[1, 1, 1, 1],
[1, 1, 1, 1]])
"""
怎么读取文件呢?一句代码可以搞定,a就是numpy数组:
a = numpy.loadtxt('haha.txt')
举个例子。
以载入苹果公司的历史股价数据为例。股价数据存储在CSV文件中,第一列为股票代码以标识股票(苹果公司股票代码为AAPL),第二列为dd-mm-yyyy格式的日期,第三列为空,随后各列依次是开盘价、最高价、最低价和收盘价,最后一列为当日的成交量。下面为一行数据:
AAPL,28-01-2011, ,344.17,344.4,333.53,336.1,21144800
将收盘价和成交量分别载入到两个数组中,代码如下所示:
>>> a,b=np.loadtxt("./data.csv",
... delimiter=',',usecols=(6,7),unpack=True)
>>> print a
[ 336.1 339.32 345.03 344.32 343.44 346.5 351.88 355.2 358.16
354.54 356.85 359.18 359.9 363.13 358.3 350.56 338.61 342.62
342.88 348.16 353.21 349.31 352.12 359.56 360. 355.36 355.76
352.47 346.67 351.99]
delimiter=’,’表示用逗号作为分隔符,usecols=(6,7)表示一个元组,用来指明获取哪些列的字段,unpack=True是说拆分存储不同的数据,即分别将收盘价和日成交量赋值给a和b。
现在菜已经洗好了,我们需要进行进一步的清洗。我们要做到“指哪打哪”,挑选我们真正需要的东西。
我们先定义一个多维矩阵。多维数组切片比一维数组要复杂一点,同时它也是你在用 NumPy 的时候经常会用到的。
# MD Array,
a = np.array([[11, 12, 13, 14, 15],
[16, 17, 18, 19, 20],
[21, 22, 23, 24, 25],
[26, 27, 28 ,29, 30],
[31, 32, 33, 34, 35]])
print(a[2,4]) # >>>25
我们开始挑选我们需要的数据,其实无非就是某些数,某些行,某些列,某些块。
# MD slicing
print(a[0, 1:4]) # >>>[12 13 14]
print(a[1:4, 0]) # >>>[16 21 26]
print(a[::2,::2]) # >>>[[11 13 15]
# [21 23 25]
# [31 33 35]]
print(a[:, 1]) # >>>[12 17 22 27 32]
用一张图来说明情况。
二、数组信息获取
还是承接上文的数据,我们获得了“我们想要的数组”,那么我们总得看看我们切的数据对不对吧?那最简单的方法就是获取下信息看看。我们可以这么做:
# Array properties
a = np.array([[11, 12, 13, 14, 15],
[16, 17, 18, 19, 20],
[21, 22, 23, 24, 25],
[26, 27, 28 ,29, 30],
[31, 32, 33, 34, 35]])
print(type(a)) # >>><class 'numpy.ndarray'>
print(a.dtype) # >>>int64
print(a.size) # >>>25
print(a.shape) # >>>(5, 5)
print(a.itemsize) # >>>8
print(a.ndim) # >>>2
print(a.nbytes) # >>>200
其它信息都很直观,'itemsize' 属性是每一个条目所占的字节。这个数组的数据类型是 int64,一个 int64 的大小是 64 比特,8 比特为 1 字节,64 除以 8 就得到了它的字节数,8 字节。
三、使用数组
既然获得的我们想要的数组。接下来我们就能对数组进行操作了。
那么有哪些运算呢?我们来看看基本的操作。
# Basic Operators
>>> import numpy as np
>>> a = np.arange(25)
>>> print(a)
[ 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]
>>> a = a.reshape((5,5))
>>> print(a)
[[ 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]]
>>> b = np.array([10, 62, 1, 14, 2, 56, 79, 2, 1, 45,
... 4, 92, 5, 55, 63, 43, 35, 6, 53, 24,
... 56, 3, 56, 44, 78])
>>> b = b.reshape((5,5))
>>> print(a + b)
[[ 10 63 3 17 6]
[ 61 85 9 9 54]
[ 14 103 17 68 77]
[ 58 51 23 71 43]
[ 76 24 78 67 102]]
>>> print(a - b)
[[-10 -61 1 -11 2]
[-51 -73 5 7 -36]
[ 6 -81 7 -42 -49]
[-28 -19 11 -35 -5]
[-36 18 -34 -21 -54]]
>>> print(a * b)
[[ 0 62 2 42 8]
[ 280 474 14 8 405]
[ 40 1012 60 715 882]
[ 645 560 102 954 456]
[1120 63 1232 1012 1872]]
>>> print(a / b)
[[0. 0.01612903 2. 0.21428571 2. ]
[0.08928571 0.07594937 3.5 8. 0.2 ]
[2.5 0.11956522 2.4 0.23636364 0.22222222]
[0.34883721 0.45714286 2.83333333 0.33962264 0.79166667]
[0.35714286 7. 0.39285714 0.52272727 0.30769231]]
>>> print(a ** 2)
[[ 0 1 4 9 16]
[ 25 36 49 64 81]
[100 121 144 169 196]
[225 256 289 324 361]
[400 441 484 529 576]]
>>> print(a < b)
[[ True True False True False]
[ True True False False True]
[False True False True True]
[ True True False True True]
[ True False True True True]]
>>> print(a.dot(b))
[[ 417 380 254 446 555]
[1262 1735 604 1281 1615]
[2107 3090 954 2116 2675]
[2952 4445 1304 2951 3735]
[3797 5800 1654 3786 4795]]
除了 dot() 之外,这些操作符都是对数组进行逐元素运算。比如 (a, b, c) + (d, e, f) 的结果就是 (a+d, b+e, c+f)。它将分别对每一个元素进行配对,然后对它们进行运算。它返回的结果是一个数组。注意,当使用逻辑运算符比如 “<” 和 “>” 的时候,返回的将是一个布尔型数组。dot() 函数计算两个数组的点积。它返回的是一个标量(只有大小没有方向的一个值)而不是数组。
dot() 函数有时候也称为点积。理解这个函数的最好方法就是看下边它的计算过程。
盗图一张,在此感谢编程派的作者!
其它一些运算符:
# dot, sum, min, max, cumsum
a = np.arange(10)
print(a.sum()) # >>>45
print(a.min()) # >>>0
print(a.max()) # >>>9
print(a.cumsum()) # >>>[ 0 1 3 6 10 15 21 28 36 45]
cumsum() 就是一个累加计算并且保存每次累加的结果,返回值就是包含所有累加结果的一个列表。比如 np.array([1, 2, 3, 4, 5]).cumsum() = [1, 3, 6, 10, 15]
四、高级索引
这个直接看例子吧!
# Fancy indexing
a = np.arange(0, 100, 10)
indices = [1, 5, -1]
b = a[indices]
print(a) # >>>[ 0 10 20 30 40 50 60 70 80 90]
print(b) # >>>[10 50 90]
布尔屏蔽(boolean masking)
布尔屏蔽是一个奇妙的特性,它允许我们根据指定条件获取数组中的元素。简单说,就是设置一个约束条件比如:mask=(a<0)用mask做下标,这样在检索的时候,都会检查一遍约束条件,这样就可以选出想要的结果。
# Boolean masking
import matplotlib.pyplot as plt
a = np.linspace(0, 2 * np.pi, 50)
b = np.sin(a)
plt.plot(a,b)
mask = b >= 0
plt.plot(a[mask], b[mask], 'bo')
mask = (b >= 0) & (a <= np.pi / 2)
plt.plot(a[mask], b[mask], 'go')
plt.show()
上边的代码展示了实现布尔屏蔽。你需要做的就是传递给数组一个与它有关的条件式,然后它就会返回给定条件下为真的值。
上边的例子将会生成下边这幅图,上面的点点就是筛选出的结果:
缺省索引是从多维数组的第一维获取索引和切片便捷方法。例如,你有一个数组 a = [[1, 2, 3, 4, 5], [6, 7, 8, 9, 10]],那么 a[3] 将会返回数组第一维中索引值为 3 的元素,这里的结果是 4。
# Incomplete Indexing
a = np.arange(0, 100, 10)
b = a[:5]
c = a[a >= 50]
print(b) # >>>[ 0 10 20 30 40]
print(c) # >>>[50 60 70 80 90]
where() 函数是另外一个根据条件返回数组中的值的有效方法。只需要把条件传递给它,它就会返回一个使得条件为真的元素的列表。
# Where
a = np.arange(0, 100, 10)
b = np.where(a < 50)
c = np.where(a >= 50)[0]
print(b) # >>>(array([0, 1, 2, 3, 4]),)
print(c) # >>>[5 6 7 8 9]
参考:
编程派教程
http://codingpy.com/article/an-introduction-to-numpy/
莫烦的教程
https://morvanzhou.github.io/tutorials/data-manipulation/np-pd/