python數據分析 - numpy | ndarray數組 | numpy常用函數

數據分析

什麼是數據分析?

數據分析是指用適當的統計分析方法對收集來的大量數據進行分析,提取有用信息和形成結論而對數據加以詳細研究和概括總結的過程。

使用python做數據分析的常用庫

  1. numpy 基礎數值算法
  2. scipy 科學計算
  3. matplotlib 數據可視化
  4. pandas 序列高級函數

numpy概述

  1. Numerical Python,數值的Python,補充了Python語言所欠缺的數值計算能力。
  2. Numpy是其它數據分析及機器學習庫的底層庫。
  3. Numpy完全標準C語言實現,運行效率充分優化。
  4. Numpy開源免費。

numpy歷史

  1. 1995年,Numeric,Python語言數值計算擴充。
  2. 2001年,Scipy->Numarray,多維數組運算。
  3. 2005年,Numeric+Numarray->Numpy。
  4. 2006年,Numpy脫離Scipy成爲獨立的項目。

numpy的核心:多維數組

  1. 代碼簡潔:減少Python代碼中的循環。
  2. 底層實現:厚內核©+薄接口(Python),保證性能。

numpy基礎

ndarray數組

用np.ndarray類的對象表示n維數組

>>> import numpy
>>> arr=numpy.array([1,2,3,4,5,6])
>>> arr
array([1, 2, 3, 4, 5, 6])
>>> type(arr)
<class 'numpy.ndarray'>

內存中的ndarray對象

元數據(metadata)

存儲對目標數組的描述信息,如:dim count、dimensions、dtype、data等。

實際數據

完整的數組數據

將實際數據與元數據分開存放,一方面提高了內存空間的使用效率,另一方面減少對實際數據的訪問頻率,提高性能。

ndarray數組對象的特點

  1. Numpy數組是同質數組,即所有元素的數據類型必須相同
  2. Numpy數組的下標從0開始,最後一個元素的下標爲數組長度減1

ndarray數組對象的創建

函數 說明
array 將輸入的數據(列表,元組,數組或其他序列類型)轉換爲ndarray
asarray 將輸入轉換爲ndarray , 如果輸入本身就是一個ndarray,就不進行賦值
arange 類似於內置的range,但返回的是一個ndarray而不是列表
ones , ones_like g根據指定的形狀和dtype創建一個全1的數組. ones_like以另一個數組爲參數,並根據其形狀和dtype創建一個全1的數組
zeros , zeros_like 類似於ones和ones_like,只不過產生的是全0的數組
empty , empty_like 創建新數組,只分配內存空間,但不填充任何值
full , full_like 用fill value 中的所有值,根據指定的形狀,和dtype創建一個數組. full_like使用另一個數組,用相同的形狀和dtype創建
eye , identity 創建一個正方形的N*N單位矩陣(對角線爲1,其餘爲0)

numpy.array(任何可被解釋爲Numpy數組的邏輯結構)

>>> import numpy
>>> arr=numpy.array([1,2,3,4,5,6])
>>> print(arr)
[1 2 3 4 5 6]

numpy.arange(起始值(0),終止值,步長(1))

>>> arr=numpy.arange(1,7)
>>> print(arr)
[1 2 3 4 5 6]

>>> arr=numpy.arange(1,7,2)
>>> print(arr)
[1 3 5]

numpy.zeros(數組元素個數, dtype=‘類型’)

import numpy
>>> arr=numpy.zeros(10)
>>> print(arr)
[0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]

>>> arr=numpy.zeros(10,dtype='int32')
>>> print(arr)
[0 0 0 0 0 0 0 0 0 0]

numpy.ones(數組元素個數, dtype=‘類型’)

import numpy
>>> arr=numpy.ones(10)
>>> print(arr)
[1. 1. 1. 1. 1. 1. 1. 1. 1. 1.]

>>> arr=numpy.ones(10,dtype='int32')
>>> print(arr)
[1 1 1 1 1 1 1 1 1 1]

numpy.zeros_like(數組)

# 構建一個結構與a1相同的全0數組
>>> a1=numpy.array([[1,2,3],[4,5,6]])
>>> a1
array([[1, 2, 3],
       [4, 5, 6]])
>>> print(a1)
[[1 2 3]
 [4 5 6]]

>>> arr=numpy.zeros_like(a1)
>>> print(arr)
[[0 0 0]
 [0 0 0]]

numpy.ones_like(數組)

# 構建一個結構與a1相同的全1數組
>>> a1=numpy.array([[1,2,3],[4,5,6]])
>>> a1
array([[1, 2, 3],
       [4, 5, 6]])
>>> print(a1)
[[1 2 3]
 [4 5 6]]
 
>>> arr=numpy.ones_like(a1)
>>> print(arr)
[[1 1 1]
 [1 1 1]]

ndarray對象屬性的基本操作

數組的維度: ndarray.shape

import numpy
# 一維數組
>>> arr=numpy.array([1,2,3,4,5,6])
>>> arr.shape
(6,)

# 二維數組
>>> arr=numpy.array([
		[1,2,3],
		[4,5,6]
	])
>>> arr.shape
(2, 3)

# 修改arr的維度
>>> arr.shape=(3,2)
>>> print(arr)
[[1 2]
 [3 4]
 [5 6]]
>>> print(arr.shape)
(3, 2)

元素的類型: ndarray.dtype

import numpy
>>> arr=numpy.array([1,2,3,4,5,6])
# 打印arr元素的類型
>>> arr.dtype
dtype('int64')
# 修改arr元素的類型
>>> arr.dtype='int32'
>>> arr.dtype
dtype('int32')

# astype() 不會改變原有的數組類型
#轉換arr元素的類型		# 注意: int 與 float 之間相互轉換時注意位數
>>> b=arr.astype(float)
>>> print(b.dtype)
float64
>>> print(arr.dtype)
int64		# 沒有改變原數組

#轉換arr元素的類型
>>> c=arr.astype(str)
>>> print(c.dtype)
<U11

數組元素的個數: ndarray.size

import numpy 
>>> arr=numpy.array([1,2,3,4,5,6])
>>> arr.size
6
>>> arr.shape=(3,2)
>>> arr.size
6
>>> print(arr)
[[1 2]
 [3 4]
 [5 6]]
 
#觀察維度,size,len的區別
>>> arr.shape
(3, 2)
>>> len(arr)
3
>>> arr.size
6

數組元素索引(下標)

數組對象[…, 頁號, 行號, 列號]

下標從0開始,到數組len-1結束。

import numpy
>>> arr=numpy.array([[[1,2],
					  [3,4]],
					 [[5,6],
					 [7,8]]])
>>> print(arr)
[[[1 2]
  [3 4]]

 [[5 6]
  [7 8]]]

>>> arr.shape
(2, 2, 2)
>>> print(arr[0])
[[1 2]
 [3 4]]
>>> print(arr[0][0])
[1 2]
>>> print(arr[0][0][0])
1
# 切片合併
>>> print(arr[0,0,0])
1

>>> for i in range(arr.shape[0]):
...     for j in range(arr.shape[1]):
...         for k in range(arr.shape[2]):
...             print(arr[i, j, k])
... 
1
2
3
4
5
6
7
8

ndarray對象屬性操作詳解

Numpy的內部基本數據類型

類型名 類型表示符
布爾型 bool_
有符號整數型 int8(-128~127) / int16 / int32 / int64
無符號整數型 uint8(0~255) / uint16 / uint32 / uint64
浮點型 float16 / float32 / float64
複數型 complex64 / complex128
字串型 str_,每個字符用32位(4字節)Unicode編碼表示

自定義複合類型

# 自定義複合類型
import numpy

data=[
	('zs', [90, 80, 85], 15),
	('ls', [92, 81, 83], 16),
	('ww', [95, 85, 95], 15)
]
---------------------------------------------------------------
#第一種設置dtype的方式		1個字符串(3字節) , 3個int型(32位) , 1個int型(32位)
>>> a=numpy.array(data,dtype='U3 , 3int32 , int32')
>>> print(a)
[('zs', [90, 80, 85], 15) ('ls', [92, 81, 83], 16)
 ('ww', [95, 85, 95], 15)]
 
>>> print(a[0])
('zs', [90, 80, 85], 15)

>>> print(a[0][1])
[90 80 85]
>>> print(a[0]['f1'])
[90 80 85]
>>> print(a[0]['f0'], ":", a[1]['f1'])
zs : [92 81 83]

---------------------------------------------------------------
#第二種設置dtype的方式(起別名)(列表)	
b = numpy.array(data, dtype=[('name', 'str_', 2),
                    ('scores', 'int32', 3),
                    ('ages', 'int32', 1)])
>>> print(b)
[('zs', [90, 80, 85], 15) ('ls', [92, 81, 83], 16)
 ('ww', [95, 85, 95], 15)]
>>> print(b[0])
('zs', [90, 80, 85], 15)
>>> print(b[0]['ages'])
15

------------------------------------------------------------
#第三種設置dtype的方式(起別名)(字典)    {'別名':[...] , '格式':[...]}
c = numpy.array(data, dtype={'names': ['name', 'scores', 'ages'],
                   		   'formats': ['U3', '3int32', 'int32']})
>>> print(c[0]['ages'])
15
>>> print(c.itemsize)
28
>>> print(c[0].itemsize)
28

# c.itemsize   c[0].itemsize  --> 每個對象所佔的字節數     --> itemsize 字節數
>>> print(c[0]['name'], ":", c[0]['scores'], ":", c.itemsize)
zs : [90 80 85] : 28

---------------------------------------------------------------
#第四種設置dtype的方式  {字段名:(dtype,起始字節)}
d = numpy.array(data, dtype={'names': ('U3', 0),
                    'scores': ('3int32', 16),
                    'ages': ('int32', 28)})
print(d[0]['names'], d[0]['scores'], d.itemsize)

---------------------------------------------------------------
#第五種設置dtype的方式
e = numpy.array([0x1234, 0x5667],
             dtype=('u2', {'lowc': ('u1', 0),
                            'hignc': ('u1', 1)}))
print('%x' % e[0])
print('%x %x' % (e['lowc'][0], e['hignc'][0]))

---------------------------------------------------------------
#測試日期類型數組
# D:精確到天(Y,M,D,h,m,s)
>>> f=numpy.array(['2011', '2012-01-01', '2013-01-01 01:01:01','2011-02-01'])
>>> f1=f.astype('M8[D]')
>>> f1
array(['2011-01-01', '2012-01-01', '2013-01-01', '2011-02-01'],
      dtype='datetime64[D]')
>>> f2=f1.astype('int32')
>>> print(f2)
[14975 15340 15706 15006]
>>> f2[3]-f2[0]
31

---------------------------------------------------------------
>>> a = numpy.array([[1 + 1j, 2 + 4j, 3 + 7j],
              [4 + 2j, 5 + 5j, 6 + 8j],
              [7 + 3j, 8 + 6j, 9 + 9j]])
>>> print(a.T)
[[1.+1.j 4.+2.j 7.+3.j]
 [2.+4.j 5.+5.j 8.+6.j]
 [3.+7.j 6.+8.j 9.+9.j]]

for x in a.flat:
	print(x.imag)

類型字符碼

類型 字符碼
np.bool_ ?
np.int8/16/32/64 i1/i2/i4/i8
np.uint8/16/32/64 u1/u2/u4/u8
np.float/16/32/64 f2/f4/f8
np.complex64/128 c8/c16
np.str_ U<字符數>
np.datetime64 M8[Y] M8[M] M8[D] M8[h] M8[m] M8[s]

字節序前綴,用於多字節整數和字符串:
</>/[=]分別表示小端/大端/硬件字節序

類型字符碼格式

<字節序前綴><維度><類型><字節數或字符數>

示例 釋義
3i4 大端字節序,3個元素的一維數組,每個元素都是整型,每個整型元素佔4個字節。
<(2,3)u8 小端字節序,6個元素2行3列的二維數組,每個元素都是無符號整型,每個無符號整型元素佔8個字節。
U7 包含7個字符的Unicode字符串,每個字符佔4個字節,採用默認字節序。

ndarray數組對象的維度操作

視圖變維(數據共享): reshape()ravel() - -> 修改數組內部的值,其他數組也會改變

>>> import numpy
>>> a=numpy.arange(1,9)
>>> print(a)
[1 2 3 4 5 6 7 8]
>>> b=a.reshape(2,4)		#視圖變維  : 變爲2行4列的二維數組
>>> print(b)
[[1 2 3 4]
 [5 6 7 8]]
>>> print(a)
[1 2 3 4 5 6 7 8]
>>> c=a.reshape(2,2,2)		#視圖變維    變爲2頁2行2列的三維數組
>>> print(c)
[[[1 2]
  [3 4]]

 [[5 6]
  [7 8]]]

