なぜ配列か、そして配列とは何か
Python のリストは何でも入れられるため柔軟ですが、数値には遅く、各要素が別々のオブジェクトです。NumPy は ndarray を加えます。これは、単一の型の数値を順に並べたメモリのかたまりです。ブロック全体にコンパイル済みコードで演算するので、Python のループより通常数十倍速く、メモリもずっと少なくて済みます。pandas、SciPy、scikit-learn など、ほぼすべての数値系ライブラリの土台です。慣習で import numpy as np とインポートします。
import numpy as np
# Create arrays
prices = np.array([184.25, 185.0, 183.2])
print(prices) # [184.25 185. 183.2 ]
print(np.zeros(3)) # [0. 0. 0.]
print(np.arange(0, 10, 3)) # [0 3 6 9]
print(np.linspace(0, 1, 5)) # [0. 0.25 0.5 0.75 1. ]
# An array knows its shape
table = np.array([[1, 2, 3], [4, 5, 6]])
print(table.shape, table.ndim, table.size) # (2, 3) 2 6配列を作る
np.array(リスト) はリスト(2次元ならリストのリスト)を変換します。np.zeros(n) と np.ones(n) は埋められた配列を作り、np.zeros((行, 列)) は形状のタプルを取ります。np.arange(start, stop, step) は range のように働きますが、浮動小数点のステップも使え、stop は含みません。丸め誤差があるので、ちょうど num 個の等間隔の点を返し両端を含む np.linspace(start, stop, num) のほうが適しています。すべての配列に .shape(各次元のサイズ)、.ndim(次元数)、.size(要素の総数)があります。
import numpy as np
# arange with floats can surprise; linspace gives exactly the points you ask for
print(len(np.arange(0, 1, 0.1))) # 10 (1.0 is excluded)
print(len(np.linspace(0, 1, 11))) # 11 (both ends included)
# Shapes and sizes
grid = np.zeros((3, 4))
print(grid.shape, grid.ndim, grid.size) # (3, 4) 2 12
print(np.ones(2) * 5) # [5. 5.]dtype:配列全体で1つの型
すべての配列には dtype があります。int64(整数)、float64(小数)、bool、そして float32 や int8 などの小さい/大きい版です。NumPy はデータから dtype を選びます。整数だけなら int64、小数が1つあれば全体が float64 になり、欠損値の印である np.nan(not-a-number)は浮動小数点なので float64 を強制します。dtype=... で自分で指定するか、新しい配列を返す .astype(...) で変換します。小さい dtype はメモリを節約します(float32 は float64 の半分)が、精度を失います。
import numpy as np
print(np.array([1, 2, 3]).dtype) # int64
print(np.array([1, 2.5, 3]).dtype) # float64 (one decimal upgrades everything)
print(np.array([True, False]).dtype) # bool
print(np.array([1, 2, np.nan])) # [ 1. 2. nan] (nan is a float)
# astype returns a new array; smaller dtypes use less memory
prices = np.zeros(1000)
print(prices.nbytes) # 8000
print(prices.astype(np.float32).nbytes) # 4000
print(np.array(["1.5", "2"], dtype=float)) # [1.5 2. ] (text converted to numbers)型変換の落とし穴:切り捨てとオーバーフロー
.astype(int) で小数を整数にすると、0に向かって切り捨てられます。しかも浮動小数点はほとんどの小数を正確に表せないため、19.99 * 100 は 1998.9999999999998 となり、1998 に切り捨てられて1セント足りなくなります。価格を整数のセントに変換するには、先に丸めます:np.round(prices * 100).astype(np.int64)。整数の dtype には範囲があり、黙って巻き戻ります。int8 は -128 から 127 なので、100 * 2 は -56 になります。お金や個数には int64 を使い、整数の / は常に浮動小数点を返し、// は切り下げることを覚えておきましょう。
import numpy as np
# Truncation: astype(int) cuts the decimals off
print(np.array([1.9, -1.9]).astype(int)) # [ 1 -1]
print(19.99 * 100, int(19.99 * 100)) # 1998.9999999999998 1998
print(np.round(np.array([19.99]) * 100).astype(np.int64)) # [1999]
# Overflow: an int8 wraps around silently
small = np.array([100, 50], dtype=np.int8)
print(small * 2) # [-56 100]
# / always gives floats, // floors
counts = np.array([1, 2, 3])
print(counts / 2, counts // 2) # [0.5 1. 1.5] [0 1 1].shape と .dtype を表示しましょう。float64 のはずが int64 だったり、(5,) のはずが (5, 1) だったりするのが、多くのバグの原因です。配列は Python のループではなく、NumPy の関数(np.linspace、np.asarray、astype)で作りましょう。ラボがループを認めないのはそのためです。np.array・zeros・ones・arange・linspaceで配列を作り、.shape・.ndim・.sizeで説明する- 配列ごとに1つの dtype(
int64・float64・bool)。astypeは新しい配列に変換する astype(int)は切り捨て(先に丸める)。小さい整数 dtype は黙って巻き戻る。nanは浮動小数点を強制する
次は配列のインデックスとスライスです。行・列・ウィンドウ・条件を満たす値を取り出します。
ラボ:採点される内容
make_gridで等間隔の点を並べる25点to_float_arrayで混在した生の値を浮動小数点にそろえる25点describe_arrayで shape・size・dtype を報告する25点to_centsで価格を安全に整数のセントへ変換する25点