1. 程式人生 > >NumPy Essentials 帶註釋原始碼 四、NumPy 核心和模組

NumPy Essentials 帶註釋原始碼 四、NumPy 核心和模組

# 來源:NumPy Essentials ch4

步長


# 步長是每個維度相鄰兩個元素的偏移差值
import numpy as np

x = np.arange(8, dtype = np.int8)
x
# array([0, 1, 2, 3, 4, 5, 6, 7])
# x 是一維陣列,步長為 1,因為 int8 佔一個位元組
x.strides
# (1,)
# data 屬性可以觀察原始資料
str(x.data)
# '\x00\x01\x02\x03\x04\x05\x06\x07'

# 將 x 轉換為 2x4 的二維陣列
x.shape = 2, 4 
x
'''
array([[0, 1, 2, 3], 
       [4, 5, 6, 7]], dtype=int8) 
'''
# 第二維的步長是 1,等於型別大小 # 第一維的步長是 4,等於第二位步長乘以第二維的長度 x.strides # (4, 1) # 原始資料還是不變 str(x.data) # '\x00\x01\x02\x03\x04\x05\x06\x07' # 轉換為 1x4x2 的三位陣列 x.shape = 1,4,2 # 第三維的步長是 1,等於型別大小 # 第二維的步長是 2,等於第三維步長乘以第三維的長度 # 第一維的步長是 8,等於第二維步長乘以第二維的長度 x.strides # (8, 2, 1) str(x.data) # '\x00\x01\x02\x03\x04\x05\x06\x07' ''' 對於連續陣列(flags 中為連續): strides[ndim - 1] = itemsize strides[i] = strides[i + 1] * shape[i + 1] def calc_strides(shape, itemsize): ndim = len(shape) strides = [0] * ndim strides[-1] = itemsize for i in xrange(ndim - 2, -1, -1): strides[i] = strides[i + 1] * shape[i + 1] return strides '''
# 再來看看不連續陣列 # 這裡 x 是連續的,y 是不連續的 x = np.ones((10000,)) y = np.ones((10000 * 100, ))[::100] # 它們的形狀一樣,都是 10000 大小的一維陣列 x.shape, y.shape # ((10000,), (10000,)) # 值也一樣 x == y # array([ True, True, True, ..., True, True, True], dtype=bool) # 檢視它們的標識 x.flags ''' C_CONTIGUOUS : True F_CONTIGUOUS : True OWNDATA : True WRITEABLE : True ALIGNED : True UPDATEIFCOPY : False 由於 x 是一維陣列,所以行和列都連續 '''
y.flags ''' C_CONTIGUOUS : False F_CONTIGUOUS : False OWNDATA : False WRITEABLE : True ALIGNED : True UPDATEIFCOPY : False y 是由切片產生的,所以行和列都不連續 並且沒有自己的資料 ''' # 它們的步長是不一樣的 # 某個維度在切片時提供了步長 # 陣列的步長也會乘這個數 x.strides, y.strides # ((8,), (800,)) ''' 不連續陣列由於不是快取友好的 訪問也較慢 %timeit x.sum() 100000 loops, best of 3: 13.8 µs per loop %timeit y.sum() 10000 loops, best of 3: 25.9 µs per loop

結構化陣列

# 結構化陣列也叫作記錄陣列
# 它的元素是一條記錄

# 要建立這種陣列,我們需要使用陣列來表示資料,每個元素是一個元組,表示記錄
# 然後我們需要指定型別,使用陣列來表示,每個元素是個二元組
# 欄位用二元組表示,第一項是名稱,第二項是型別
x = np.array([(1, 0.5, 'NumPy'), (10, -0.5, 'Essential')], 
      dtype=[('f0', '<i4'), ('f1', '<f4'), ('f2', 'S10')]) 

# 位置下標得到的是元組(記錄)
x[0] 
# (1, 0.5, 'NumPy') 
# 還可以通過欄位名稱訪問
# 得到的是欄位值的陣列
x['f2'] 
# array(['NumPy', 'Essential'], dtype='|S10') 

# 欄位值的資料還是檢視
# 修改它會修改原始陣列
y = x['f0'] 
y 
# array([ 1, 10]) 
y[:] = y * 10 
y 
# array([ 10, 100]) 
y[:] = y + 0.5 
y 
# array([ 10, 100]) 
x 
'''
array([(10, 0.5, 'NumPy'), (100, -0.5, 'Essential')], 
    dtype=[('f0', '<i4'), ('f1', '<f4'), ('f2', 'S10')]) 
'''

# 欄位的每個元素也可以是陣列
z = np.ones((2,), dtype = ('3i4, (2,3)f4')) 
z 
'''
array([([1, 1, 1], [[1.0, 1.0, 1.0], [1.0, 1.0, 1.0]]), 
       ([1, 1, 1], [[1.0, 1.0, 1.0], [1.0, 1.0, 1.0]])], 
      dtype=[('f0', '<i4', (3,)), ('f1', '<f4', (2, 3))]) 
'''

# 我們也可以給欄位型別重新命名
x.dtype.names 
# ('f0', 'f1', 'f2') 
x.dtype.names = ('id', 'value', 'note') 
x 
''' 
array([(10, 0.5, 'NumPy'), (100, -0.5, 'Essential')], 
      dtype=[('id', '<i4'), ('value', '<f4'), ('note', 'S10')]) 
'''