>>> d=a.reshape(-1,2)		# -1 自動變維,根據另一個維度來變化
>>> print(d)
[[1 2]
 [3 4]
 [5 6]
 [7 8]]

# 把多維數組轉換成一維數組ravel()
>>> e=d.ravel()			#視圖變維	變爲1維數組
>>> print(e)
[1 2 3 4 5 6 7 8]

# 修改數組內部的值,其他數組也會改變
>>> a[0]=99
>>> print(a)
[99  2  3  4  5  6  7  8]
>>> print(b)
[[99  2  3  4]
 [ 5  6  7  8]]
>>> print(e)
[99  2  3  4  5  6  7  8]


複製變維(數據獨立): flatten()

>>> import numpy
>>> a=numpy.arange(1,9)
>>> print(a)
[1 2 3 4 5 6 7 8]
>>> b=a.flatten()
>>> print(b)
[1 2 3 4 5 6 7 8]
# 數據獨立
>>> a+=10
>>>> print(a)
[11 12 13 14 15 16 17 18]
>>> print(b)
[1 2 3 4 5 6 7 8]	# 數據不改變

就地變維:直接改變原數組對象的維度,不返回新數組 shaperesize()

>>> import numpy
>>> a=numpy.arange(1,9)
>>> print(a)
[1 2 3 4 5 6 7 8]

>>> a.shape=(2,4)
>>> print(a)
[[1 2 3 4]
 [5 6 7 8]]
 
>>> a.resize(2,2,2)
>>> print(a)
[[[1 2]
  [3 4]]

 [[5 6]
  [7 8]]]


ndarray數組切片操作
#數組對象切片的參數設置與列表切面參數類似
#  步長+:默認切從首到尾
#  步長-:默認切從尾到首
數組對象[起始位置:終止位置:步長, ...]
#默認位置步長:1
import numpy as np
a = np.arange(1, 10)
print(a)  # 1 2 3 4 5 6 7 8 9
print(a[:3])  # 1 2 3
print(a[3:6])   # 4 5 6
print(a[6:])  # 7 8 9
print(a[::-1])  # 9 8 7 6 5 4 3 2 1
print(a[:-4:-1])  # 9 8 7
print(a[-4:-7:-1])  # 6 5 4
print(a[-7::-1])  # 3 2 1
print(a[::])  # 1 2 3 4 5 6 7 8 9
print(a[:])  # 1 2 3 4 5 6 7 8 9
print(a[::3])  # 1 4 7
print(a[1::3])  # 2 5 8
print(a[2::3])  # 3 6 9

ndarray數組的掩碼操作

import numpy
>>> a=numpy.arange(10)
>>>> print(a)
[0 1 2 3 4 5 6 7 8 9]
>>> mask=[True, False,True, False,True, False,True, False,True, False]
>>> print(a[mask])
[0 2 4 6 8]


import numpy
# ---------bool 掩碼----------
>>>ary = numpy.arange(10)
>>>mask = ary % 2 == 0   # 取出偶數
>>>print(mask)
>>>print(ary[mask])
[ True False  True False  True False  True False  True False]
[0 2 4 6 8]

>>>b = numpy.arange(1,100)
>>>print(b[(b % 3 == 0) & (b % 7 == 0)])  # 同時爲3和7的公倍數
[21 42 63 84]

# ---------索引掩碼-------------
>>>a=numpy.array([21,32,43,54,65,21])
>>>mask=[2,3,1,3,4,0,0]	# 取出對應索引元素
>>>print(a[mask])
[43 54 32 54 65 21 21]


# 爲商品排序
>>>products=numpy.array(['Mi','Apple','SanSung','Huawei'])
>>>prices=numpy.array([2999,4999,3999,7888])
# 爲數組排序,返回有序索引
>>>index=numpy.argsort(prices)
>>>print(index)
[0 2 1 3]
>>>print(products[index])
['Mi' 'SanSung' 'Apple' 'Huawei']

numpy.argsort()

# numpy.argsort() --> 返回有序索引
>>> a=numpy.array([3,2,12,4,45])
>>> print(a)
[ 3  2 12  4 45]
>>> print(numpy.argsort(a))
[1 0 3 2 4]

多維數組的切片操作

import numpy as np
a = np.arange(1, 28)
a.resize(3,3,3)
print(a)

# 以逗號分隔

#切出1頁 
print(a[1, :, :])		
#切出所有頁的1行
print(a[:, 1, :])		
#切出0頁的1行1列
print(a[0, :, 1])		
多維數組的組合與拆分

垂直方向操作:

import numpy
>>> a=numpy.arange(1,7).reshape(2,3)
>>> print(a)
[[1 2 3]
 [4 5 6]]
>>> b=numpy.arange(7,13).reshape(2,3)
>>> print(b)
[[ 7  8  9]
 [10 11 12]]

# 垂直方向完成組合操作,生成新數組
>>> c=numpy.vstack((a,b))	# 注意: 參數爲元組
>>> print(c)
[[ 1  2  3]
 [ 4  5  6]
 [ 7  8  9]
 [10 11 12]]
# 垂直方向完成拆分操作,生成兩個數組
>>> d,e=numpy.vsplit(c,2)
>>> print(d)	# 從中間拆分(不能拆分則報錯)
[[1 2 3]
 [4 5 6]]
>>> print(e)
[[ 7  8  9]
 [10 11 12]]
>>> print(c)	# 不改變原數組
[[ 1  2  3]
 [ 4  5  6]
 [ 7  8  9]
 [10 11 12]]

水平方向操作:

import numpy as np
a = np.arange(1, 7).reshape(2, 3)
b = np.arange(7, 13).reshape(2, 3)
# 水平方向完成組合操作,生成新數組 
c = np.hstack((a, b))
# 水平方向完成拆分操作,生成兩個數組
d, e = np.hsplit(c, 2)

長度不等的數組組合:

import numpy
>>> a=numpy.array([1,2,3,4])
>>> b=numpy.array([1,2,3])

# 填充b數組使其長度與a相同
# pad_width(頭部添加0個元素,尾部添加1個元素)
# constant_values=添加的元素
>>> c=numpy.pad(b,pad_width=(0,1),mode='constant',constant_values=-1)
>>> print(c)
[ 1  2  3 -1]
>>> print(b)	# 不改變原數組
[1 2 3]

# 垂直方向完成組合操作,生成新數組
>>> d=numpy.vstack((a,c))
>>> print(d)
[[ 1  2  3  4]
 [ 1  2  3 -1]]

深度方向操作:(3維)

import numpy as np
a = np.arange(1, 7).reshape(2, 3)
b = np.arange(7, 13).reshape(2, 3)
# 深度方向(3維)完成組合操作,生成新數組
i = np.dstack((a, b))
# 深度方向(3維)完成拆分操作,生成兩個數組
k, l = np.dsplit(i, 2)

多維數組組合與拆分的相關函數:

# 通過axis作爲關鍵字參數指定組合的方向,取值如下:
# 若待組合的數組都是二維數組:
#	0: 垂直方向組合
#	1: 水平方向組合
# 若待組合的數組都是三維數組:
#	0: 垂直方向組合
#	1: 水平方向組合
#	2: 深度方向組合
np.concatenate((a, b), axis=0)
# 通過給出的數組與要拆分的份數,按照某個方向進行拆分,axis的取值同上
np.split(c, 2, axis=0)

簡單的一維數組組合方案

a = np.arange(1,9)		#[1, 2, 3, 4, 5, 6, 7, 8]
b = np.arange(9,17)		#[9,10,11,12,13,14,15,16]
#把兩個數組摞在一起成兩行
c = np.row_stack((a, b))
print(c)
#把兩個數組組合在一起成兩列
d = np.column_stack((a, b))
print(d)

ndarray類的其他屬性

  • shape - 維度

  • dtype - 元素類型

  • size - 元素數量

  • ndim - 維數,len(shape)

  • itemsize - 元素字節數

  • nbytes - 總字節數 = size x itemsize

  • real - 複數數組的實部數組

  • imag - 複數數組的虛部數組

  • T - 數組對象的轉置視圖

  • flat - 扁平迭代器

>>> import numpy
>>> a = numpy.array([[1 + 1j, 2 + 4j, 3 + 7j],
...               [4 + 2j, 5 + 5j, 6 + 8j],
...               [7 + 3j, 8 + 6j, 9 + 9j]])
>>> print(a.shape)
(3, 3)
>>> print(a.dtype)
complex128
>>> print(a.ndim)
2
>>> print(len(a.shape))
2
>>> print(len(a))
3
>>> print(a.size)
9
>>> print(a.itemsize)
16
>>> print(a.nbytes)	# size*itemsize
144
>>> print(a.real,a.imag,sep='\n')
[[1. 2. 3.]
 [4. 5. 6.]
 [7. 8. 9.]]
[[1. 4. 7.]
 [2. 5. 8.]
 [3. 6. 9.]]
>>> print(a.T)
[[1.+1.j 4.+2.j 7.+3.j]
 [2.+4.j 5.+5.j 8.+6.j]
 [3.+7.j 6.+8.j 9.+9.j]]
>>> print([elem for elem in a.flat])
[(1+1j), (2+4j), (3+7j), (4+2j), (5+5j), (6+8j), (7+3j), (8+6j), (9+9j)]
>>> a.tolist()
[[(1+1j), (2+4j), (3+7j)], [(4+2j), (5+5j), (6+8j)], [(7+3j), (8+6j), (9+9j)]]



numpy常用函數

加載文件

numpy提供了函數用於加載邏輯上可被解釋爲二維數組的文本文件,格式如下:

數據項1 <分隔符> 數據項2 <分隔符> ... <分隔符> 數據項n
例如:
AA,AA,AA,AA,AA
BB,BB,BB,BB,BB
...
或:
AA:AA:AA:AA:AA
BB:BB:BB:BB:BB
...

調用numpy.loadtxt()函數可以直接讀取該文件並且獲取ndarray數組對象:

import numpy as np
# 直接讀取該文件並且獲取ndarray數組對象 
# 返回值:
#     unpack=False:返回一個二維數組
#     unpack=True: 多個一維數組
np.loadtxt(
    '../aapl.csv',			# 文件路徑
    delimiter=',',			# 分隔符
    usecols=(1, 3),			# 讀取1、3兩列 (下標從0開始)
    unpack=False,			# 是否按列拆包
    dtype='U10, f8',		# 制定返回每一列數組中元素的類型
    converters={1:func}		# 轉換器函數字典
)    

案例:讀取aapl.csv文件,得到文件中的信息:

import numpy as np
import datetime as dt
# 日期轉換函數
def dmy2ymd(dmy):
	dmy = str(dmy, encoding='utf-8')
	time = dt.datetime.strptime(dmy, '%d-%m-%Y').date()
	t = time.strftime('%Y-%m-%d')
	return t
dates, opening_prices,highest_prices, \
	lowest_prices, closeing_pric es  = np.loadtxt(
    '../data/aapl.csv',		# 文件路徑
    delimiter=',',			# 分隔符
    usecols=(1, 3, 4, 5, 6),			# 讀取1、3兩列 (下標從0開始)
    unpack=True,
    dtype='M8[D], f8, f8, f8, f8',		# 制定返回每一列數組中元素的類型
    converters={1:dmy2ymd})

案例:使用matplotlib繪製K線圖

  1. 繪製dates與收盤價的折線圖:
import numpy as np
import datetime as dt
import matplotlib.pyplot as mp
import matplotlib.dates as md

# 繪製k線圖,x爲日期
mp.figure('APPL K', facecolor='lightgray')
mp.title('APPL K')
mp.xlabel('Day', fontsize=12)
mp.ylabel('Price', fontsize=12)

#拿到座標軸
ax = mp.gca()
#設置主刻度定位器爲周定位器(每週一顯示主刻度文本)
ax.xaxis.set_major_locator( md.WeekdayLocator(byweekday=md.MO) )
ax.xaxis.set_major_formatter(md.DateFormatter('%d %b %Y'))
#設置次刻度定位器爲日定位器 
ax.xaxis.set_minor_locator(md.DayLocator())
mp.tick_params(labelsize=8)
dates = dates.astype(md.datetime.datetime)

mp.plot(dates, opening_prices, color='dodgerblue',
		linestyle='-')
# 斜着顯示刻度
mp.gcf().autofmt_xdate()
mp.show()


  1. 繪製每一天的蠟燭圖:
#繪製每一天的蠟燭圖
#填充色:漲爲白色,跌爲綠色
rise = closeing_prices >= opening_prices
color = np.array([('white' if x else 'limegreen') for x in rise])
#邊框色:漲爲紅色,跌爲綠色
edgecolor = np.array([('red' if x else 'limegreen') for x in rise])

#繪製線條
mp.bar(dates, highest_prices - lowest_prices, 0.1,
	lowest_prices, color=edgecolor)
#繪製方塊
mp.bar(dates, closeing_prices - opening_prices, 0.8,
	opening_prices, color=color, edgecolor=edgecolor)

算數平均值

S = [s1, s2, ..., sn]

樣本中的每個值都是真值與誤差的和。

算數平均值:
m = (s1 + s2 + ... + sn) / n

算數平均值表示對真值的無偏估計。

m = np.mean(array)	# 1.mean()函數
m = array.mean()	# 2..mean()方法
# 示例
>>> import numpy
>>> a=numpy.array([1,2,3,4,2,3,3])
>>> a.mean()
2.5714285714285716
>>> numpy.mean(a)
2.5714285714285716

案例:計算收盤價的算術平均值。

import numpy as np
closing_prices = np.loadtxt(
    '../../data/aapl.csv', delimiter=',',
    usecols=(6), unpack=True)
mean = 0
for closing_price in closing_prices:
    mean += closing_price
mean /= closing_prices.size
print(mean)
mean = np.mean(closing_prices)
print(mean)

加權平均值

樣本:S=[s1,s2,s3...sn]S = [s_1, s_2, s_3 ... s_n]

權重:W=[w1,w2,w3...wn]W =[w_1, w_2, w_3 ... w_n]

加權平均值:a=s1w1+s2w2+...+snwnw1+w2+...+wna = \frac{s_1w_1 + s_2w_2 + ... + s_nw_n}{w_1+w_2+...+w_n}

a = np.average(closing_prices, weights=volumes)

>>> import numpy
>>> a=numpy.array([1,2,3,4,2,3,3])
>>> w=[1,2,1,2,1,2,4]
>>> numpy.average(a,weights=w)		# weights 權重 
2.769230769230769

VWAP - 成交量加權平均價格(成交量體現了市場對當前交易價格的認可度,成交量加權平均價格將會更接近這支股票的真實價值)

import numpy as np
closing_prices, volumes = np.loadtxt(
    '../../data/aapl.csv', delimiter=',',
    usecols=(6, 7), unpack=True)
vwap, wsum = 0, 0
for closing_price, volume in zip(
        closing_prices, volumes):
    vwap += closing_price * volume
    wsum += volume
vwap /= wsum
print(vwap)
vwap = np.average(closing_prices, weights=volumes)
print(vwap)

TWAP - 時間加權平均價格(時間越晚權重越高,參考意義越大)

import datetime as dt
import numpy as np

def dmy2days(dmy):
    dmy = str(dmy, encoding='utf-8')
    date = dt.datetime.strptime(dmy, '%d-%m-%Y').date()
    days = (date - dt.date.min).days
    return days

days, closing_prices = np.loadtxt(
    '../../data/aapl.csv', delimiter=',',
    usecols=(1, 6), unpack=True,
    converters={1: dmy2days})
twap = np.average(closing_prices, weights=days)
print(twap)

最值

np.max() np.min() np.ptp(): 返回一個數組中最大值/最小值/極差

import numpy
# 產生9個介於[10, 100)區間的隨機數
>>> a = numpy.random.randint(10,100,9)
>>> print(a)
[86 23 16 58 43 18 73 93 32]
>>> print(numpy.max(a))
93
>>> print(a.max())
93
>>> print(numpy.min(a))
16
>>> print(numpy.ptp(a)) # max-min(93-16)
77

np.argmax() mp.argmin(): 返回一個數組中最大/最小元素的下標

>>> print(numpy.argmax(a))
7
>>> print(numpy.argmin(a))
2
>>> print(a.argmin())
2

np.maximum() np.minimum(): 將兩個同維數組中對應元素中最大/最小元素構成一個新的數組

>>> a=numpy.random.randint(10,100,9)
>>> print(a)
[45 95 72 84 60 78 85 14 97]
>>> b=numpy.random.randint(10,100,9)
>>> print(b)
[60 80 94 66 57 12 22 51 75]

>>> print(numpy.maximum(a,b))	# 取較大值
[60 95 94 84 60 78 85 51 97]
>>> print(numpy.minimum(a,b))	# 取較小值
[45 80 72 66 57 12 22 14 75]

案例:評估AAPL股票的波動性。

import numpy as np
highest_prices, lowest_prices = np.loadtxt(
    '../../data/aapl.csv', delimiter=',',
    usecols=(4, 5), dtype='f8, f8', unpack=True)
max_price = np.max(highest_prices)
min_price = np.min(lowest_prices)
print(min_price, '~', max_price)

查看AAPL股票最大最小值的日期,分析爲什麼這一天出現最大最小值。

import numpy as np
dates, highest_prices, lowest_prices = np.loadtxt(
    '../../data/aapl.csv', delimiter=',',
    usecols=(1, 4, 5), dtype='U10, f8, f8',
    unpack=True)
max_index = np.argmax(highest_prices)
min_index = np.argmin(lowest_prices)
print(dates[min_index], dates[max_index])

觀察最高價與最低價的波動範圍,分析這支股票底部是否堅挺。

import numpy as np
dates, highest_prices, lowest_prices = np.loadtxt(
    '../../data/aapl.csv', delimiter=',',
    usecols=(1, 4, 5), dtype='U10, f8, f8',
    unpack=True)
highest_ptp = np.ptp(highest_prices)
lowest_ptp = np.ptp(lowest_prices)
print(lowest_ptp, highest_ptp)

中位數

將多個樣本按照大小排序,取中間位置的元素。

若樣本數量爲奇數,中位數爲最中間的元素

[1,2000,3000,4000,10000000][1, 2000, 3000, 4000, 10000000]

若樣本數量爲偶數,中位數爲最中間的兩個元素的平均值

[1,2000,3000,4000,5000,10000000][1,2000,3000,4000,5000,10000000]

案例:分析中位數的算法,測試numpy提供的中位數API:

import numpy as np
closing_prices = np.loadtxt( '../../data/aapl.csv', 
	delimiter=',', usecols=(6), unpack=True)
size = closing_prices.size
sorted_prices = np.msort(closing_prices)
median = (sorted_prices[int((size - 1) / 2)] + sorted_prices[int(size / 2)]) / 2
print(median)
median = np.median(closing_prices)
print(median)


>>> a=numpy.random.randint(10,100,9)
>>>> print(numpy.msort(a))
[14 45 60 72 78 84 85 95 97]
>>> print(a)
[45 95 72 84 60 78 85 14 97]
>>> numpy.median(a)
78.0

標準差

樣本:S=[s1,s2,s3,...,sn]S = [s_1, s_2, s_3, ..., s_n]

平均值:m=s1+s2+s3+...+snnm = \frac{s_1 + s_2 + s_3 + ... + s_n}{n}

離差:D=[d1,d2,d3,...,dn];di=SimD = [d_1, d_2, d_3, ..., d_n]; d_i = S_i-m

離差方:Q=[q1,q2,q3,...,qn];qi=di2Q = [q_1, q_2, q_3, ..., q_n]; q_i=d_i^2

總體方差:v=(q1+q2+q3+...+qn)nv = \frac{(q_1+q_2+q_3 + ... + q_n)}{n}

總體標準差:s=vs = \sqrt{v}

樣本方差:v=(q1+q2+q3+...+qn)n1v' = \frac{(q_1+q_2+q_3 + ... + q_n)}{n-1}

樣本標準差:s=vs' = \sqrt{v'}

import numpy as np
closing_prices = np.loadtxt(
    '../../data/aapl.csv', delimiter=',', usecols=(6), unpack=True)
mean = np.mean(closing_prices)         # 算數平均值
devs = closing_prices - mean           # 離差
dsqs = devs ** 2                       # 離差方
pvar = np.sum(dsqs) / dsqs.size        # 總體方差
pstd = np.sqrt(pvar)                   # 總體標準差
svar = np.sum(dsqs) / (dsqs.size - 1)  # 樣本方差
sstd = np.sqrt(svar)                   # 樣本標準差
print(pstd, sstd)
pstd = np.std(closing_prices)          # 總體標準差
sstd = np.std(closing_prices, ddof=1)  # 樣本標準差
print(pstd, sstd)

>>> a=numpy.random.randint(10,100,9)
>>> print(a)
[63 95 19 53 60 25 76 73 32]
>>> mean=numpy.mean(a)
>>> print(mean)
55.111111111111114
>>> devs=a-mean
>>> print(devs)
[  7.88888889  39.88888889 -36.11111111  -2.11111111   4.88888889  -30.11111111  20.88888889  17.88888889 -23.11111111]
>>> dsqs=devs**2
>>> print(dsqs)
[  62.2345679  1591.12345679 1304.01234568    4.45679012   23.90123457  906.67901235  436.34567901  320.01234568  534.12345679]
>>> pvar=numpy.sum(dsqs)/dsqs.size
>>> print(pvar)
575.8765432098766
>>> pstd=numpy.sqrt(pvar)
>>> print(pstd)
23.997427845706227
>>> svar=numpy.sum(dsqs)/(dsqs.size-1)
>>> print(svar)
647.8611111111111
>>> sstd=numpy.sqrt(svar)
>>> print(sstd)
25.45311594110063

>>> pstd=numpy.std(a)
>>> print(pstd)
23.997427845706227
>>> sstd=numpy.std(a , ddof=1)
>>> print(sstd)
25.45311594110063

數組的軸向彙總

案例:彙總每週的最高價,最低價,開盤價,收盤價。

def func(data):
    pass
#func 	處理函數
#axis 	軸向 [0,1]   0豎向  1橫向
#array 	數組
np.apply_along_axis(func, axis, array)

  • numpy.pad(array,pad_width,mode)
    'array'爲要填補的數組
    'pad_width'是在各維度的各個方向上想要填補的長度,如((12),(22)),
    表示在第一個維度上水平方向上padding=1,垂直方向上padding=2,在第二個維度上水平方向上padding=2,垂直方向上padding=2。
    如果直接輸入一個整數,則說明各個維度和各個方向所填補的長度都一樣。
    'mode'爲填補類型,即怎樣去填補,有“constant”,“edge”等模式,如果爲constant模式,就得指定填補的值,如果不指定,則默認填充0>>> a=numpy.array([1,2,3])
    >>> print(a)
    [1 2 3]
    >>> numpy.pad(a,(1,2),mode='edge')
    array([1, 1, 2, 3, 3, 3])
    >>> numpy.pad(a,(1,2),mode='constant')
    array([0, 1, 2, 3, 0, 0])
    >>> numpy.pad(a,(1,2),mode='constant',constant_values=(5,7))
    array([5, 1, 2, 3, 7, 7])
    
    

沿着數組中所指定的軸向,調用處理函數,並將每次調用的返回值重新組織成數組返回。

wdays, opening_prices, highest_prices, \
    lowest_prices, closing_prices = np.loadtxt(
        '../data/aapl.csv',
        delimiter=',', usecols=(1, 3, 4, 5, 6),
        unpack=True, converters={1: dmy2wday})

first_mon = np.where(wdays==0)[0][0]
last_fri = np.where(wdays==4)[0][-1]

wdays = wdays[first_mon:last_fri+1]
indices = np.arange(first_mon, last_fri+1)

#把週一至週五每天的indices值統計爲5個數組
mon_indices = indices[wdays==0]
tue_indices = indices[wdays==1]
wen_indices = indices[wdays==2]
thu_indices = indices[wdays==3]
fri_indices = indices[wdays==4]
max_len = np.max((mon_indices.size, tue_indices.size, wen_indices.size, thu_indices.size, fri_indices.size))
mon_indices = np.pad(mon_indices, pad_width=(0, max_len-mon_indices.size), mode='constant', constant_values=-1)
indices = np.vstack((mon_indices,tue_indices,wen_indices,thu_indices,fri_indices))

# numpy將會把每一行的indices傳入summary函數執行業務
def summary(indices):
    indices = indices[indices!=-1]
    opening_price = opening_prices[indices[0]]
    highest_price = highest_prices[indices].max()
    lowest_price = lowest_prices[indices].min()
    closing_price = closing_prices[indices[-1]]
    return opening_price, highest_price, lowest_price, closing_price
	
r = np.apply_along_axis(summary, 1, indices)
print(r)

np.savetxt('../../data/summary.csv', summaries, delimiter=',', fmt='%g')


移動均線

收盤價5日均線:從第五天開始,每天計算最近五天的收盤價的平均值所構成的一條線。

移動均線算法:

