# coding: utf-8

# numpyを用いて線形代数を表現する

#見たい処理のコメントを外してコンパイルする

# numpyのインポート
import numpy as np

# 配列の生成（要素がすべて０）
# 2×3の二次元配列を作る
x = 2
y = 3
shapes = x, y
numpy.zeros(shapes, dtype =None)
# >> array([[ 0.,  0.,  0.],
#           [ 0.,  0.,  0.]])
# 2×3×2の三次元配列を作る
shapes = 2,3,2
numpy.zeros(shapes, dtype =None)
# >> array([[[ 0.,  0.],
#            [ 0.,  0.],
#            [ 0.,  0.]],
#
#           [[ 0.,  0.],
#            [ 0.,  0.],
#            [ 0.,  0.]]])

# 配列の要素を指定して生成
# (3,)の配列を作る　※（3,1)や(1,3)ではない
x = np.array([1, 2, 3])
x
# >> [1 2 3]
# (2, 3)の配列
a = np.array([[1, 2, 3],[4, 5, 6]])
a
# >> [[1 2 3],
#     [4 5 6]]
# 配列の形を表示する
a.shape
# >> (2, 3)

# 行列を生成
np.matrix("1, 2; 3, 4")
# >> matrix([[1, 2],
#            [3, 4]])



# 配列の要素へのアクセス
# Pythonの多重リストとは異なり、ひとつの[ ]にすべての添え字を入力できる
a = np.array([[1, 2, 3],[4, 5, 6]])
a[1, 2]
# >> 6
a[0] # 0番目の要素にアクセス（0行目をすべて取り出す）
# >> array([1, 2, 3])
a[:, 0:1] # 2次元配列のまま(2, 1) ・・・行を抜き出す
# >> array([[1],
#           [4]])
a[:,0] # 1次元配列になる(2, )　・・・行を1次元にして抜き出す

# 配列の形を変える
a = np.array([[1, 2, 3],[4, 5, 6]])
a.reshape(1, 6)
# >> array([[1, 2, 3, 4, 5, 6]])　※要素数は一致していなければならない

# 配列を転置する
a = np.array([[1, 2, 3],[4, 5, 6]])
a.T
# >> array([[1, 4],
#           [2, 5],
#           [3, 6]])

# 配列の連結
a = np.array([[1, 2, 3], [4, 5, 6]])
b = np.ones((2,3)) # aと同じサイズの配列を用意
#　行について連結
np.r_[a, b]
# >> array([[ 1.,  2.,  3.],
#           [ 4.,  5.,  6.],
#           [ 1.,  1.,  1.],
#           [ 1.,  1.,  1.]]) ※次元の数は一致していなければならない
# 列について連結
np.c_[a, b]
# >> array([[ 1.,  2.,  3.,  1.,  1.,  1.],
#           [ 4.,  5.,  6.,  1.,  1.,  1.]])

# 配列をランダムに並べ替える
indices = [1, 2, 3]
np.random.shuffle(indices)
# >> [2, 1, 3]


# 計算


# 配列同士の和
# 配列同士の四則演算では、配列のサイズが一致しなくてもいい
a = np.array([[1, 2, 3], [4, 5, 6]])
b = np.ones((2,3))
a + b
# 配列同士の差
a - b
# >> array([[ 2.,  3.,  4.],
#           [ 5.,  6.,  7.]])
# 配列同士の積 ※行列積にはならない
a * b
# 配列同士の商
a / b
# 行列同士の和
a = np.matrix("1 2; 3 4")
b = np.matrix("5 6; 7 8")
a + b
# >> matrix([[ 6,  8],
#            [10, 12]])
# 行列同士の差
a - b
# >> matrix([[-4, -4],
#            [-4, -4]])
# 行列の要素同士の積 (行列積)
a * b
# >> matrix([[19, 22],
#            [43, 50]])
# 行列の要素同士の商
a / b
# >> matrix([[ 0.2       ,  0.33333333],
#            [ 0.42857143,  0.5       ]])
# 逆行列を作る
from numpy import linalg as la
la.inv(a)
# >> matrix([[-2. ,  1. ],
#            [ 1.5, -0.5]])

# 整数同士のドット積（通常の掛け算と同じ）
# 3 * 4
np.dot(3, 4)
# >> 12

# 行列同士のドット積
# (2, 3)・(4, 5)
# (2 * 4)+(3 * 5) = 23
np.dot([2, 3],[4, 5])
# >> 23

# 複素数のドット積
# ベクトルの積を計算するとき、\n
# 内積ではなくベクトルを求めたいとき
# かつ、平面ベクトルの積を空間ベクトルではなく平面ベクトルを求めるとき
# に複素数を使う
# (2j, 3j) * (4j, 5j)
np.dot([2j, 3j], [4j, 5j])
# >> (-23+0j)

# ２次元行列同士のドット積を計算する
a = [[1, 0], [0, 1]]
b = [[4, 1], [2, 2]]
np.dot(a, b)
# >> array([[4, 1], 
#           [2, 2]])

# ベクトルの外積の表現
# 外積が何を意味するのかはわからない
a = [[1, 0], [0, 1]]
b = [[4, 1], [2, 2]]
np.outer(a, b)
# array([[4, 1, 2, 2],
#        [0, 0, 0, 0],
#        [0, 0, 0, 0],
#        [4, 1, 2, 2]])

# 単位行列の作成
# 正方行列かつ、「＼」方向の対角線上だけが１で、他がすべて０の行列のこと
# 引数にN次を指定する
np.identity(3)
# >> array([[ 1.,  0.,  0.],
#           [ 0.,  1.,  0.],
#           [ 0.,  0.,  1.]])

# 固有値、固有ベクトルの計算
# 固有ベクトル：ベクトルの長さだけが変わり、方向だけが変わるベクトルのこと
# 固有値：固有ベクトルに掛ける伸縮率のこと
 # numpy.linalgパッケージに含まれるLA.eig()を使う
from numpy import linalg as LA
# 2次配列[[3, 1],[2, 4]]の固有値、固有ベクトル
matrix1 = np.array([[3, 1],[2, 4]])
w, v = LA.eig(matrix1)
 # w = 固有値, v = 固有ベクトル（正規化済）
w
# >> array([ 2.,  5.])
v
# >> array([[-0.70710678, -0.4472136 ],
#           [ 0.70710678, -0.89442719]])
# 固有値のみを求める場合
# 2 次行列 [[3, 1], [2, 4]] の固有値
matrix1 = np.array([[3, 1],[2, 4]])
LA.eigvals(matrix1)
# >> array([ 2.,  5.])