# 也可以使用字典來表示型別
# names 鍵是名稱列表,formats 鍵是型別列表
dict_ex = np.zeros((2,), dtype = {'names':['id', 'value'], 'formats':['i4', '2f4']}) 
dict_ex 
'''
array([(0, [0.0, 0.0]), (0, [0.0, 0.0])], 
      dtype=[('id', '<i4'), ('value', '<f4', (2,))]) 
'''

# 屬性索引也支援多值
x[['id', 'note']]
'''
array([(10, 'NumPy'), (100, 'Essential')], 
      dtype=[('id', '<i4'), ('note', 'S10')]) 
'''

日期時間

# datetime64 可以使用字串來構造
x = np.datetime64('2015-04-01') 
y = np.datetime64('2015-04') 
x.dtype, y.dtype 
# (dtype('<M8[D]'), dtype('<M8[M]'))

# 我們也可以指定最小單位
# 缺失的值會使用 1 來填充
y = np.datetime64('2015-04', 'D') 
y, y.dtype 
# (numpy.datetime64('2015-04-01'), dtype('<M8[D]')) 
# 我們可以使用 arange 來生成日期陣列
x = np.arange('2015-01', '2015-04', dtype = 'datetime64[M]') 
x 
# array(['2015-01', '2015-02', '2015-03'], dtype='datetime64[M]') 

# 但是隻包含日期單位時,不能指定時間單位
y = np.datetime64('2015-04-01', 's') 
# TypeError: Cannot parse "2015-04-01" as unit 's' using casting rule 'same_kind' 

# datetime64 相減會生成 timedelta64
x 
# array(['2015-01', '2015-02', '2015-03'], dtype='datetime64[M]') 
y = np.datetime64('2015-01-01') 
x - y
# array([ 0, 31, 59], dtype='timedelta64[D]') 

# 我們也可以將 datetime64 與 timedelta64 相加
# 這表示 2015 年 1 月 1 日加上 12 個月是 2016 年 1 月 1 日
np.datetime64('2015') + np.timedelta64(12, 'M') 
# numpy.datetime64('2016-01') 
# 或者 timedelta64 之間的運算
# 這表示一週是 7 天
np.timedelta64(1, 'W') / np.timedelta64(1, 'D') 
# 7.0 

x 
# array(['2015-01', '2015-02', '2015-03'], dtype='datetime64[M]') 
# tolist 將 NumPy 陣列轉換成 Python 列表
# 如果陣列是 datetime64 型別
# 每個元素會轉為原生的 datetime.data
x.tolist() 
'''
[datetime.date(2015, 1, 1), 
 datetime.date(2015, 2, 1), 
 datetime.date(2015, 3, 1)] 
'''

# datetime64 的 item 方法會返回等價的 datetime.date 物件
[element.item() for element in x]
'''
[datetime.date(2015, 1, 1), 
 datetime.date(2015, 2, 1), 
 datetime.date(2015, 3, 1)] 
'''

NumPy 檔案 IO


# 首先建立記錄陣列
id = np.arange(1000) 
value = np.random.random(1000) 
day = np.random.random_integers(0, 365, 1000) * np.timedelta64(1,'D') 
date = np.datetime64('2014-01-01') + day 
# np.core.records.fromarrays 從欄位陣列建立記錄陣列
rec_array = np.core.records.fromarrays([id, value, date], names='id, value, date', formats='i4, f4, a10') 
rec_array[:5] 
'''
rec.array([(0, 0.07019801437854767, '2014-07-10'), 
       (1, 0.4863224923610687, '2014-12-03'), 
       (2, 0.9525277614593506, '2014-03-11'), 
       (3, 0.39706873893737793, '2014-01-02'), 
       (4, 0.8536589741706848, '2014-09-14')], 
      dtype=[('id', '<i4'), ('value', '<f4'), ('date', 'S10')]) 
'''

# savetxt 以純文字形式儲存陣列
# 將格式指定為逗號分隔,所以它是 CSV
np.savetxt('./record.csv', rec_array, fmt='%i,%.4f,%s') 

# 我們需要將其讀進來
# 並指定型別和分隔符
# 使用 np.loadtxt 也可以
read_array = np.genfromtxt('./record.csv', dtype='i4,f4,a10', delimiter=',', skip_header=0) 
read_array[:5] 
'''
array([(0, 0.07020000368356705, '2014-07-10'), 
       (1, 0.486299991607666, '2014-12-03'), 
       (2, 0.9524999856948853, '2014-03-11'), 
       (3, 0.3971000015735626, '2014-01-02'), 
       (4, 0.8536999821662903, '2014-09-14')], 
      dtype=[('f0', '<i4'), ('f1', '<f4'), ('f2', 'S10')]) 
'''

# 為欄位重新命名
read_array.dtype.names = ('id', 'value', 'date')

# 獲取 value 欄位是否大於 0.75
mask = read_array['value'] >= 0.75 
from numpy.lib.recfunctions import append_fields 
# append_fields 新增新的欄位
# 引數依次是源陣列、新欄位名稱、資料和型別
read_array = append_fields(read_array, 'mask', data=mask, dtypes='i1') 
read_array[:5] 
'''
masked_array(data = [(0, 0.07020000368356705, '2014-07-10', 0) 
 (1, 0.486299991607666, '2014-12-03', 0)
 
 (2, 0.9524999856948853, '2014-03-11', 1) 
 (3, 0.3971000015735626, '2014-01-02', 0) 
dtype = [('id', '<i4'), ('value', '<f4'), ('date', 'S10'), ('mask','i1')]) 
'''