(a+b+c+d+e)/5
(b+c+d+e+f)/5
(c+d+e+f+g)/5
...
(f+g+h+i+j)/5

在K線圖中繪製5日均線圖

import datetime as dt
import numpy as np
import matplotlib.pyplot as mp
import matplotlib.dates as md

def dmy2ymd(dmy):
    dmy = str(dmy, encoding='utf-8')
    date = dt.datetime.strptime(dmy, '%d-%m-%Y').date()
    ymd = date.strftime('%Y-%m-%d')
    return ymd

dates, closing_prices = np.loadtxt('../data/aapl.csv', delimiter=',',
    usecols=(1, 6), unpack=True, dtype='M8[D], f8', converters={1: dmy2ymd})
sma51 = np.zeros(closing_prices.size - 4)
for i in range(sma51.size):
    sma51[i] = closing_prices[i:i + 5].mean()
# 開始繪製5日均線
mp.figure('Simple Moving Average', facecolor='lightgray')
mp.title('Simple Moving Average', fontsize=20)
mp.xlabel('Date', fontsize=14)
mp.ylabel('Price', fontsize=14)
ax = mp.gca()
# 設置水平座標每個星期一爲主刻度
ax.xaxis.set_major_locator(md.WeekdayLocator( byweekday=md.MO))
# 設置水平座標每一天爲次刻度
ax.xaxis.set_minor_locator(md.DayLocator())
# 設置水平座標主刻度標籤格式
ax.xaxis.set_major_formatter(md.DateFormatter('%d %b %Y'))
mp.tick_params(labelsize=10)
mp.grid(linestyle=':')
dates = dates.astype(md.datetime.datetime)
mp.plot(dates, closing_prices, c='lightgray', label='Closing Price')
mp.plot(dates[4:], sma51, c='orangered', label='SMA-5(1)')
mp.legend()
mp.gcf().autofmt_xdate()
mp.show()

卷積

先理解卷積運算的過程:

a = [1, 2, 3, 4, 5]	# 源數組
b = [8, 7, 6]		# 卷積核(運算時相反,先算8)(編程問題)
使用b作爲卷積核,對a數組執行卷積運算
# 對應元素相乘後相加
				44	65	86			有效卷積(valid)
			23	44	65	86	59		同維卷積(same)
		8	23	44	65	86	59	30	完全卷積(full)
0   0   1   2   3   4   5   0   0
6   7   8
	6   7   8
		6   7   8
			6   7   8
				6   7   8
					6   7   8
						6   7   8

c = numpy.convolve(a, b, 卷積類型)

>>> import numpy
>>> a=[1,2,3,4,5]
>>> b=[8,7,6]
>>> c=numpy.convolve(a,b,'valid')	# 有效卷積
>>> print(c)
[44 65 86]
>>> c=numpy.convolve(a,b,'same')	# 同維卷積
>>> print(c)
[23 44 65 86 59]
>>> c=numpy.convolve(a,b,'full')	# 完全卷積
>>> print(c)
[ 8 23 44 65 86 59 30]

5日移動均線序列可以直接使用卷積實現

a = [a, b, c, d, e, f, g, h, i, j] 
b = [1/5, 1/5, 1/5, 1/5, 1/5]

使用卷積函數numpy.convolve(a, b, 卷積類型)實現5日均線

sma52 = np.convolve( closing_prices, np.ones(5) / 5, 'valid')
mp.plot(dates[4:], sma52, c='limegreen', alpha=0.5,
        linewidth=6, label='SMA-5(2)')

使用卷積函數numpy.convolve(a, b, 卷積類型)實現10日均線

sma10 = np.convolve(closing_prices, np.ones(10) / 10, 'valid')
mp.plot(dates[9:], sma10, c='dodgerblue', label='SMA-10')

使用卷積函數numpy.convolve(a, b, 卷積類型)實現加權5日均線

weights = np.exp(np.linspace(-1, 0, 5))
weights /= weights.sum()
ema5 = np.convolve(closing_prices, weights[::-1], 'valid')
mp.plot(dates[4:], sma52, c='limegreen', alpha=0.5,
        linewidth=6, label='SMA-5')

布林帶

布林帶由三條線組成:

中軌:移動平均線

上軌:中軌+2x5日收盤價標準差 (頂部的壓力)

下軌:中軌-2x5日收盤價標準差 (底部的支撐力)

布林帶收窄代表穩定的趨勢,布林帶張開代表有較大的波動空間的趨勢。

繪製5日均線的布林帶

weights = np.exp(np.linspace(-1, 0, 5))
weights /= weights.sum()
em5 = np.convolve(closing_prices, weights[::-1], 'valid')
stds = np.zeros(em5.size)
for i in range(stds.size):
    stds[i] = closing_prices[i:i + 5].std()
stds *= 2
lowers = medios - stds
uppers = medios + stds

mp.plot(dates, closing_prices, c='lightgray', label='Closing Price')
mp.plot(dates[4:], medios, c='dodgerblue', label='Medio')
mp.plot(dates[4:], lowers, c='limegreen', label='Lower')
mp.plot(dates[4:], uppers, c='orangered', label='Upper')

線性模型

什麼是線性關係?

x=1y=60x=2y=65x=3y=70x=4y=75x=5y=??? x=1 \quad \rarr \quad y=60 \\ x=2 \quad \rarr \quad y=65 \\ x=3 \quad \rarr \quad y=70 \\ x=4 \quad \rarr \quad y=75 \\ x=5 \quad \rarr \quad y= ??? \\

線性預測

假設一組數據符合一種線型規律,那麼就可以預測未來將會出現的數據。

a	b	c	d	e	f	?

