天天看點

利用python進行資料分析-第四章筆記

利用python進行資料分析的第四章閱讀筆記。

Chapter 4 NumPy Basics: Arrays and Vectorized Computation

題外話:numpy short for numerical python 😃

Ndarray: A Multidimensional Array Object

ndarray: short for N-dimensional array object. 一個最直覺的優點是可以直接操作整個 ndarray 的元素而不必使用 for loop。

盡量使用

import numpy as np
np.function()
           

防止 python 的内置函數與 numpy 中給出的函數産生沖突。

  • Creating ndarrays

一些常用方法:

# 1
# params: any sequence-like object
np.array()
# 2
# params: shape(tuple)
np.zeros((1,2))
np.ones((2,3))
np.empty((3,4))
# 3
# params: range(int), like python built-in range
np.arange(15)
           

順便可以在建立時,規定資料類型:

np.array([1,2,3,4,5], dtype=np.float64)

利用python進行資料分析-第四章筆記

還可以使用

astype

對已有的 array 進行資料類型的轉換:

arr = np.array([1,2,3])
arr1 = arr.astype(np.float64)

# 字元串如果無法轉換成相應的資料類型會 ValueError
arr2 = np.array(['1.2', '2.3'])
arr3 = arr2.astype(np.float)
           
  • Arithmetic with NumPy Arrays
Arrays are important because they enable you to express batch operations on data without writing any for loops. NumPy users call this vectorization. Any arithmetic operations between equal-size arrays applies the operation element-wise.

需要注意的是,arr * arr 的運算是 element-wise 的,即各位置相應元素相乘,也就是說不是矩陣之間的運算。

Operations between differently sized arrays is called broadcasting and will be discussed in more detail in Appendix A.
  • Basic Indexing and Slicing

對于一維的 ndarray,slicing 操作與 list 的 slicing 很類似,但是需要重點關注不同點:

  1. ndarray 支援 broadcast 操作,例如

    ndarray[3:7]=12

    是允許的,但是在 list 上就不可以;
  2. 最重要的,ndarray 不會對 slicing 的切片進行 copy,也就是說對 slicing 的一切修改會反映在原 ndarray 上:
a = np.array([1,2,3,4])
arr = a[1:4]
arr[:] = 43
print(arr)
print(a)
           

這種設計的原因是顧慮 numpy 在處理大規模資料時的效率問題。

If you want a copy of a slice of an ndarray instead of a view, you will need to explicitly copy the array—for example, arr[5:8].copy().

對于二維和更高維的 ndarray,slicing 就相對難了解一些,一個較好了解的方式是從外向裡,例如:

arr3d = np.array([[[1, 2, 3], [4, 5, 6]], [[7, 8, 9], [10, 11, 12]]])
arr3d[0] # = [[1, 2, 3], [4, 5, 6]]
arr3d[0,1] # = [4, 5, 6]
arr3d[:2, 1:] # = [[[4,5,6],[10,11,12]]]
           
  • Boolean Indexing

這個性質就是 mask 的實作基礎。直接看代碼好了解一些。

names = np.array(['Bob', 'Joe', 'Will', 'Bob', 'Will', 'Joe', 'Joe'])
data = np.random.randn(7, 4)
data[names == 'Bob'] # = [data[0], data[3]] 
           

也可以在

data[names == 'Bob']

裡指定列的序号,同時可以使用更複雜的 bool 表達式來表示條件。也可以結合之前 slicing 的 broadcast 性質進行指派。

  • Fancy Indexing

顧名思義,這種方式十分 fancy 😃

arr = np.arange(32).reshape((8,4))
arr[[0,1,2,3]]
# [[ 0  1  2  3]
# [ 4  5  6  7]
# [ 8  9 10 11]
# [12 13 14 15]]
           

index 是負數的時候從後向前,與 list 的 index 類似。

還有兩個比較複雜的例子:

arr[[1, 5, 7, 2], [0, 3, 1, 2]] 
# arr[1,0], arr[5,3], arr[7,1], arr[2,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]]) 
           
  • Transposing Arrays and Swapping Axes

對于二維的 ndarray,Transposing 就是我們常說的矩陣轉置,對于更高維的 ndarray,transpose 可能不是那樣直覺,但是隻要記住轉置的定義是

a[dimension_1][dimension_2] = a[dimension_2][dimension_1]

即可。

arr = np.arange(16).reshape((2,2,4))
arr.transpose((1,0,2))
# array([[[ 0,  1,  2,  3],[ 8,  9, 10, 11]],
#        [[ 4,  5,  6,  7],[12, 13, 14, 15]]])
           

對于那些一步得不到的轉置,會通過多次轉置的方式得到:

arr = np.arange(32).reshape((4,2,2,2))
print(np.transpose(arr, (1,3,2,0)))
# a: 0 1 2 3
a = np.transpose(arr, (1,0,2,3))
# a: 1 0 2 3
a = np.transpose(a, (0,3,2,1))
# a: 1 3 2 0
print(a)
           

.T

運算符是一個 transpose 的簡單形式,對于二維的 ndarray,

.T

就表示矩陣的轉置,而對于多元的 ndarray,

.T

算符的規則是:

a.shape # = (d0, d1, d2, d3, ..., dn)
a.T.shape # = (dn, ..., d2, d1, d0)
           

swapaxes

方法與 transpose 和

.T

算符類似,不同的是隻支援兩個次元的轉換:

arr.swapaxes(axis1, axis2)
           

注意:

swapaxes similarly returns a view on the data without making a copy.

這裡涉及到 view 和 copy 的差別,其實有點類似于 C++ 中的引用,先看一段代碼:

arr = np.arange(32).reshape((4,2,2,2))
print(arr)
print(arr.transpose(3,1,2,0))
brr = arr.swapaxes(3,0)
brr[0,0,0,0] = 111111
brr[0,0,0,1] = 222222
print(arr)
print(brr)
           

這裡的 brr 就是 arr 的一個 view,個人的了解是 brr 的每個位置上存的都是 arr 中每個元素的 view(類似于引用),修改 brr 的同時就直接把 arr 的相應位置的值也都修改了。

Universal Functions: Fast Element-Wise Array Functions

A universal function, or ufunc, is a function that performs element-wise operations on data in ndarrays.

一些常用的一進制 ufunc,詳細的參數資訊可以檢視 numpy 文檔:

利用python進行資料分析-第四章筆記

常用二進制 ufunc:

利用python進行資料分析-第四章筆記
利用python進行資料分析-第四章筆記

Array-Oriented Programming with Arrays

這部分講了一些不用 for loop 進行 ndarray 計算的執行個體。

  • 使用 meshgrid 構造坐标點矩陣計算多個點間距離
x, y = np.meshgrid([1,2,3],[-1,-1.5,-2])
print(x, y)
           

x 和 y 的相應位置表示一個點的橫縱坐标,如果把圖像畫出來是這樣的:

利用python進行資料分析-第四章筆記

是以計算距離就可以使用:

z = np.sqrt(x ** 2 + y ** 2)
           

順便書中還把距離的灰階圖畫了出來,可以參考一下。

  • Expressing Conditional Logic as Array Operations

主要講了 numpy.where() 的使用。

用法是:

np.where(arr > 0, 2, -2)

,即 arr 中大于0的元素指派為2,否則指派為-2。

where 的文檔是這麼描述的:

where(condition, x=None, y=None, /)

param condition

where(condition, [x, y])

Return elements, either from

x

or

y

, depending on

condition

.

If only

condition

is given, return

condition.nonzero()

Parameters

condition : array_like, bool

When True, yield

x

, otherwise yield

y

x, y : array_like, optional

Values from which to choose.

x

,

y

and

condition

need to be

broadcastable to some shape.

發現 condition, x 和 y 其實都是 array-like 的形式,這裡其實是有 broadcast 的支援。

  • Mathematical and Statistical Methods

一些簡單的統計方法。

利用python進行資料分析-第四章筆記

解釋一下:

cumsum 和 cumprod 的 axis 參數表示計算累加和(積)的軸,axis = 0 表示第一個次元,也就是直覺上的從上到下計算累加和(積),而不是表示對第幾維的元素做累加和(積)。即 axis 表示方向。

  • Methods for Boolean Arrays

boolean arrays 也是 ndarray,是以相應的統計方法都适用,同時還有

bools.any()

bools.all()

兩個方法,類似于集合運算中的并和交。

  • Sorting

這裡 numpy 的 sort 和 python 内置的 sort 很類似,numpy 的 sort 可以規定 axis。需要注意的是

np.sort()

傳回的是一個 copy,而

arr.sort()

是在原地修改 arr 的值,沒有傳回值。

arr = np.random.randn(5,3)
print(arr)
brr = np.sort(arr, axis=1)
print(brr)
print(arr)
arr.sort(1)
print(arr)
           
  • Unique and Other Set Logic

對于一維的 ndarray,numpy 提供了一些獨有的方法。

利用python進行資料分析-第四章筆記

File Input and Output with Arrays

np.save()

np.load()

  • input:

    np.load(file_path)

  • output:
np.save(file_name, arr)
np.savez(file_name, a=arr, b=brr)
np.savez_compressed(file_name, a=arr, b=brr)
           

有個問題是如果我想存100個甚至更多的 ndarray 怎麼辦?

Linear Algebra

一些簡單的線性代數用法:

利用python進行資料分析-第四章筆記
利用python進行資料分析-第四章筆記

分别解釋一下:

  1. diag:傳回方陣的對角元素,或者根據一個1維的 ndarray 傳回一個對角陣。
  2. dot:傳回矩陣的内積
  3. trace:傳回矩陣的 trace(對角元素和)
  4. det:傳回矩陣的行列式
  5. eig:計算矩陣的特征值和特征向量
  6. inv:計算矩陣的逆,不成功時抛出 LinAlgError
  7. pinv:計算矩陣的僞逆
  8. qr:求矩陣的 qr 分解
  9. svd:求矩陣的奇異值分解
  10. solve:求解線性方程組
  11. lstsq:求出線性方程組的最小二乘解

Pseudorandom Number Generation

python 内置的 random 比 numpy 的 random 慢。

下面是一些基本用法:

利用python進行資料分析-第四章筆記
  1. python 内置的 random.randint(a, b) 的範圍是 [a, b],而 numpy.random.randint(a, b) 的範圍是 [a,b)

Example: Random Walks

用純 python 的寫法:

import random

position = 0
walk = [position]
steps = 1000
for i in range(steps):
    x = random.randint(0,1)
    print(x)
    step = 1 if x else -1
    position = position + step
    walk.append(position)

plt.plot(walk[:100])
plt.show()
           

用 numpy 的寫法:

arr = np.random.randn(1000,)
step = np.where(arr > 0, 1, -1)
walk = step.cumsum()
           

而且可以同時進行多次的随機遊走,隻需要讓 arr 是一個二維矩陣即可:

arr = np.random.randn(3,1000)
step = np.where(arr > 0, 1, -1)
walk = step.cumsum(axis=1)
           

對 walk 使用統計方法可以對某次随機遊走進行分析。

結果如下:

利用python進行資料分析-第四章筆記

Conclusion

While much of the rest of the book will focus on building data wrangling skills with pandas, we will continue to work in a similar array-based style. In Appendix A, we will dig deeper into NumPy features to help you further develop your array computing skills.

繼續閱讀