{aw0+bw1+cw2=dbw0+cw1+dw2=ecw0+dw1+ew2=f \begin{cases} aw_0 + bw_1 + cw_2 = d \\ bw_0 + cw_1 + dw_2 = e \\ cw_0 + dw_1 + ew_2 = f \\ \end{cases}

線型方程組轉換爲矩陣相乘的形式:
[abcbcdcde]×[w0w1w2]=[def]AxB \left[ \begin{array}{ccc} a & b & c\\ b & c & d\\ c & d & e\\ \end{array} \right ] \times \left[ \begin{array}{ccc} w_0\\ w_1\\ w_2\\ \end{array} \right ]= \left[ \begin{array}{ccc} d\\ e\\ f\\ \end{array} \right ] \\ \quad \quad A \quad \quad \quad \quad \quad x\quad \quad \quad \quad B \quad \\

a×w0+b×w1+c×w2=db×w0+c×w1+d×w2=ec×w0+d×w1+e×w2=f a\times w_0+b\times w_1+c\times w_2 =d \\ b\times w_0+c\times w_1+d\times w_2 =e \\ c\times w_0+d\times w_1+e\times w_2 =f

根據線性模型的特點可以通過一組歷史數據求出線性關係係數x, y, z,從而預測d、e、f下的一個數據是多少。

線性預測需要使用歷史數據進行檢驗,讓預測結果可信度更高

案例:使用線性預測,預測下一天的收盤價。

# 整理五元一次方程組    最終獲取一組股票走勢預測值
N = 5
pred_prices = np.zeros(closing_prices.size - 2 * N + 1)
for i in range(pred_prices.size):
    a = np.zeros((N, N))
    for j in range(N):
        a[j, ] = closing_prices[i + j:i + j + N]
    b = closing_prices[i + N:i + N * 2]
    x = np.linalg.lstsq(a, b)[0]
    pred_prices[i] = b.dot(x)
# 由於預測的是下一天的收盤價,所以想日期數組中追加一個元素,爲下一個工作日的日期
dates = dates.astype(md.datetime.datetime)
mp.plot(dates, closing_prices, 'o-', c='lightgray', label='Closing Price')
dates = np.append(dates, dates[-1] + pd.tseries.offsets.BDay())
mp.plot(dates[2 * N:], pred_prices, 'o-',c='orangered', 
        linewidth=3,label='Predicted Price')
mp.legend()
mp.gcf().autofmt_xdate() 
mp.show()

線性擬合

線性擬合可以尋求與一組散點走向趨勢規律相適應的線型表達式方程。

有一組散點描述時間序列下的股價:

[x1, y1]
[x2, y2]
[x3, y3] 
...
[xn, yn]

根據線型 y=kx + b 方程可得:

kx1 + b = y1
kx2 + b = y2
kx3 + b = y3
...
kxn + b = yn

[x11x21x31xn1]×[kb]=[y1y2y3yn]AB \left[ \begin{array}{ccc} x{_1} & 1\\ x{_2} & 1\\ x{_3} & 1 \\ x{_n} & 1 \\ \end{array} \right ] \times \left[ \begin{array}{ccc} k\\ b\\ \end{array} \right ] = \left[ \begin{array}{ccc} y{_1}\\ y{_2}\\ y{_3}\\ y{_n}\\ \end{array} \right ] \\A\quad \quad \quad \quad B \quad \quad \quad

樣本過多,每兩組方程即可求得一組k與b的值。x=numpy.linalg.lstsq(A, B) 可以通過最小二乘法求出所有結果中擬合誤差最小的k與b的值。 k=x[0] , b=x[1]

案例:利用線型擬合畫出股價的趨勢線

  1. 繪製趨勢線(趨勢可以表示爲最高價、最低價、收盤價的均值):
dates, opening_prices, highest_prices, \
    lowest_prices, closing_prices = np.loadtxt('../data/aapl.csv', delimiter=',',
        usecols=(1, 3, 4, 5, 6), unpack=True,dtype='M8[D], f8, f8, f8, f8',
        converters={1: dmy2ymd})
trend_points = (highest_prices + lowest_prices + closing_prices) / 3
days = dates.astype(int)
a = np.column_stack((days, np.ones_like(days)))
x = np.linalg.lstsq(a, trend_points)[0]
trend_line = days * x[0] + x[1]
mp.figure('Trend', facecolor='lightgray')
mp.title('Trend', fontsize=20)
mp.xlabel('Date', fontsize=14)
mp.ylabel('Price', fontsize=14)
ax = mp.gca()
ax.xaxis.set_major_locator(md.WeekdayLocator(byweekday=md.MO))
ax.xaxis.set_minor_locator(md.DayLocator())
ax.xaxis.set_major_formatter(md.DateFormatter('%d %b %Y'))
mp.tick_params(labelsize=10)
mp.grid(linestyle=':')
dates = dates.astype(md.datetime.datetime)
rise = closing_prices - opening_prices >= 0.01
fall = opening_prices - closing_prices >= 0.01
fc = np.zeros(dates.size, dtype='3f4')
ec = np.zeros(dates.size, dtype='3f4')
fc[rise], fc[fall] = (1, 1, 1), (0.85, 0.85, 0.85)
ec[rise], ec[fall] = (0.85, 0.85, 0.85), (0.85, 0.85, 0.85)
mp.bar(dates, highest_prices - lowest_prices, 0,lowest_prices, color=fc, edgecolor=ec)
mp.bar(dates, closing_prices - opening_prices, 0.8,opening_prices, color=fc, 
       edgecolor=ec)
mp.scatter(dates, trend_points, c='dodgerblue',alpha=0.5, s=60, zorder=2)
mp.plot(dates, trend_line, linestyle='o-', c='dodgerblue',linewidth=3, label='Trend')
mp.legend()
mp.gcf().autofmt_xdate()
mp.show()
  1. 繪製頂部壓力線(趨勢線+(最高價 - 最低價))
trend_points = (highest_prices + lowest_prices + closing_prices) / 3
spreads = highest_prices - lowest_prices
resistance_points = trend_points + spreads
days = dates.astype(int)
x = np.linalg.lstsq(a, resistance_points)[0]
resistance_line = days * x[0] + x[1]
mp.scatter(dates, resistance_points, c='orangered', alpha=0.5, s=60, zorder=2)
mp.plot(dates, resistance_line, c='orangered', linewidth=3, label='Resistance')
  1. 繪製底部支撐線(趨勢線-(最高價 - 最低價))
trend_points = (highest_prices + lowest_prices + closing_prices) / 3
spreads = highest_prices - lowest_prices
support_points = trend_points - spreads
days = dates.astype(int)
x = np.linalg.lstsq(a, support_points)[0]
support_line = days * x[0] + x[1]
mp.scatter(dates, support_points, c='limegreen', alpha=0.5, s=60, zorder=2)
mp.plot(dates, support_line, c='limegreen', linewidth=3, label='Support')

協方差、相關矩陣、相關係數

通過兩組統計數據計算而得的協方差可以評估這兩組統計數據的相似程度。

樣本

A = [a1, a2, ..., an]
B = [b1, b2, ..., bn]

平均值

ave_a = (a1 + a2 +...+ an)/n
ave_b = (b1 + b2 +...+ bn)/m

離差(用樣本中的每一個元素減去平均數,求得數據的誤差程度):

dev_a = [a1, a2, ..., an] - ave_a
dev_b = [b1, b2, ..., bn] - ave_b

協方差(離差相乘後求均值)

協方差可以簡單反映兩組統計樣本的相關性,值爲正,則爲正相關;值爲負,則爲負相關,絕對值越大相關性越強。

cov_ab = ave(dev_a * dev_b)
cov_ba = ave(dev_b * dev_a)

案例:計算兩組數據的協方差,並繪圖觀察。

>>> import numpy
>>> import matplotlib.pyplot as mp

>>> a=numpy.random.randint(1,30,10)
>>> print(a)
[ 5 12 25  9 19 21 13 14 27 21]
>>> b=numpy.random.randint(1,30,10)
>>> print(b)
[19  3 28  5 16  1 16  2  3  6]

#平均值
>>> ave_a=numpy.mean(a)
>>> print(ave_a)
16.6
>>> ave_b=numpy.mean(b)
>>> print(ave_b)
9.9

#離差
>>> dev_a=a-ave_a
>>> print(dev_a)
[-11.6  -4.6   8.4  -7.6   2.4   4.4  -3.6  -2.6  10.4   4.4]
>>> dev_b=b-ave_b
>>> print(dev_b)
[ 9.1 -6.9 18.1 -4.9  6.1 -8.9  6.1 -7.9 -6.9 -3.9]

#協方差
>>> cov_ab=numpy.mean(dev_a*dev_b)
>>> print(cov_ab)
0.060000000000002274
>>> cov_ba=numpy.mean(dev_b*dev_a)
>>> print(cov_ba)
0.060000000000002274

>>> print('a與b樣本方差:', numpy.sum(dev_a**2)/(len(dev_a)-1), numpy.sum(dev_b**2)/(len(dev_b)-1))
a與b樣本方差: 50.71111111111112 84.54444444444447

#繪圖,查看兩條圖線的相關性
mp.figure('COV LINES', facecolor='lightgray')
mp.title('COV LINES', fontsize=16)
mp.xlabel('x', fontsize=14)
mp.ylabel('y', fontsize=14)
x = np.arange(0, 10)
#a,b兩條線
mp.plot(x, a, color='dodgerblue', label='Line1')
mp.plot(x, b, color='limegreen', label='Line2')
#a,b兩條線的平均線
mp.plot([0, 9], [ave_a, ave_a], color='dodgerblue', linestyle='--', alpha=0.7, linewidth=3)
mp.plot([0, 9], [ave_b, ave_b], color='limegreen', linestyle='--', alpha=0.7, linewidth=3)

mp.grid(linestyle='--', alpha=0.5)
mp.legend()
mp.tight_layout()
mp.show()

相關係數

協方差除以兩組統計樣本標準差的乘積是一個[-1, 1]之間的數。該結果稱爲統計樣本的相關係數。

# a組樣本 與 b組樣本做對照後的相關係數
cov_ab/(std_a * std_b)
# b組樣本 與 a組樣本做對照後的相關係數
cov_ba/(std_b * std_a)
# a樣本與a樣本作對照   b樣本與b樣本做對照   二者必然相等
cov_ab/(std_a * std_b)=cov_ba/(std_b * std_a)

通過相關係數可以分析兩組數據的相關性:

若相關係數越接近於0,越表示兩組樣本越不相關。
若相關係數越接近於1,越表示兩組樣本正相關。
若相關係數越接近於-1,越表示兩組樣本負相關。

案例:輸出案例中兩組數據的相關係數。

>>> print('相關係數:', cov_ab/(numpy.std(a)*numpy.std(b)), cov_ba/(numpy.std(a)*numpy.std(b)))
相關係數: 0.001018156739784947 0.001018156739784947

相關矩陣

[var_astd_a×std_acov_abstd_a×std_bcov_bastd_b×std_avar_bstd_b×std_b] \left[ \begin{array}{c} \frac{var\_a}{std\_a \times std\_a} & \frac{cov\_ab}{std\_a \times std\_b} \\ \frac{cov\_ba}{std\_b \times std\_a} & \frac{var\_b}{std\_b \times std\_b}\\ \end{array} \right ]
矩陣正對角線上的值都爲1。(同組樣本自己相比絕對正相關)
[1cov_abstd_a×std_bcov_bastd_b×std_a1] \left[ \begin{array}{ccc} 1 & \frac{cov\_ab}{std\_a \times std\_b} \\ \frac{cov\_ba}{std\_b \times std\_a} & 1\\ \end{array} \right ]

numpy提供了求得相關矩陣的API:

# 相關矩陣
# [[a與a的相關係數,a與b的相關係數],
#  [b與a的相關係數,b與b的相關係數]]
>>> numpy.corrcoef(a,b)
array([[1.        , 0.00101816],
       [0.00101816, 1.        ]])

# 相關矩陣的分子矩陣 
# [[a方差,ab協方差], 
#  [ba協方差, b方差]]
>>> numpy.cov(a,b)
array([[5.07111111e+01, 6.66666667e-02],
       [6.66666667e-02, 8.45444444e+01]])	

多項式擬合

多項式的一般形式:
y=p0xn+p1xn1+p2xn2+p3xn3+...+pn y=p_{0}x^n + p_{1}x^{n-1} + p_{2}x^{n-2} + p_{3}x^{n-3} +...+p_{n}

多項式擬合的目的是爲了找到一組 p0,p1,...,pnp_0, p_1, ..., p_n,使得擬合方程儘可能的與實際樣本數據相符合。

假設擬合得到的多項式如下:
f(x)=p0xn+p1xn1+p2xn2+p3xn3+...+pn f(x)=p_{0}x^n + p_{1}x^{n-1} + p_{2}x^{n-2} + p_{3}x^{n-3} +...+p_{n}
則擬合函數與真實結果的差方如下
loss=(y1f(x1))2+(y2f(x2))2+...+(ynf(xn))2 loss = (y_1-f(x_1))^2 + (y_2-f(x_2))^2 + ... + (y_n-f(x_n))^2

那麼多項式擬合的過程即爲求取一組p0,p1,...,pnp_0, p_1, ..., p_n, 使得loss的值最小。

多項式擬合相關API:

根據一組樣本,並給出最高次冪,求出擬合係數
numpy.polyfit(X, Y, 最高次冪)->P


>>> X=numpy.array([1,2,3,4,5])
>>> Y=numpy.array([5,4,3,2,1])
>>> P=numpy.polyfit(X,Y,4)
>>> print(P)
[ 5.22933252e-17 -8.13886356e-16  3.62812466e-15 -1.00000000e+00 6.00000000e+00]
		p0				p1					p2				p3				p4

多項式運算相關API:

根據擬合係數與自變量求出擬合值, 由此可得擬合曲線座標樣本數據 [X, Y']
numpy.polyval(P, X)->Y'
>>> numpy.polyval(P,[1.2,2.5])
array([4.8, 3.5])

多項式函數求導,根據擬合係數求出多項式函數導函數的係數
numpy.polyder(P)->Q 
>>> print(Q)
[ 2.09173301e-16 -2.44165907e-15  7.25624932e-15 -1.00000000e+00]

已知多項式係數Q 求多項式函數的根(與x軸交點的橫座標)
xs = numpy.roots(Q)
>>> xs=numpy.roots(Q)
>>> print(xs)
[168464.32557766+0.j        -84226.32633872+145891.01586885j   -84226.32633872-145891.01586885j]
>>> len(xs)
3

兩個多項式函數的差函數的係數(可以通過差函數的根求取兩個曲線的交點)
Q = numpy.polysub(P1, P2)

案例:求多項式 y = 4x3 + 3x2 - 1000x + 1曲線拐點的座標。

'''
1. 求出多項式的導函數
2. 求出導函數的根,若導函數的根爲實數,則該點則爲曲線拐點。
'''
import numpy as np
import matplotlib.pyplot as mp

x = np.linspace(-20, 20, 1000)
y = 4*x**3 + 3*x**2  - 1000*x + 1
Q = np.polyder([4,3,-1000,1])
xs = np.roots(Q)
ys =  4*xs**3 + 3*xs**2  - 1000*xs + 1
mp.plot(x, y)
mp.scatter(xs, ys, s=80, c='orangered')
mp.show()

案例:使用多項式函數擬合兩隻股票bhp、vale的差價函數:

'''
1. 計算兩隻股票的差價
2. 利用多項式擬合求出與兩隻股票差價相近的多項式係數,最高次爲4
3. 把該曲線的拐點都標出來。
'''
dates, bhp_closing_prices = np.loadtxt('../../data/bhp.csv', 
                                       delimiter=',',usecols=(1, 6), unpack=True, 
                                       dtype='M8[D], f8', conv erters={1: dmy2ymd})
vale_closing_prices = np.loa dtxt('../../data/vale.csv', delimiter=',',
                                 usecols=(6), unpack=True)
diff_closing_prices = bhp_closing_prices - vale_closing_prices
days = dates.astype(int)
p = np.polyfit(days, diff_closing_prices, 4)
poly_closing_prices = np.polyval(p, days)
q = np.polyder(p)
roots_x = np.roots(q)
roots_y = np.polyval(p, roots_x)
mp.figure('Polynomial Fitting', facecolor='lightgray')
mp.title('Polynomial Fitting', fontsize=20)
mp.xlabel('Date', fontsize=14)
mp.ylabel('Difference Price', fontsize=14)
ax = mp.gca()
ax.xaxis.set_major_locator(md.WeekdayLocator(byweekday=md.MO))
ax.xaxis.set_minor_locator(md.DayLocator())
ax.xaxis.set_major_formatter(md.DateFormatter('%d %b %Y'))
mp.tick_params(labelsize=10)
mp.grid(linestyle=':')
dates = dates.astype(md.datetime.datetime)
mp.plot(dates, poly_closing_prices, c='limegreen',
        linewidth=3, label='Polynomial Fitting')
mp.scatter(dates, diff_closing_prices, c='dodgerblue',
           alpha=0.5, s=60, label='Difference Price')
roots_x = roots_x.astype(int).astype('M8[D]').astype(
    		md.datetime.datetime)
mp.scatter(roots_x, roots_y, marker='^', s=80,
           c='orangered', label='Peek', zorder=4)
mp.legend()
mp.gcf().autofmt_xdate()
mp.show()

數據平滑

數據的平滑處理通常包含有降噪、擬合等操作。降噪的功能意在去除額外的影響因素,擬合的目的意在數學模型化,可以通過更多的數學方法識別曲線特徵。

案例:繪製兩隻股票收益率曲線。收益率 =(後一天收盤價-前一天收盤價) / 前一天收盤價

  1. 使用卷積完成數據降噪。
dates, bhp_closing_prices = np.loadtxt( '../data/bhp.csv', delimiter=',', usecols=(1,6), dtype='M8[D], f8',converters={1:dmy2ymd}, unpack=True)
vale_closing_prices = np.loadtxt( '../data/vale.csv', delimiter=',', usecols=(6), dtype='f8',converters={1:dmy2ymd}, unpack=True)

bhp_returns = np.diff(bhp_closing_prices) / bhp_closing_prices[:-1]
vale_returns = np.diff(vale_closing_prices) / vale_closing_prices[:-1]
dates = dates[:-1]

#卷積降噪
convolve_core = np.hanning(8)
convolve_core /= convolve_core.sum()
bhp_returns_convolved = np.convolve(bhp_returns, convolve_core, 'valid')
vale_returns_convolved = np.convolve(vale_returns, convolve_core, 'valid')
#繪製這條曲線
mp.figure('BHP VALE RETURNS', facecolor='lightgray')
mp.title('BHP VALE RETURNS', fontsize=20)
mp.xlabel('Date')
mp.ylabel('Price')
ax = mp.gca()
ax.xaxis.set_major_locator(md.WeekdayLocator(byweekday=md.MO))
ax.xaxis.set_minor_locator(md.DayLocator())
ax.xaxis.set_major_formatter(md.DateFormatter('%Y %m %d'))
dates = dates.astype('M8[D]')
#繪製收益線
mp.plot(dates, bhp_returns, color='dodgerblue', linestyle='--', label='bhp_returns', alpha=0.3)
mp.plot(dates, vale_returns, color='orangered', linestyle='--', label='vale_returns', alpha=0.3)
#繪製卷積降噪線
mp.plot(dates[7:], bhp_returns_convolved, color='dodgerblue', label='bhp_returns_convolved', alpha=0.5)
mp.plot(dates[7:], vale_returns_convolved, color='orangered', label='vale_returns_convolved', alpha=0.5)

mp.show()


  1. 對處理過的股票收益率做多項式擬合。
#擬合這兩條曲線,獲取兩組多項式係數
dates = dates.astype(int)
bhp_p = np.polyfit(dates[7:], bhp_returns_convolved, 3)
bhp_polyfit_y = np.polyval(bhp_p, dates[7:])
vale_p = np.polyfit(dates[7:], vale_returns_convolved, 3)
vale_polyfit_y = np.polyval(vale_p, dates[7:])
#繪製擬合線
mp.plot(dates[7:], bhp_polyfit_y, color='dodgerblue', label='bhp_returns_polyfit')
mp.plot(dates[7:], vale_polyfit_y, color='orangered', label='vale_returns_polyfit')

  1. 通過獲取兩個函數的焦點可以分析兩隻股票的投資收益比。
#求兩條曲線的交點  f(bhp) = f(vale)的根
sub_p = np.polysub(bhp_p, vale_p)
roots_x = np.roots(sub_p)	# 讓f(bhp) - f(vale) = 0  函數的兩個根既是兩個函數的焦點
roots_x = roots_x.compress( (dates[0] <= roots_x) & (roots_x <= dates[-1]))
roots_y = np.polyval(bhp_p, roots_x)
#繪製這些點
mp.scatter(roots_x, roots_y, marker='D', color='green', s=60, zorder=3)

符號數組

sign函數可以把樣本數組的變成對應的符號數組,正數變爲1,負數變爲-1,0則變爲0。

ary = np.sign(源數組)

淨額成交量(OBV)

成交量可以反映市場對某支股票的人氣,而成交量是一隻股票上漲的能量。一支股票的上漲往往需要較大的成交量。而下跌時則不然。

若相比上一天的收盤價上漲,則爲正成交量;若相比上一天的收盤價下跌,則爲負成交量。

繪製OBV柱狀圖

dates, closing_prices, volumes = np.loadtxt(
    '../../data/bhp.csv', delimiter=',',
    usecols=(1, 6, 7), unpack=True,
    dtype='M8[D], f8, f8', converters={1: dmy2ymd})
diff_closing_prices = np.diff(closing_prices)
sign_closing_prices = np.sign(diff_closing_prices)
obvs = volumes[1:] * sign_closing_prices
mp.figure('On-Balance Volume', facecolor='lightgray')
mp.title('On-Balance Volume', fontsize=20)
mp.xlabel('Date', fontsize=14)
mp.ylabel('OBV', fontsize=14)
ax = mp.gca()
ax.xaxis.set_major_locator(md.WeekdayLocator(byweekday=md.MO))
ax.xaxis.set_minor_locator(md.DayLocator())
ax.xaxis.set_major_formatter(md.DateFormatter('%d %b %Y'))
mp.tick_params(labelsize=10)
mp.grid(axis='y', linestyle=':')
dates = dates[1:].astype(md.datetime.datetime)
mp.bar(dates, obvs, 1.0, color='dodgerblue',
       edgecolor='white', label='OBV')
mp.legend()
mp.gcf().autofmt_xdate()
mp.show()

數組處理函數

ary = np.piecewise(源數組, 條件序列, 取值序列)

針對源數組中的每一個元素,檢測其是否符合條件序列中的每一個條件,符合哪個條件就用取值系列中與之對應的值,表示該元素,放到目標 數組中返回。

條件序列: [a < 0, a == 0, a > 0]

取值序列: [-1, 0, 1]

a = np.array([70, 80, 60, 30, 40])
d = np.piecewise(
    a, 
    [a < 60, a == 60, a > 60],
    [-1, 0, 1])
# d = [ 1  1  0 -1 -1]

矢量化

矢量化指的是用數組代替標量來操作數組裏的每個元素。

numpy提供了vectorize函數,可以把處理標量的函數矢量化,返回的函數可以直接處理ndarray數組。

import math as m
import numpy as np

def foo(x, y):
    return m.sqrt(x**2 + y**2)

x, y = 1, 4
print(foo(x, y))
X, Y = np.array([1, 2, 3]), np.array([4, 5, 6])
vectorized_foo = np.vectorize(foo)
print(vectorized_foo(X, Y))
print(np.vectorize(foo)(X, Y))

numpy還提供了frompyfuc函數,也可以完成與vectorize相同的功能:

# 把foo轉換成矢量函數,該矢量函數接收2個參數,返回一個結果 
fun = np.frompyfunc(foo, 2, 1)
fun(X, Y)

案例:定義一種買進賣出策略,通過歷史數據判斷這種策略是否值得實施。

dates, opening_prices, highest_prices, \
    lowest_prices, closing_prices = np.loadtxt(
        '../../data/bhp.csv', delimiter=',',
        usecols=(1, 3, 4, 5, 6), unpack=True,
        dtype='M8[D], f8, f8, f8, f8',
        converters={1: dmy2ymd})
    
# 定義一種投資策略
def profit(opening_price, highest_price,
           lowest_price, closing_price):
    buying_price = opening_price * 0.99
    if lowest_price <= buying_price <= highest_price:
        return (closing_price - buying_price) * \
            100 / buying_price
    return np.nan  # 無效值

# 矢量化投資函數
profits = np.vectorize(profit)(opening_prices, 
       highest_prices, lowest_prices, closing_prices)
nan = np.isnan(profits)
dates, profits = dates[~nan], profits[~nan]
gain_dates, gain_profits = dates[profits > 0], profits[profits > 0]
loss_dates, loss_profits = dates[profits < 0], profits[profits < 0]
mp.figure('Trading Simulation', facecolor='lightgray')
mp.title('Trading Simulation', fontsize=20)
mp.xlabel('Date', fontsize=14)
mp.ylabel('Profit', fontsize=14)
ax = mp.gca()
ax.xaxis.set_major_locator(md.WeekdayLocator(byweekday=md.MO))
ax.xaxis.set_minor_locator(md.DayLocator())
ax.xaxis.set_major_formatter(md.DateFormatter('%d %b %Y'))
mp.tick_params(labelsize=10)
mp.grid(linestyle=':')
if dates.size > 0:
    dates = dates.astype(md.datetime.datetime)
    mp.plot(dates, profits, c='gray',
            label='Profit')
    mp.axhline(y=profits.mean(), linestyle='--',
               color='gray')
if gain_dates.size > 0:
    gain_dates = gain_dates.astype(md.datetime.datetime)
    mp.plot(gain_dates, gain_profits, 'o',
            c='orangered', label='Gain Profit')
    mp.axhline(y=gain_profits.mean(), linestyle='--',
               color='orangered')
if loss_dates.size > 0:
    loss_dates = loss_dates.astype(md.datetime.datetime)
    mp.plot(loss_dates, loss_profits, 'o',
            c='limegreen', label='Loss Profit')
    mp.axhline(y=loss_profits.mean(), linestyle='--',
               color='limegreen')
mp.legend()
mp.gcf().autofmt_xdate()
mp.show()

矩陣

矩陣是numpy.matrix類類型的對象,該類繼承自numpy.ndarray,任何針對多維數組的操作,對矩陣同樣有效,但是作爲子類矩陣又結合其自身的特點,做了必要的擴充,比如:乘法計算、求逆等。

矩陣對象的創建

# 如果copy的值爲True(缺省),所得到的矩陣對象與參數中的源容器共享同一份數
# 據,否則,各自擁有獨立的數據拷貝。
numpy.matrix(
    ary,		# 任何可被解釋爲矩陣的二維容器
  	copy=True	# 是否複製數據(缺省值爲True,即複製數據)
)
# 等價於:numpy.matrix(..., copy=False)
# 由該函數創建的矩陣對象與參數中的源容器一定共享數據,無法擁有獨立的數據拷貝
numpy.mat(任何可被解釋爲矩陣的二維容器)

# 該函數可以接受字符串形式的矩陣描述:
# 數據項通過空格分隔,數據行通過分號分隔。例如:'1 2 3; 4 5 6'
numpy.mat(拼塊規則)

矩陣的乘法運算

# 矩陣的乘法:乘積矩陣的第i行第j列的元素等於
# 被乘數矩陣的第i行與乘數矩陣的第j列的點積
#
#           1   2   6
#    X----> 3   5   7
#    |      4   8   9
#    |
# 1  2  6   31  60  74
# 3  5  7   46  87 116
# 4  8  9   64 120 161
e = np.mat('1 2 6; 3 5 7; 4 8 9')
print(e * e)

# 2行3列 x 3行8列  得到 2行8列
2,3  x  3,8  -->2,8

矩陣的逆矩陣

若兩個矩陣A、B滿足:AB = BA = E (E爲單位矩陣),則成爲A、B爲逆矩陣。

e = np.mat('1 2 6; 3 5 7; 4 8 9')
print(e.I)
print(e * e.I)

ndarray提供了方法讓多維數組替代矩陣的運算:

a = np.array([
    [1, 2, 6],
    [3, 5, 7],
    [4, 8, 9]])
# 點乘法求ndarray的點乘結果,與矩陣的乘法運算結果相同
k = a.dot(a)
print(k)
# linalg模塊中的inv方法可以求取a的逆矩陣
l = np.linalg.inv(a)
print(l)

案例:假設一幫孩子和家長出去旅遊,去程坐的是bus,小孩票價爲3元,家長票價爲3.2元,共花了118.4;回程坐的是Train,小孩票價爲3.5元,家長票價爲3.6元,共花了135.2。分別求小孩和家長的人數。使用矩陣求解。

[33.23.53.6]×[xy]=[118.4135.2] \left[ \begin{array}{ccc} 3 & 3.2 \\ 3.5 & 3.6 \\ \end{array} \right] \times \left[ \begin{array}{ccc} x \\ y \\ \end{array} \right] = \left[ \begin{array}{ccc} 118.4 \\ 135.2 \\ \end{array} \right]

import numpy as np

prices = np.mat('3 3.2; 3.5 3.6')
totals = np.mat('118.4; 135.2')

persons = prices.I * totals
print(persons)

把逆矩陣的概念推廣到非方陣,即稱爲廣義逆矩陣

案例:斐波那契數列

1 1 2 3 5 8 13 21 34 …

X      1   1    1   1    1   1
       1   0    1   0    1   0
    --------------------------------
1  1   2   1    3   2    5   3
1  0   1   1    2   1    3   2
 F^1    F^2      F^3 	  F^4  ...  f^n

代碼

import numpy as np
n = 35

# 使用遞歸實現斐波那契數列
def fibo(n):
    return 1 if n < 3 else fibo(n - 1) + fibo(n - 2)
print(fibo(n))

# 使用矩陣實現斐波那契數列
print(int((np.mat('1. 1.; 1. 0.') ** (n - 1))[0, 0]))

通用函數

裁剪、壓縮

數組的裁剪

# 將調用數組中小於和大於下限和上限的元素替換爲下限和上限,返回裁剪後的數組,調
# 用數組保持不變。
ndarray.clip(min=下限, max=上限)

數組的壓縮

# 返回由調用數組中滿足條件的元素組成的新數組。
ndarray.compress(條件)

案例:

from __future__ import unicode_literals
import numpy as np
a = np.array([10, 20, 30, 40, 50])
print(a)
b = a.clip(min=15, max=45)
print(b)
c = a.compress((15 <= a) & (a <= 45))
print(c)

加法與乘法通用函數

np.add(a, a) 					# 兩數組相加
np.add.reduce(a) 				# a數組元素累加和
np.add.accumulate(a) 			# 累加和過程
np.add.outer([10, 20, 30], a)	# 外和
np.prod(a)
np.cumprod(a)
np.outer([10, 20, 30], a)

案例:

a = np.arange(1, 7)
print(a)
b = a + a
print(b)
b = np.add(a, a)
print(b)
c = np.add.reduce(a)
print(c)
d = np.add.accumulate(a)
print(d)
#  +  	 1  2  3  4  5  6   
#	   --------------------
# 10   |11 12 13 14 15 16 |
# 20   |21 22 23 24 25 26 |
# 30   |31 32 33 34 35 36 |
       --------------------
f = np.add.outer([10, 20, 30], a)
print(f)
#  x  	 1  2  3  4  5  6   
#	   -----------------------
# 10   |10 20 30  40  50  60 |
# 20   |20 40 60  80 100 120 |
# 30   |30 60 90 120 150 180 |
       -----------------------
g = np.outer([10, 20, 30], a)
print(g)

除法與取整通用函數

np.divide(a, b) 	# a 真除 b

np.floor(a / b)		# (真除的結果向下取整)
np.ceil(a / b) 		# (真除的結果向上取整)
np.trunc(a / b)		# (真除的結果截斷取整)
np.round(a / b)		# (真除的結果四捨五入取整)

案例:

import numpy as np

a = np.array([20, 20, -20, -20])
b = np.array([3, -3, 6, -6])
# 真除
c = np.true_divide(a, b)
c = np.divide(a, b)
c = a / b
print('array:',c)
# 對ndarray做floor操作
d = np.floor(a / b)
print('floor_divide:',d)
# 對ndarray做ceil操作
e = np.ceil(a / b)
print('ceil ndarray:',e)
# 對ndarray做trunc操作
f = np.trunc(a / b)
print('trunc ndarray:',f)
# 對ndarray做around操作
g = np.around(a / b)
print('around ndarray:',g)

位運算通用函數

位異或:

位異或:
c = a ^ b
c = np.bitwise_xor(a, b)
位與:
e = a & b
e = np.bitwise_and(a, b)
位或:
e = a | b
e = np.bitwise_or(a, b)
位反:
e = ~a
e = np.bitwise_or(a, b)
移位:
<<		__lshift__		left_shift
>>		__rshift__		right_shift


按位異或操作可以很方便的判斷兩個數據是否同號。

0 ^ 0 = 0
0 ^ 1 = 1
1 ^ 0 = 1
1 ^ 1 = 0

a = np.array([0, -1, 2, -3, 4, -5])
b = np.array([0, 1, 2, 3, 4, 5])
print(a, b)
c = a ^ b
# c = a.__xor__(b)
# c = np.bitwise_xor(a, b)
print(np.where(c < 0)[0])

利用位與運算計算某個數字是否是2的冪

#  1 2^0 00001   0 00000
#  2 2^1 00010   1 00001
#  4 2^2 00100   3 00011
#  8 2^3 01000   7 00111
# 16 2^4 10000  15 01111
# ...

d = np.arange(1, 21)
print(d)
e = d & (d - 1)
e = d.__and__(d - 1)
e = np.bitwise_and(d, d - 1)
print(e)

三角函數通用函數

numpy.sin()

合成方波

一個方波由如下參數的正弦波疊加而成:
y=4π×sin(x)y=4π3×sin(3x)......y=4π2n1×sin((2n1)x) y = 4\pi \times sin(x) \\ y = \frac{4\pi}{3} \times sin(3x) \\ ...\\ ...\\ y = \frac{4\pi}{2n-1} \times sin((2n-1)x)
曲線疊加的越多,越接近方波。所以可以設計一個函數,接收曲線的數量n作爲參數,返回一個矢量函數,該函數可以接收x座標數組,返回n個正弦波疊加得到的y座標數組。

x = np.linspace(-2*np.pi, 2*np.pi, 1000)
y = np.zeros(1000)
n = 1000
for i in range(1, n+1):
	y += 4 / ((2 * i - 1) * np.pi) * np.sin((2 * i - 1) * x)
mp.plot(x, y, label='n=1000')
mp.legend()
mp.show()

特徵值和特徵向量

對於n階方陣A,如果存在數a和非零n維列向量x,使得Ax=ax,則稱a是矩陣A的一個特徵值,x是矩陣A屬於特徵值a的特徵向量

#已知n階方陣A, 求特徵值與特徵數組
# eigvals: 特徵值數組
# eigvecs: 特徵向量數組 
eigvals, eigvecs = np.linalg.eig(A)
#已知特徵值與特徵向量,求方陣
S = np.mat(eigvecs) * np.mat(np.diag(eigvals)) * np.mat(eigvecs逆) 

案例:

import numpy as np
A = np.mat('3 -2; 1 0')
print(A)
eigvals, eigvecs = np.linalg.eig(A)
print(eigvals)
print(eigvecs)
print(A * eigvecs[:, 0])	# 方陣*特徵向量
print(eigvals[0] * eigvecs[:, 0])	#特徵值*特徵向量
S = np.mat(eigvecs) * np.mat(np.diag(eigvals)) * np.mat(eigvecs.I)

案例:讀取圖片的亮度矩陣,提取特徵值與特徵向量,保留部分特徵值,重新生成新的亮度矩陣,繪製圖片。

'''
特徵值與特徵向量
'''
import numpy as np
import scipy.misc as sm
import matplotlib.pyplot as mp


original = sm.imread('../data/lily.jpg', True)
#提取特徵值
eigvals, eigvecs = np.linalg.eig(original)
eigvals[50:] = 0
print(np.diag(eigvals).shape)
original2 = np.mat(eigvecs) * np.mat(np.diag(eigvals)) * np.mat(eigvecs).I
mp.figure("Lily Features")
mp.subplot(121)
mp.xticks([])
mp.yticks([])
mp.imshow(original, cmap='gray')

mp.subplot(122)
mp.xticks([])
mp.yticks([])
mp.imshow(original2, cmap='gray')
mp.tight_layout()
mp.show()

奇異值分解

有一個矩陣M,可以分解爲3個矩陣U、S、V,使得U x S x V等於M。U與V都是正交矩陣(乘以自身的轉置矩陣結果爲單位矩陣)。那麼S矩陣主對角線上的元素稱爲矩陣M的奇異值,其它元素均爲0。

import numpy as np
M = np.mat('4 11 14; 8 7 -2')
print(M)
U, sv, V = np.linalg.svd(M, full_matrices=False)
print(U * U.T)
print(V * V.T)
print(sv)
S = np.diag(sv)
print(S)
print(U * S * V)

案例:讀取圖片的亮度矩陣,提取奇異值與兩個正交矩陣,保留部分奇異值,重新生成新的亮度矩陣,繪製圖片。

original = sm.imread('../data/lily.jpg', True)
#提取奇異值  sv 	
U, sv, V = np.linalg.svd(original)
print(U.shape, sv.shape, V.shape)
sv[50:] = 0
original2 = np.mat(U) * np.mat(np.diag(sv)) * np.mat(V)
mp.figure("Lily Features")
mp.subplot(221)
mp.xticks([])
mp.yticks([])
mp.imshow(original, cmap='gray')

mp.subplot(222)
mp.xticks([])
mp.yticks([])
mp.imshow(original2, cmap='gray')
mp.tight_layout()

快速傅里葉變換(fft)

什麼是傅里葉變換?

法國科學家傅里葉提出傅里葉定理,任何一條週期曲線,無論多麼跳躍或不規則,都能表示成一組光滑正弦曲線疊加之和。傅里葉變換即是將不規則曲線拆解爲一組光滑正弦曲線的過程。

傅里葉變換的目的是可將時域(即時間域)上的信號轉變爲頻域(即頻率域)上的信號,隨着域的不同,對同一個事物的瞭解角度也就隨之改變,因此在時域中某些不好處理的地方,在頻域就可以較爲簡單的處理。這就可以大量減少處理信號存儲量。

例如:彈鋼琴

假設有一時間域函數:y = f(x),根據傅里葉的理論它可以被分解爲一系列正弦函數的疊加,他們的振幅A,頻率ω或初相位φ不同:
y=A1sin(ω1x+ϕ1)+A2sin(ω2x+ϕ2)+A2sin(ω2x+ϕ2)+R y = A_1sin(\omega_1x+\phi_1) + A_2sin(\omega_2x+\phi_2) + A_2sin(\omega_2x+\phi_2) + R

所以傅里葉變換可以把一個比較複雜的函數轉換爲多個簡單函數的疊加,看問題的角度也從時間域轉到了頻率域,有些的問題處理起來就會比較簡單。

傅里葉變換相關函數

導入快速傅里葉變換所需模塊

import numpy.fft as nf

通過採樣數與採樣週期求得傅里葉變換分解所得曲線的頻率序列

freqs = np.fft.fftfreq(採樣數量, 採樣週期)

通過原函數值的序列j經過快速傅里葉變換得到一個複數數組,複數的模代表的是振幅,複數的輻角代表初相位

np.fft.fft(原函數數組) -> 複數數組(表示一組正弦函數)

通過 複數數組 經過逆向傅里葉變換得到合成的函數值數組

np.fft.ifft(複數數組)->原函數值數組

案例:針對方波,繪製時域圖與頻域圖。

import numpy as np
import numpy.fft as nf
import matplotlib.pyplot as mp
times = np.linspace(0, 2 * np.pi, 201)
sigs1 = 4 / (1 * np.pi) * np.sin(1 * times)
sigs2 = 4 / (3 * np.pi) * np.sin(3 * times)
sigs3 = 4 / (5 * np.pi) * np.sin(5 * times)
sigs4 = 4 / (7 * np.pi) * np.sin(7 * times)
sigs5 = 4 / (9 * np.pi) * np.sin(9 * times)
sigs6 = sigs1 + sigs2 + sigs3 + sigs4 + sigs5

mp.subplot(121)
mp.title('Time Domain', fontsize=16)
mp.xlabel('Time', fontsize=12)
mp.ylabel('Signal', fontsize=12)
mp.tick_params(labelsize=10)
mp.grid(linestyle=':')
mp.plot(times, sigs1, label=r'$\omega$='+str(round(1 / (2 * np.pi),3)))
mp.plot(times, sigs2, label=r'$\omega$='+str(round(3 / (2 * np.pi),3)))
mp.plot(times, sigs3, label=r'$\omega$='+str(round(5 / (2 * np.pi),3)))
mp.plot(times, sigs4, label=r'$\omega$='+str(round(7 / (2 * np.pi),3)))
mp.plot(times, sigs5, label=r'$\omega$='+str(round(9 / (2 * np.pi),3)))
mp.plot(times, sigs6, label=r'$\omega$='+str(round(1 / (2 * np.pi),3)))
mp.legend()
mp.show()

案例:針對合成波做快速傅里葉變換,得到一組複數序列;再針對該複數序列做逆向傅里葉變換得到新的合成波並繪製。

ffts = nf.fft(sigs6)
sigs7 = nf.ifft(ffts).real
mp.plot(times, sigs7, label=r'$\omega$='+str(round(1 / (2 * np.pi),3)), alpha=0.5, linewidth=6)

案例:針對合成波做快速傅里葉變換,得到分解波數組的頻率、振幅、初相位數組,並繪製頻域圖像。

# 得到分解波的頻率序列
freqs = nf.fftfreq(times.size, times[1] - times[0])
# 複數的模爲信號的振幅(能量大小)
ffts = nf.fft(sigs6)
pows = np.abs(ffts)

mp.subplot(122)
mp.title('Frequency Domain', fontsize=16)
mp.xlabel('Frequency', fontsize=12)
mp.ylabel('Power', fontsize=12)
mp.tick_params(labelsize=10)
mp.grid(linestyle=':')
mp.plot(freqs[freqs >= 0], pows[freqs >= 0], c='orangered', label='Frequency Spectrum')
mp.legend()
mp.tight_layout()
mp.show()

基於傅里葉變換的頻域濾波

含噪信號是高能信號與低能噪聲疊加的信號,可以通過傅里葉變換的頻域濾波實現降噪。

通過FFT使含噪信號轉換爲含噪頻譜,去除低能噪聲,留下高能頻譜後再通過IFFT留下高能信號。

案例:基於傅里葉變換的頻域濾波爲音頻文件去除噪聲。

  1. 讀取音頻文件,獲取音頻文件基本信息:採樣個數,採樣週期,與每個採樣的聲音信號值。繪製音頻時域的:時間/位移圖像。
import numpy as np
import numpy.fft as nf
import scipy.io.wavfile as wf
import matplotlib.pyplot as mp

sample_rate, noised_sigs = wf.read('../data/noised.wav')
noised_sigs = noised_sigs / 2 ** 15
times = np.arange(len(noised_sigs)) / sample_rate
mp.figure('Filter', facecolor='lightgray')
mp.subplot(221)
mp.title('Time Domain', fontsize=16)
mp.ylabel('Signal', fontsize=12)
mp.tick_params(labelsize=10)
mp.grid(linestyle=':')
mp.plot(times[:178], noised_sigs[:178],c='orangered', label='Noised')
mp.legend()
mp.show()

  1. 基於傅里葉變換,獲取音頻頻域信息,繪製音頻頻域的:頻率/能量圖像。
freqs = nf.fftfreq(times.size, 1 / sample_rate)
noised_ffts = nf.fft(noised_sigs)
noised_pows = np.abs(noised_ffts)
mp.subplot(222)
mp.title('Frequency Domain', fontsize=16)
mp.ylabel('Power', fontsize=12)
mp.tick_params(labelsize=10)
mp.grid(linestyle=':')
mp.semilogy(freqs[freqs >= 0],noised_pows[freqs >= 0], c='limegreen',label='Noised')
mp.legend()

  1. 將低頻噪聲去除後繪製音頻頻域的:頻率/能量圖像。
fund_freq = freqs[noised_pows.argmax()]
noised_indices = np.where(freqs != fund_freq)
filter_ffts = noised_ffts.copy()
filter_ffts[noised_indices] = 0
filter_pows = np.abs(filter_ffts)

mp.subplot(224)
mp.xlabel('Frequency', fontsize=12)
mp.ylabel('Power', fontsize=12)
mp.tick_params(labelsize=10)
mp.grid(linestyle=':')
mp.plot(freqs[freqs >= 0], filter_pows[freqs >= 0],c='dodgerblue', label='Filter')
mp.legend() 

  1. 基於逆向傅里葉變換,生成新的音頻信號,繪製音頻時域的:時間/位移圖像。
filter_sigs = nf.ifft(filter_ffts).real
mp.subplot(223)
mp.xlabel('Time', fontsize=12)
mp.ylabel('Signal', fontsize=12)
mp.tick_params(labelsize=10)
mp.grid(linestyle=':')
mp.plot(times[:178], filter_sigs[:178],c='hotpink', label='Filter')
mp.legend()

  1. 重新生成音頻文件。
wf.write('../../data/filter.wav',sample_rate,(filter_sigs * 2 ** 15).astype(np.int16))

隨機數模塊(random)

生成服從特定統計規律的隨機數序列。

二項分佈(binomial)

二項分佈就是重複n次獨立事件的伯努利試驗。在每次試驗中只有兩種可能的結果,而且兩種結果發生與否互相對立,並且相互獨立,事件發生與否的概率在每一次獨立試驗中都保持不變。

# 產生size個隨機數,每個隨機數來自n次嘗試中的成功次數,其中每次嘗試成功的概率爲p。
np.random.binomial(n, p, size)

二項分佈可以用於求如下場景的概率的近似值:

  1. 某人投籃命中率爲0.3,投10次,進5個球的概率。
sum(np.random.binomial(10, 0.3, 200000) == 5) / 200000

  1. 某人打客服電話,客服接通率是0.6,一共打了3次,都沒人接的概率。
sum(np.random.binomial(3, 0.6, 200000) == 0) / 200000

超幾何分佈(hypergeometric)

# 產生size個隨機數,每個隨機數t爲在總樣本中隨機抽取nsample個樣本後好樣本的個數,總樣本由ngood個好樣本和nbad個壞樣本組成
np.random.hypergeometric(ngood, nbad, nsample, size)

模球遊戲:將25個好球和1個壞球放在一起,每次模3個球,全爲好球加1分,只要摸到了壞球減6分,求100輪的過程中分值的變化。

import numpy as np
import matplotlib.pyplot as mp
outcomes = np.random.hypergeometric(25, 1, 3, 100)
scores = [0]
for outcome in outcomes:
    if outcome == 3:
        scores.append(scores[-1] + 1)
    else:
        scores.append(scores[-1] - 6)
scores = np.array(scores)
mp.figure('Hypergeometric Distribution', facecolor='lightgray')
mp.title('Hypergeometric Distribution', fontsize=20)
mp.xlabel('Round', fontsize=14)
mp.ylabel('Score', fontsize=14)
mp.tick_params(labelsize=12)
mp.grid(linestyle=':')
o, h, l, c = 0, scores.argmax(), scores.argmin(), scores.size-1
if scores[o] < scores[c]:
    color = 'orangered'
elif scores[c] < scores[o]:
    color = 'limegreen'
else:
    color = 'dodgerblue'
mp.plot(scores, c=color, label='Score')
mp.axhline(y=scores[o], linestyle='--',color='deepskyblue', linewidth=1)
mp.axhline(y=scores[h], linestyle='--',color='crimson', linewidth=1)
mp.axhline(y=scores[l], linestyle='--',color='seagreen', linewidth=1)
mp.axhline(y=scores[c], linestyle='--',color='orange', linewidth=1)
mp.legend()
mp.show()

正態分佈(normal)

# 產生size個隨機數,服從標準正態(期望=0, 標準差=1)分佈。
np.random.normal(size)
# 產生size個隨機數,服從正態分佈(期望=1, 標準差=10)。
np.random.normal(loc=1, scale=10, size)

:ex222π 標準正態分佈概率密度: \frac{e^{-\frac{x^2}{2}}}{\sqrt{2\pi}}

案例:生成10000個服從正態分佈的隨機數並繪製隨機值的頻數直方圖。

import numpy as np
import matplotlib.pyplot as mp
samples = np.random.normal(size=10000)
mp.figure('Normal Distribution',facecolor='lightgray')
mp.title('Normal Distribution', fontsize=20)
mp.xlabel('Sample', fontsize=14)
mp.ylabel('Occurrence', fontsize=14)
mp.tick_params(labelsize=12)
mp.grid(axis='y', linestyle=':')
mp.hist(samples, 100, normed=True,
               edgecolor='steelblue',
               facecolor='deepskyblue',
               label='Normal')[1]
mp.legend()
mp.show()


雜項功能

排序

import numpy
sorted_array = numpy.msort(array)[::-1]

聯合間接排序

聯合間接排序支持爲待排序列排序,若待排序列值相同,則利用參考序列作爲參考繼續排序。最終返回排序過後的有序索引序列。

indices = numpy.lexsort((參考序列, 待排序列))

案例:先按價格排序,再按銷售量倒序排列。

import numpy as np
prices = np.array([92,83,71,92,40,12,64])
volumes = np.array([100,251,4,12,709,34,75])
print(volumes)
names = ['Product1','Product2','Product3','Product4','Product5','Product6','Product7']
ind = np.lexsort((volumes*-1, prices)) 
print(ind)
for i in ind:
	print(names[i], end=' ')

複數數組排序

按照實部的升序排列,對於實部相同的元素,參考虛部的升序,直接返回排序後的結果數組。

numpy.sort_complex(複數數組)

插入排序

若有需求需要向有序數組中插入元素,使數組依然有序,numpy提供了searchsorted方法查詢並返回可插入位置數組。

indices = numpy.searchsorted(有序數組, 待插入數據數組)

調用numpy提供了insert方法將待插入元素數組中的元素,按照位置數組中的位置,插入到目標數組中,返回結果數組。

numpy.insert(A, indices, B) # 向A數組中的indices位置插入B數組中的元素

案例:

import numpy as np
#             0  1  2  3  4  5  6
a = np.array([1, 2, 4, 5, 6, 8, 9])
b = np.array([7, 3])
c = np.searchsorted(a, b)
print(c)
d = np.insert(a, c, b)
print(d)


插值

需求:統計各小區彩民買彩票的情況:

彩民數量 彩票購買量
30 100注
40 120注
50 135注
60 155注
45 -
65 170注

scipy提供了常見的插值算法可以通過 一定規律插值器函數。若我們給插值器函數更多的散點x座標序列,該函數將會返回相應的y座標序列。

func = si.interp1d(
    離散水平座標, 
    離散垂直座標,
    kind=插值算法(缺省爲線性插值)
)

案例:

# scipy.interpolate
import scipy.interpolate as si

# 原始數據 11組數據
min_x = -50
max_x = 50
dis_x = np.linspace(min_x, max_x, 11)
dis_y = np.sinc(dis_x)

# 通過一系列的散點設計出符合一定規律插值器函數,使用線性插值(kind缺省值)
linear = si.interp1d(dis_x, dis_y)
lin_x = np.linspace(min_x, max_x, 200)
lin_y = linear(lin_x)

# 三次樣條插值 (CUbic Spline Interpolation) 獲得一條光滑曲線
cubic = si.interp1d(dis_x, dis_y, kind='cubic')
cub_x = np.linspace(min_x, max_x, 200)
cub_y = cubic(cub_x)

積分

直觀地說,對於一個給定的正實值函數,在一個實數區間上的定積分可以理解爲座標平面上由曲線、直線以及軸圍成的曲邊梯形的面積值(一種確定的實數值)。

利用微元法認識什麼是積分。

案例:

  1. 在[-5, 5]區間繪製二次函數y=2x2+3x+4的曲線:
import numpy as np
import matplotlib.pyplot as mp
import matplotlib.patches as mc

def f(x):
    return 2 * x ** 2 + 3 * x + 4

a, b = -5, 5
x1 = np.linspace(a, b, 1001)
y1 = f(x1)
mp.figure('Integral', facecolor='lightgray')
mp.title('Integral', fontsize=20)
mp.xlabel('x', fontsize=14)
mp.ylabel('y', fontsize=14)
mp.tick_params(labelsize=10)
mp.grid(linestyle=':')
mp.plot(x1, y1, c='orangered', linewidth=6,label=r'$y=2x^2+3x+4$', zorder=0)
mp.legend()
mp.show()

  1. 微分法繪製函數在與x軸還有[-5, 5]所組成的閉合區域中的小梯形。
n = 50
x2 = np.linspace(a, b, n + 1)
y2 = f(x2)
area = 0
for i in range(n):
    area += (y2[i] + y2[i + 1]) * (x2[i + 1] - x2[i]) / 2
print(area)
for i in range(n):
    mp.gca().add_patch(mc.Polygon([
        [x2[i], 0], [x2[i], y2[i]],
        [x2[i + 1], y2[i + 1]], [x2[i + 1], 0]],
        fc='deepskyblue', ec='dodgerblue',
        alpha=0.5))

調用scipy.integrate模塊的quad方法計算積分:

import scipy.integrate as si
# 利用quad求積分 給出函數f,積分下限與積分上限[a, b]   返回(積分值,最大誤差)
area = si.quad(f, a, b)[0]
print(area)

圖像

scipy.ndimage中提供了一些簡單的圖像處理,如高斯模糊、任意角度旋轉、邊緣識別等功能。

import numpy as np
import scipy.misc as sm
import scipy.ndimage as sn
import matplotlib.pyplot as mp
#讀取文件
original = sm.imread('../../data/head.jpg', True)
#高斯模糊
median = sn.median_filter(original, 21)
#角度旋轉
rotate = sn.rotate(original, 45)
#邊緣識別
prewitt = sn.prewitt(original)
mp.figure('Image', facecolor='lightgray')
mp.subplot(221)
mp.title('Original', fontsize=16)
mp.axis('off')
mp.imshow(original, cmap='gray')
mp.subplot(222)
mp.title('Median', fontsize=16)
mp.axis('off')
mp.imshow(median, cmap='gray')
mp.subplot(223)
mp.title('Rotate', fontsize=16)
mp.axis('off')
mp.imshow(rotate, cmap='gray')
mp.subplot(224)
mp.title('Prewitt', fontsize=16)
mp.axis('off')
mp.imshow(prewitt, cmap='gray')
mp.tight_layout()
mp.show()

金融相關

import numpy as np
# 終值 = np.fv(利率, 期數, 每期支付, 現值)
# 將1000元以1%的年利率存入銀行5年,每年加存100元,
# 到期後本息合計多少錢?
fv = np.fv(0.01, 5, -100, -1000)
print(round(fv, 2))
# 現值 = np.pv(利率, 期數, 每期支付, 終值)
# 將多少錢以1%的年利率存入銀行5年,每年加存100元,
# 到期後本息合計fv元?
pv = np.pv(0.01, 5, -100, fv)
print(pv)
# 淨現值 = np.npv(利率, 現金流)
# 將1000元以1%的年利率存入銀行5年,每年加存100元,
# 相當於一次性存入多少錢?
npv = np.npv(0.01, [
    -1000, -100, -100, -100, -100, -100])
print(round(npv, 2))
fv = np.fv(0.01, 5, 0, npv)
print(round(fv, 2))
# 內部收益率 = np.irr(現金流)
# 將1000元存入銀行5年,以後逐年提現100元、200元、
# 300元、400元、500元,銀行利率達到多少,可在最後
# 一次提現後償清全部本息,即淨現值爲0元?
irr = np.irr([-1000, 100, 200, 300, 400, 500])
print(round(irr, 2))
npv = np.npv(irr, [-1000, 100, 200, 300, 400, 500])
print(npv)
# 每期支付 = np.pmt(利率, 期數, 現值)
# 以1%的年利率從銀行貸款1000元,分5年還清,
# 平均每年還多少錢?
pmt = np.pmt(0.01, 5, 1000)
print(round(pmt, 2))
# 期數 = np.nper(利率, 每期支付, 現值)
# 以1%的年利率從銀行貸款1000元,平均每年還pmt元,
# 多少年還清?
nper = np.nper(0.01, pmt, 1000)
print(int(nper))
# 利率 = np.rate(期數, 每期支付, 現值, 終值)
# 從銀行貸款1000元,平均每年還pmt元,nper年還清,
# 年利率多少?
rate = np.rate(nper, pmt, 1000, 0)
print(round(rate, 2))

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章