Python の基礎 (NumPy、pandas)2023年9月10日 | |
はじめにPython を使う醍醐味は、標準外で提供されている各種パッケージにある。ここでは、その中で NumPy (一部 SciPy)、pandas の基礎について述べる。 配列NumPy は科学技術計算用のパッケージで、配列や行列などを提供する。パッケージがインストールされていれば、以下のようにインポートする。 >>> import numpy as np numpy はよく np として使われる。 次のように配列 (ndarray) を定義できる。 >>> a = np.array([1, 2, 3]) >>> a array([1, 2, 3]) >>> type(a) <class 'numpy.ndarray'> これだけだとリストと違いがなさそうに見えるが、すべての要素の型は一致している必要がある。数値計算においては、リストよりも効率的に処理される。 要素の型は、元にしたリストによる。
>>> a = np.array([1, 2, 3])
>>> a.dtype
dtype('int64')
>>> a = np.array([1., 2., 3.])
>>> a.dtype
dtype('float64')
要素の型を指定できる。
>>> a = np.array([1, 2, 3], dtype=float)
>>> a
array([1., 2., 3.])
>>> a.dtype
dtype('float64')
続きの数値なら、np.arange() で作れる。 >>> np.arange(1, 11) array([ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) >>> np.arange(1, 11, 2) array([1, 3, 5, 7, 9]) >>> np.arange(10, 0, -1) array([10, 9, 8, 7, 6, 5, 4, 3, 2, 1]) 繰り返しを行う np.tile() というものもある。 >>> np.tile([1, 2], 3) array([1, 2, 1, 2, 1, 2]) 要素のアクセス各要素にアクセスするには、添字を使う (0 から始まる)。 >>> a[0] 1 "0:2" の形で 1 要素目から 2 要素目までの部分配列を取り出せる。 >>> a[0:2] array([1, 2]) このような書き方をスライス表記という。 添字のリストを与えることもできる。 >>> a[[0, 1]] array([1, 2]) bool のリストを与えることもできる (これをマスクという)。 >>> a[[True, False, True]] array([1, 3]) 要素のデータ型要素の値を変更する際、データ型に注意すること。
>>> a = np.array([1, 2, 3])
>>> a
array([1, 2, 3])
>>> a[0] = 2.5
>>> a
array([2, 2, 3])
>>> a.dtype
dtype('int64')
上の a の要素の型は int64 になっているので、要素に浮動小数点を入れると、小数点以下が黙って切り捨てられてしまう。 ちなみに、Juila だときっちり怒られる。
julia> a = [1, 2, 3]
3-element Vector{Int64}:
1
2
3
julia> a[1] = 2.5
ERROR: InexactError: Int64(2.5)
R の場合は、数値のデフォルト型が浮動小数点数型なので問題にならない。 > a <- c(1, 2, 3) > a [1] 1 2 3 > typeof(a) [1] "double" > a[1] <- 2.5 > a [1] 2.5 2.0 3.0 このあたりの違いに気をつけること。 ベクトルのサイズ長さを知りたければ len() を使えばよい。 >>> len(a) 3 属性 size で大きさを得ることもできる。 >>> a.size 3 要素の追加や削除np.append() で末尾に要素を追加できる。ただし、もとの配列は変更されない。 >>> np.append(a, 4) array([1, 2, 3, 4]) >>> a array([1, 2, 3]) 要素を削除するには np.delete() を使う。 >>> b = np.append(a, 4) >>> b array([1, 2, 3, 4]) >>> np.delete(b, 3) array([1, 2, 3]) np.delete() の第 2 引数は削除したい要素の添字である。 添字指定でも同じことができる。 >>> b[[x for x in range(len(b)) if x != 3]] array([1, 2, 3]) 途中に要素を追加するには np.insert() を用いる。 >>> np.insert(a, 1, 5) array([1, 5, 2, 3]) 配列の連結末尾に配列を追加する場合は np.append() を用いる。 >>> np.append(a, [5, 6, 4]) array([1, 2, 3, 5, 6, 4]) 配列の代入配列の要素を先頭とそれ以外に分けて変数に入れることができる。 >>> a, *b = np.array([1, 2, 3]) >>> a 1 >>> b [2, 3] 配列を別の変数に代入する場合には注意が必要である。 >>> a = np.array([1, 2, 3]) >>> b = a >>> b[0] = 2 >>> b array([2, 2, 3]) >>> a array([2, 2, 3]) 上の例では、b の要素の値を変更すると、a の要素の値も変わってしまう。代入はオブジェクトを入れる箱に名前を付けるだけのため、b は a と同じオブジェクトを見ているからである。b に a のコピーを割り当てたい場合は、copy() を用いる。 >>> a = np.array([1, 2, 3]) >>> b = a.copy() >>> b[0] = 2 >>> b array([2, 2, 3]) >>> a array([1, 2, 3]) 大きさだけ確保するとりあえずベクトルの大きさだけ確保するには、np.zeros() を使えばよい。 >>> a = np.zeros(3) >>> a array([0., 0., 0.]) np.tile() でもよい。 >>> a = np.tile(0., 3) >>> a array([0., 0., 0.]) ある値で埋める全要素を特定の値にしたい場合などは、fill() を使う。 >>> a = np.array([1, 2, 3]) >>> a array([1, 2, 3]) >>> a.fill(0) >>> a array([0, 0, 0]) 以下のようにしてもよい。 >>> a[:] = 0 結合する配列を結合するには、np.hstack() を使う。 >>> a = np.array([1, 2, 3]) >>> b = np.array([4, 5, 6]) >>> np.hstack((a, b)) array([1, 2, 3, 4, 5, 6]) 最初と最後を取り出す配列の最初と最後の要素を取り出すには、次のようにする。 >>> a = np.arange(1, 101) >>> a[:3] array([1, 2, 3]) >>> a[-3:] array([ 98, 99, 100]) ":3" は 0、1、2、"-3:" は -3、-2、-1 となる。 要素が含まれるかどうか要素が含まれるかどうかは、in、not in でわかる。 >>> 1 in np.array([1, 2, 3]) True >>> 4 in np.array([1, 2, 3]) False >>> 4 not in np.array([1, 2, 3]) True 要素の並び替え要素を並び替えるには sort() を用いる。 >>> a = np.array([1, 2, 3, 5, 6, 4]) >>> a.sort() >>> a array([1, 2, 3, 4, 5, 6]) もとの配列を変更したくない場合は、sorted() を用いる。 >>> sorted(a) [1, 2, 3, 4, 5, 6] >>> a array([1, 2, 3, 5, 6, 4]) 単純に逆にするには、np.flip() を用いる。 >>> np.flip(a) array([4, 6, 5, 3, 2, 1]) 重複要素の削除重複要素を削除するには np.unique() を使う。 >>> a = np.array([1, 2, 2, 5, 4, 6, 3]) >>> np.unique(a) array([1, 2, 3, 4, 5, 6]) もとの添字を得ることもできる。 >>> u, idx = np.unique(a, return_index=True) >>> idx array([0, 1, 6, 4, 3, 5]) >>> a[idx] array([1, 2, 3, 4, 5, 6]) 最大値と最小値要素の最大値、最小値を得るには、それぞれ max()、min() を用いる。 >>> rng = np.random.default_rng() >>> a = rng.random(5) >>> a array([0.94425088, 0.36188794, 0.62370355, 0.62279579, 0.29481583]) >>> a.max() 0.9442508774201377 >>> a.min() 0.2948158309981246 np.max()、np.min() でもよい。 >>> np.max(a) 0.9442508774201377 >>> np.min(a) 0.2948158309981246 最大値、最小値のそれぞれの添字がほしい場合は、argmax()、argmin() がある。 >>> a.argmax() 0 >>> a.argmin() 4 np.maximum() で配列同士の大きいほうの要素を取ることができる。これを使ってあるスカラー値で下限を設定することができる。 >>> np.maximum(a, 0.5) array([0.94425088, 0.5 , 0.62370355, 0.62279579, 0.5 ]) 絶対値np.abs() でそれぞれの要素の絶対値が得られる。 >>> a = np.array([1, -2, 3, -4, 5]) >>> np.abs(a) array([1, 2, 3, 4, 5]) 合計np.sum()、sum() で合計を取ることができる。 >>> a = np.array([1, 2, 3, 4, 5]) >>> np.sum(a) 15 >> a.sum() 15 >> sum(a) 15 足していく途中がほしい場合は np.cumsum()、cumsum() を使う。 >>> np.cumsum(a) array([ 1, 3, 6, 10, 15]) >>> a.cumsum() array([ 1, 3, 6, 10, 15]) 積np.prod()、prod() で各要素の積を取ることができる。 >>> a = np.array([1, 2, 3, 4, 5]) >>> np.prod(a) 120 >>> a.prod() 120 積の途中がほしい場合は np.cumprod()、cumprod() を用いる。 >>> np.cumprod(a) array([ 1, 2, 6, 24, 120]) >>> a.cumprod() array([ 1, 2, 6, 24, 120]) 演算の積算np.ufunc.reduce() を用いると、ある演算子を要素に適用した結果などが得られる。 >>> a = np.array([1, 2, 3, 4, 5]) >>> np.ufunc.reduce(np.add, a) 15 >>> np.ufunc.reduce(np.multiply, a) 120 ここで、np.add や np.multiply は、+ や * の関数である。 初期値を指定できる。 >>> np.ufunc.reduce(np.multiply, a, initial=-1) -120 積算の途中がほしい場合は、np.ufunc.accumulate() を用いる。 >>> np.ufunc.accumulate(np.add, a) array([ 1, 3, 6, 10, 15]) 階差np.diff() で階差を作る。 >>> np.diff(np.cumprod([1, 2, 3, 4, 5])) array([ 1, 4, 18, 96]) 配列同士の演算配列の要素同士を足し合わせるには、+ でよい。 >>> a = np.array([1, 2, 3]) >>> b = np.array([4, 5, 6]) >>> a + b array([5, 7, 9]) - や * も同様である。片方がスカラーでも機能する。 各要素への関数の適用配列の各要素へ関数を適用して別の配列を作るには、map() を用いる。 >>> a = np.arange(1, 6) >>> a array([1, 2, 3, 4, 5]) >>> np.array(list(map(lambda x: 2*x + 1, a))) array([ 3, 5, 7, 9, 11]) 抽出filter() で条件に合った要素だけを取り出すことができる。 >>> a = np.arange(1, 11) >>> a array([ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) >>> np.array(list(filter(lambda x: x % 2 == 0, a))) array([ 2, 4, 6, 8, 10]) map() を使って bool 型の添字リストを作る方法もある。 >>> a[list(map(lambda x: x % 2 == 0, a))] array([ 2, 4, 6, 8, 10]) 内包表記でもよい。 >>> np.array([x for x in a if x % 2 == 0]) array([ 2, 4, 6, 8, 10]) もっと直接的な書き方もできる。 >>> a[a % 2 == 0] array([ 2, 4, 6, 8, 10]) 条件に合う場所の添字がほしい場合は、np.argwhere() を用いる。
>>> a = np.arange(2, 11)
>>> a
array([ 2, 3, 4, 5, 6, 7, 8, 9, 10])
>>> np.argwhere(a % 2 == 0)
array([[0],
[2],
[4],
[6],
[8]])
np.argwhere() は、行列でも使えるため、上のような感じで返ってくるが、1次元配列の場合は次のようにしたほうが便利かもしれない。 >>> np.argwhere(a % 2 == 0).flat[:] array([0, 2, 4, 6, 8]) flat というのは、たとえば "[[0]]" を添字指定でふつうに取り出すと "[0]" になるが、flat で添字を指定して取り出すと "0" が直接得られる。 条件判定どれかの要素で条件が成り立つかを調べるには np.any()、any() が使える。また、すべての要素で条件が成り立つかどうかは np.all()、all() でわかる。 >>> a = np.array([1, 3, 5, 8, 9]) >>> np.any(a % 2 == 0) True >>> any(a % 2 == 0) True >>> np.all(a % 2 == 1) False >>> all(a % 2 == 1) False >>> a = np.array([1, 3, 5, 7, 9]) >>> np.all(a % 2 == 1) True >>> all(a % 2 == 1) True 条件に合うものが何個あるかを数えるには、np.sum()、sum() を用いればよい。 >>> a = np.array([1, 3, 5, 8, 9, 10]) >>> np.sum(a % 2 == 0) 2 >>> sum(a % 2 == 0) 2 要素の置き換え要素の置き換えは、以下のようにできる。 >>> a = np.array([1, 2, 3, 4, 5], dtype=float) >>> a[a % 2 == 0] = np.nan >>> a array([ 1., nan, 3., nan, 5.]) ここで、np.nan は NaN (Not a Number) である。np.nan を 0 に置き換えたいとすると、次のようにすればよい。 >>> np.place(a, np.isnan(a), 0) >>> a array([1., 0., 3., 0., 5.]) あるいは、この場合は次のようにもできる。 >>> a[np.isnan(a)] = 0 >>> a array([1., 0., 3., 0., 5.]) masked array というものもある。np.nan を含んだまま合計すると値が np.nan になってしまうが、np.nan を除いて合計を取ることができる。 >>> a.sum() nan >>> np.ma.masked_array(a, mask=np.isnan(a)).sum() 9.0 次のようにもできる。 >>> a[np.logical_not(np.isnan(a))].sum() 9.0 行列次のような形で行列を定義することができる。
>>> a = np.array([[1, 2], [3, 4]])
>>> a
array([[1, 2],
[3, 4]])
np.matrix() というものもあるが、利用が推奨されていない。 値が 0 の行列がとりあえずほしい場合は、np.zeros() を用いる。
>>> np.zeros((2, 3))
array([[0., 0., 0.],
[0., 0., 0.]])
要素にアクセスしたければ、カンマ区切りで添字を指定すればよい (0 から始まる)。 >>> a[0, 1] 2 "0:2" のような添字指定で部分行列のを得ることも可能。 >>> a[0:2, 0] array([1, 3]) 範囲による添字指定で先頭と末尾の指定を省略することもできる。 >>> a[1:, 1] array([4]) >>> a[:-1, 1] array([2]) ある列全体がほしいといった場合は、":" だけ指定すればよい。 >>> a[:, 1] array([2, 4]) サイズがほしい場合は、属性 size で得られる。 >>> a.size 4 サイズは全体のサイズである。行列の形がほしい場合は、属性 shape で得られる。 >>> a.shape (2, 2) 多次元配列次のようにすると、多次元配列を定義できる。
>>> a = np.array([[[1, 2], [3, 4]], [[5, 6], [7, 8]]])
>>> a
array([[[1, 2],
[3, 4]],
[[5, 6],
[7, 8]]])
>>> a[0]
array([[1, 2],
[3, 4]])
>>> a[1]
array([[5, 6],
[7, 8]])
>>> a[0, :, :]
array([[1, 2],
[3, 4]])
大きさだけ確保するとりあえず行列の大きさだけ確保するには、np.zeros() を使えばよい。
>>> a = np.zeros((2, 3))
>>> a
array([[0., 0., 0.],
[0., 0., 0.]])
np.tile() でもよい。
>>> a = np.tile(0., (2, 3))
>>> a
array([[0., 0., 0.],
[0., 0., 0.]])
ある値で埋める全要素を特定の値にしたい場合などは、fill() を使う。
>>> a = np.array([[1, 2], [3, 4]])
>>> a
array([[1, 2],
[3, 4]])
>>> a.fill(0)
>>> a
array([[0, 0],
[0, 0]])
array([0, 0, 0])
以下のようにしてもよい。 >>> a[:] = 0 形状を変えるreshape() で形状を変えられる。
>>> a = np.arange(1, 7).reshape(3, 2)
>>> a
array([[1, 2],
[3, 4],
[5, 6]])
>>> a = np.arange(1, 7).reshape(3, 2, order="F")
>>> a
array([[1, 4],
[2, 5],
[3, 6]])
もとの行列には影響しない。
>>> a.reshape(1, 6)
array([[1, 4, 2, 5, 3, 6]])
>>> a
array([[1, 4],
[2, 5],
[3, 6]])
これを使うと、ちょっとした行列を作るのが少し楽になるかもしれない。
>> a = np.array([1, 2, 3, 4]).reshape(2, 2)
>>> a
array([[1, 2],
[3, 4]])
結合する配列を結合するには、np.vstack()、np.hstack() を使う。
>>> a = np.array([[1, 2], [3, 4]])
>>> b = np.array([[5, 6], [7, 8]])
>>> np.vstack((a, b))
array([[1, 2],
[3, 4],
[5, 6],
[7, 8]])
>>> np.hstack((a, b))
array([[1, 2, 5, 6],
[3, 4, 7, 8]])
行列の部分参照を得るPython の変数間の代入では、オブジェクトの参照渡されるが、NumPy の配列では、配列の部分の参照を渡すことができる。 >>> a = np.array([[1, 2], [3, 4]]) >>> b = a[:, 1].view() >>> b array([2, 4]) 上の例では、view() で a の部分の参照を b に渡している。実は、スライス自体がこの参照を返すようになっている。つまり、単に以下のようにすればよい。
>>> b = a[:, 1]
>>> b
array([2, 4])
>>> b[:] = 0.
>>> a
array([[1, 0],
[3, 0]])
b の値を変更すると、a の値が部分的に変更される。 ループ行ごとのループ >>> a = np.array([[2, 4], [6, 8]]) >> for x in a: ... print(x) ... [2 4] [6 8] 列ごとのループ >>> for x in a.T: ... print(x) ... [2 6] [4 8] a.T は a の転置である。あるいは >>> for x in np.nditer(a, flags=["external_loop"], order="F"): ... print(x) ... [2 6] [4 8] 要素ごとのループ >>> for x in np.nditer(a): ... print(x) ... 2 4 6 8 >> for x in np.nditer(a, order="F"): ... print(x) ... 2 6 4 8 値を変更したい場合は、次のようにする。
>>> with np.nditer(a, op_flags=["readwrite"]) as it:
... for x in it:
... x[...] = 2*x
...
>>> a
array([[ 4, 8],
[12, 16]])
インデックスがほしい場合は、次のようにする。 >>> it = np.nditer(a, flags=["c_index"]) >>> for x in it: ... print(it.index, x) ... 0 2 1 4 2 6 3 8 >>> it = np.nditer(a, flags=["f_index"], order="F") >>> for x in it: ... print(it.index, x) ... 0 2 1 6 2 4 3 8 >>> it = np.nditer(a, flags=["multi_index"]) >>> for x in it: ... print(it.multi_index, x) ... (0, 0) 2 (0, 1) 4 (1, 0) 6 (1, 1) 8 転置T、transpose()、np.transpose() で行列の転置が得られる。
>>> a = np.array([[1, 2], [3, 4]])
>>> a
array([[1, 2],
[3, 4]])
>>> a.T
array([[1, 3],
[2, 4]])
>>> a.transpose()
array([[1, 3],
[2, 4]])
>>> np.transpose(a)
array([[1, 3],
[2, 4]])
複素共役行列conj()、conjugate()、np.conj()、np.conjugate() で複素共役行列が得られる。
>>> a = np.array([[1 + 2j], [3 + 4j]])
>>> a
array([[1.+2.j],
[3.+4.j]])
>>> a.conj()
array([[1.-2.j],
[3.-4.j]])
>>> a.conjugate()
array([[1.-2.j],
[3.-4.j]])
>>> np.conj(a)
array([[1.-2.j],
[3.-4.j]])
>>> np.conjugate(a)
array([[1.-2.j],
[3.-4.j]])
行列同士の演算行列同士の和は + で計算できる。
>>> a = np.array([1, 2, 3, 4]).reshape(2, 2)
>>> b = np.array([5, 6, 7, 8]).reshape(2, 2)
>>> a + b
array([[ 6, 8],
[10, 12]])
積 * だと、要素同士の積になる。
>>> a * b
array([[ 5, 12],
[21, 32]])
行列の積を計算したい場合は、@ あるいは np.matmul() を用いる。
>>> a @ b
array([[19, 22],
[43, 50]])
>>> np.matmul(a, b)
array([[19, 22],
[43, 50]])
これと似た dot()、np.dot() もある。
>>> a.dot(b)
array([[19, 22],
[43, 50]])
>>> np.dot(a, b)
array([[19, 22],
[43, 50]])
累乗も要素ごとの累乗になる。
>>> a = np.array([1, 2, 3, 4, 5, 6, 7, 9, 8]).reshape(3, 3)
>>> a
array([[1, 2, 3],
[4, 5, 6],
[7, 9, 8]])
>>> a**2
array([[ 1, 4, 9],
[16, 25, 36],
[49, 81, 64]])
行列の累乗を計算したい場合は、np.linalg.matrix_power() を用いる。
>>> np.linalg.matrix_power(a, 2)
array([[ 30, 39, 39],
[ 66, 87, 90],
[ 99, 131, 139]])
>>> np.linalg.matrix_power(a, -2)
array([[ 3.74074074, -3.85185185, 1.44444444],
[-3.25925926, 3.81481481, -1.55555556],
[ 0.40740741, -0.85185185, 0.44444444]])
単位行列
>>> np.eye(3)
array([[1., 0., 0.],
[0., 1., 0.],
[0., 0., 1.]])
ノルム>>> a = np.array([1, 2, 3]) >>> np.linalg.norm(a) 3.7416573867739413 正規化正規化の関数はなさそうなので、自分で定義する。 >>> normalize = lambda a: a/np.linalg.norm(a) >>> normalize(a) array([0.26726124, 0.53452248, 0.80178373]) 内積>>> a = np.array([1, 2, 3]) >>> b = np.array([4, 5, 6]) >>> a.dot(b) 32 >>> np.dot(a, b) 32 外積>>> np.cross(a, b) array([-3, 6, -3]) トレース
>>> a = np.array([1, 2, 3, 4, 5, 6, 7, 9, 8]).reshape(3, 3)
>>> a
array([[1, 2, 3],
[4, 5, 6],
[7, 9, 8]])
>>> np.trace(a)
14
対角成分>>> np.diag(a) array([1, 5, 8]) 行列式>>> np.linalg.det(a) 8.999999999999998 逆行列
>>> np.linalg.inv(a)
array([[-1.55555556, 1.22222222, -0.33333333],
[ 1.11111111, -1.44444444, 0.66666667],
[ 0.11111111, 0.55555556, -0.33333333]])
固有値
>>> a = np.array([1, 2, 3, 4]).reshape(2, 2)
>>> a
array([[1, 2],
[3, 4]])
>>> f = np.linalg.eig(a)
>>> f
(array([-0.37228132, 5.37228132]), array([[-0.82456484, -0.41597356],
[ 0.56576746, -0.90937671]]))
返り値は固有値、固有ベクトルのペアである。固有値だけ得る関数もある。 >>> np.linalg.eigvals(a) array([-0.37228132, 5.37228132]) 分解LU 分解LU 分解は NumPy にないので、代わりに SciPy を使う。
>>> from scipy.linalg import lu
>>> a = np.array([1, 2, 3, 4, 5, 6, 7, 9, 8]).reshape(3, 3)
>>> a
array([[1, 2, 3],
[4, 5, 6],
[7, 9, 8]])
>>> P, L, U = lu(a)
>>> P
array([[0., 1., 0.],
[0., 0., 1.],
[1., 0., 0.]])
>>> L
array([[ 1. , 0. , 0. ],
[ 0.14285714, 1. , 0. ],
[ 0.57142857, -0.2 , 1. ]])
>>> U
array([[7. , 9. , 8. ],
[0. , 0.71428571, 1.85714286],
[0. , 0. , 1.8 ]])
>>> P @ L @ U
array([[1., 2., 3.],
[4., 5., 6.],
[7., 9., 8.]])
Cholesky 分解
>>> a = np.array([4, 12, -16, 12, 37, -43, -16, -43, 98]).reshape(3, 3)
>>> a
array([[ 4, 12, -16],
[ 12, 37, -43],
[-16, -43, 98]])
>>> L = np.linalg.cholesky(a)
>>> L
array([[ 2., 0., 0.],
[ 6., 1., 0.],
[-8., 5., 3.]])
>>> L @ L.T
array([[ 4., 12., -16.],
[ 12., 37., -43.],
[-16., -43., 98.]])
QR 分解
>>> a = np.array([12, -51, 4, 6, 167, -68, -4, 24, -41]).reshape(3, 3)
>>> a
array([[ 12, -51, 4],
[ 6, 167, -68],
[ -4, 24, -41]])
>>> Q, R = np.linalg.qr(a)
>>> Q
array([[-0.85714286, 0.39428571, 0.33142857],
[-0.42857143, -0.90285714, -0.03428571],
[ 0.28571429, -0.17142857, 0.94285714]])
>>> R
array([[ -14., -21., 14.],
[ 0., -175., 70.],
[ 0., 0., -35.]])
>>> Q @ R
array([[ 12., -51., 4.],
[ 6., 167., -68.],
[ -4., 24., -41.]])
特異値分解
>>> a = np.array([
... 1, 0, 0, 0, 2,
... 0, 0, 3, 0, 0,
... 0, 0, 0, 0, 0,
... 0, 2, 0, 0, 0]).reshape(4, 5)
>>> a
array([[1, 0, 0, 0, 2],
[0, 0, 3, 0, 0],
[0, 0, 0, 0, 0],
[0, 2, 0, 0, 0]])
>>> U, S, Vh = np.linalg.svd(a)
>>> U
array([[ 0., 1., 0., 0.],
[ 1., 0., 0., 0.],
[ 0., 0., 0., 1.],
[ 0., 0., -1., 0.]])
>>> S
array([3. , 2.23606798, 2. , 0. ])
>>> Vh
array([[-0. , 0. , 1. , -0. , 0. ],
[ 0.4472136 , 0. , 0. , 0. , 0.89442719],
[ 0. , -1. , 0. , 0. , 0. ],
[ 0. , 0. , 0. , 1. , 0. ],
[-0.89442719, 0. , 0. , 0. , 0.4472136 ]])
>>> U @ np.diag(S) @ Vh[:4, :]
array([[1., 0., 0., 0., 2.],
[0., 0., 3., 0., 0.],
[0., 0., 0., 0., 0.],
[0., 2., 0., 0., 0.]])
連立方程式を解く
>>> a = np.array([1, 2, 3, 4, 5, 6, 7, 9, 8]).reshape(3, 3)
>>> a
array([[1, 2, 3],
[4, 5, 6],
[7, 9, 8]])
>>> b = np.array([1, 2, 3]).reshape(3, 1)
>>> b
array([[1],
[2],
[3]])
>>> x = np.linalg.solve(a, b)
>>> x
array([[-0.11111111],
[ 0.22222222],
[ 0.22222222]])
>>> a @ x
array([[1.],
[2.],
[3.]])
データフレームデータフレームは、データ分析のための便利なデータ構造である。データフレームは、pandas パッケージによって提供されている。パッケージがインストールされていれば、以下のようにインポートする。 >>> import pandas as pd pandas はよく pd として使われる。 データフレームを手で作る場合は、次のようになる。
>>> df = pd.DataFrame({"name": ["omochi", "annko", "tamago"], "type": ["sabatora", "chatora", "mike"], "age": [3, 4, 5]})
>>> df
name type age
0 omochi sabatora 3
1 annko chatora 4
2 tamago mike 5
空の状態から作っていく方法もある。
>>> df = pd.DataFrame()
>>> df["name"] = ["omochi", "annko", "tamago"]
>>> df["type"] = ["sabatora", "chatora", "mike"]
>>> df["age"] = [3, 4, 5]
>>> df
name type age
0 omochi sabatora 3
1 annko chatora 4
2 tamago mike 5
データの個数データの個数は len() で得られる。 >>> len(df) 3 要素のアクセス要素のアクセスは iloc でできる。 >>> df.iloc[0, 1] 'sabatora' >>> df.iloc[:, 1] 0 sabatora 1 chatora 2 mike Name: type, dtype: object ピンポイントの値がほしければ、iat も使える。 >>> df.iat[0, 1] 'sabatora' values を使う方法もある。 >>> df.values[0, 1] 'sabatora' >>> df.values[:, 1] array(['sabatora', 'chatora', 'mike'], dtype=object) 結果を見るとわかるように、この場合は NumPy の配列で返ってくる。 直接スライスを使うと、選択した行が得られる。
>>> df[:2]
name type age
0 omochi sabatora 3
1 annko chatora 4
名前による参照名前による参照も行える。
>>> df.name
0 omochi
1 annko
2 tamago
Name: name, dtype: object
>>> df["name"]
0 omochi
1 annko
2 tamago
Name: name, dtype: object
>>> df[["name", "type"]]
name type
0 omochi sabatora
1 annko chatora
2 tamago mike
部分がほしければ次のようにもできる。
>>> df[["name", "type"]][:2]
name type
0 omochi sabatora
1 annko chatora
loc() を使う方法もある。
>>> df.loc[:1, ["name", "type"]]
name type
0 omochi sabatora
1 annko chatora
スライスの数値が異なることに注意。 ピンポイントでデータがほしければ、at() が使える。 >>> df.at[1, "name"] 'annko' 列の名前を変更する>>> df.columns Index(['name', 'type', 'age'], dtype='object') columns の値を変えれば、列の名前を変更できる。
>>> df.columns = ["名前", "タイプ", "年齢"]
>>> df
名前 タイプ 年齢
0 omochi sabatora 3
1 annko chatora 4
2 tamago mike 5
カテゴリカルデータにするある種類の中でしか値を取らないカテゴリカルデータというものがある。type をカテゴリカルデータに変換するには、次のようにする。
>>> df["type"] = df["type"].astype("category")
>>> df
name type age
0 omochi sabatora 3
1 annko chatora 4
2 tamago mike 5
>>> df["type"]
0 sabatora
1 chatora
2 mike
Name: type, dtype: category
Categories (3, object): ['chatora', 'mike', 'sabatora']
type に変な値を入れようとすると怒られる。 >>> df["type"][1] = "white" ... TypeError: Cannot setitem on a Categorical with a new category (white), set the categories first あとからカテゴリーを追加する場合は、次のようにする。
>>> df["type"] = df["type"].astype("category")
>>> df["type"] = df["type"].cat.add_categories("white")
>>> df["type"][1] = "white"
>>> df
name type age
0 omochi sabatora 3
1 annko white 4
2 tamago mike 5
>>> df.type
0 sabatora
1 white
2 mike
Name: type, dtype: category
Categories (4, object): ['chatora', 'mike', 'sabatora', 'white']
データの追加行データを追加したければ、最後の行の次に追加すればよい。
>>> df.loc[3, :] = ["momo", "mike", 3]
>>> df
name type age
0 omochi sabatora 3.0
1 annko chatora 4.0
2 tamago mike 5.0
3 momo mike 3.0
添字を飛ばすと何が起こるかというと、そのまま入るだけである。
>>> df.loc[5, :] = ["kinako", "chatora", 4]
>>> df
name type age
0 omochi sabatora 3.0
1 annko chatora 4.0
2 tamago mike 5.0
3 momo mike 3.0
5 kinako chatora 4.0
データは、名前を指定して入れることができる。
>>> df = pd.DataFrame()
>>> df["name"] = ["omochi", "annko", "tamago"]
>>> df["type"] = ["sabatora", "chatora", "mike"]
>>> df["age"] = [3, 4, 5]
>>> df
name type age
0 omochi sabatora 3
1 annko chatora 4
2 tamago mike 5
データの削除drop() でデータを取り除くことができる。
>>> df.drop(1)
name type age
0 omochi sabatora 3
2 tamago mike 5
>>> df.drop("type", axis=1)
name age
0 omochi 3
1 annko 4
2 tamago 5
結合データフレームを結合するには、pd.concat() を用いる。 >>> df = pd.DataFrame() >>> df["height"] = [147.9, 163.5, 159.8, 155.1, 163.3] >>> df["weight"] = [41.7, 60.2, 47.0, 53.2, 48.3] >>> df2 = pd.DataFrame() >>> df["height"] = [158.7, 172.0, 161.2, 153.9, 161.6] >>> df["weight"] = [59.7, 58.5, 49.0, 46.7, 44.5] >>> df3 = pd.concat([df, df2], ignore_index=True) >>> df3 height weight 0 147.9 41.7 1 163.5 60.2 2 159.8 47.0 3 155.1 53.2 4 163.3 48.3 5 158.7 59.7 6 172.0 58.5 7 161.2 49.0 8 153.9 46.7 9 161.6 44.5 "ignore_index=True" を指定しないと、もとのインデックスが残る。 >>> df3 = pd.concat([df, df2]) >>> df3 height weight 0 147.9 41.7 1 163.5 60.2 2 159.8 47.0 3 155.1 53.2 4 163.3 48.3 0 158.7 59.7 1 172.0 58.5 2 161.2 49.0 3 153.9 46.7 4 161.6 44.5 最初と最後を取り出すデータフレームの最初と最後を取り出すには、head()、tail() を使う。 >>> df = pd.DataFrame() >>> df["height"] = [147.9, 163.5, 159.8, 155.1, 163.3, 158.7, 172.0, 161.2, 153.9, 161.6] >>> df["weight"] = [41.7, 60.2, 47.0, 53.2, 48.3, 59.7, 58.5, 49.0, 46.7, 44.5] >>> df.head(3) height weight 0 147.9 41.7 1 163.5 60.2 2 159.8 47.0 >>> df.tail(3) height weight 7 161.2 49.0 8 153.9 46.7 9 161.6 44.5 値が含まれるかどうかvalues を使えば、in で値が含まれるか確認できる。
>>> df = pd.DataFrame()
>>> df["name"] = ["omochi", "annko", "tamago"]
>>> df["type"] = ["sabatora", "chatora", "mike"]
>>> df["age"] = [3, 4, 5]
>>> df
name type age
0 omochi sabatora 3
1 annko chatora 4
2 tamago mike 5
>>> "annko" in df["name"].values
True
isin() というものもある。 >>> df["name"].isin(["annko"]) 0 False 1 True 2 False Name: name, dtype: bool 並び替え値で並び替えるには、sort_values() を用いる。 >>> df = pd.DataFrame() >>> df["height"] = [147.9, 163.5, 159.8, 155.1, 163.3, 158.7, 172.0, 161.2, 153.9, 161.6] >>> df["weight"] = [41.7, 60.2, 47.0, 53.2, 48.3, 59.7, 58.5, 49.0, 46.7, 44.5] >>> df.sort_values(by=["height"]) height weight 0 147.9 41.7 8 153.9 46.7 3 155.1 53.2 5 158.7 59.7 2 159.8 47.0 7 161.2 49.0 9 161.6 44.5 4 163.3 48.3 1 163.5 60.2 6 172.0 58.5 >>> df.sort_values(by=["height"], ascending=False) height weight 6 172.0 58.5 1 163.5 60.2 4 163.3 48.3 9 161.6 44.5 7 161.2 49.0 2 159.8 47.0 5 158.7 59.7 3 155.1 53.2 8 153.9 46.7 0 147.9 41.7 重複データの削除重複したデータを削除するには drop_duplicates() を用いる。
>>> df2 = pd.concat([df, df.head(3)], ignore_index=True)
>>> df2
height weight
0 147.9 41.7
1 163.5 60.2
2 159.8 47.0
3 155.1 53.2
4 163.3 48.3
5 158.7 59.7
6 172.0 58.5
7 161.2 49.0
8 153.9 46.7
9 161.6 44.5
10 147.9 41.7
11 163.5 60.2
12 159.8 47.0
>>> df2.drop_duplicates()
height weight
0 147.9 41.7
1 163.5 60.2
2 159.8 47.0
3 155.1 53.2
4 163.3 48.3
5 158.7 59.7
6 172.0 58.5
7 161.2 49.0
8 153.9 46.7
9 161.6 44.5
デフォルトでは重複データで最初に出てきたものを採用する。最後に出てきたものを採用するなら、次のようにする。
>>> df2.drop_duplicates(keep="last")
height weight
3 155.1 53.2
4 163.3 48.3
5 158.7 59.7
6 172.0 58.5
7 161.2 49.0
8 153.9 46.7
9 161.6 44.5
10 147.9 41.7
11 163.5 60.2
12 159.8 47.0
変わる部分は、インデックスである。インデックスを振り直すなら、reset_index() を用いる。 >>> df2.drop_duplicates(keep="last").reset_index(drop=True) height weight 0 155.1 53.2 1 163.3 48.3 2 158.7 59.7 3 172.0 58.5 4 161.2 49.0 5 153.9 46.7 6 161.6 44.5 7 147.9 41.7 8 163.5 60.2 9 159.8 47.0 "drop=True" を指定しない場合、もとのインデックスが残る。 最大値と最小値要素の最大値、最小値を得るには、それぞれ max()、min() を用いる。 >>> df["height"].max() 172.0 >>> df["height"].min() 147.9 総和と平均要素の総和や平均値を得るには、それぞれ sum()、mean() を用いる。 >>> df["height"].sum() 1597.0 >>> df["height"].mean() 159.7 要素への関数の適用列ごとの平均をとるには次のようにできる。 >>> df.apply(lambda x: x.mean()) height 159.70 weight 50.88 dtype: float64 行ごとに計算したい場合は、次のようにできる。 >>> df["BMI"] = df.apply(lambda x: round(x["weight"]/(x["height"]/100.)**2, 1), axis=1) >>> df height weight BMI 0 147.9 41.7 19.1 1 163.5 60.2 22.5 2 159.8 47.0 18.4 3 155.1 53.2 22.1 4 163.3 48.3 18.1 5 158.7 59.7 23.7 6 172.0 58.5 19.8 7 161.2 49.0 18.9 8 153.9 46.7 19.7 9 161.6 44.5 17.0 要素ごとに処理を行いたい場合は、次のようにする。 >>> df["height"].map(lambda x: x if x > 150 else 150) 0 150.0 1 163.5 2 159.8 3 155.1 4 163.3 5 158.7 6 172.0 7 161.2 8 153.9 9 161.6 Name: height, dtype: float64 データフレーム全体について要素ごとに処理したい場合は、df.map() を用いる。 >>> df.map(lambda x: round(x)) height weight BMI 0 148 42 19 1 164 60 22 2 160 47 18 3 155 53 22 4 163 48 18 5 159 60 24 6 172 58 20 7 161 49 19 8 154 47 20 9 162 44 17 抽出「身長が 160 cm 以上」といった条件に合うデータを得るには、次のようにする。 >>> df[df["height"] >= 160] height weight 1 163.5 60.2 4 163.3 48.3 6 172.0 58.5 7 161.2 49.0 9 161.6 44.5 条件判定どれかの要素で条件が成り立つかを調べるには any() が使える。また、すべての要素で条件が成り立つかどうかは all() でわかる。 >>> any(df["height"].map(lambda x: x > 160)) True >>> any(df["height"].map(lambda x: x < 150)) True >>> all(df["height"].map(lambda x: x > 160)) False 要素の置き換え条件に合った要素を別の値で置き換えるのは map() でできる。 >>> df["height"].map(lambda x: 160 if x < 160 else x) 0 160.0 1 163.5 2 160.0 3 160.0 4 163.3 5 160.0 6 172.0 7 161.2 8 160.0 9 161.6 Name: height, dtype: float64 ファイルの書き出しと読み込みデータフレームを CSV ファイルとして書き出すには、次のようにする。
>>> df = pd.DataFrame({"name": ["omochi", "annko", "tamago"], "type": ["sabatora", "chatora", "mike"], "age": [3, 4, 5]})
>>> df.to_csv("cats.csv", index=False)
cats.csv name,type,age omochi,sabatora,3 annko,chatora,4 tamago,mike,5 もし "index=False" がなかったら、以下のような結果になる。 ,name,type,age 0,omochi,sabatora,3 1,annko,chatora,4 2,tamago,mike,5 書き出した CSV ファイルを読み込むには、次のようにする。
>>> df2 = pd.read_csv("cats.csv")
>>> df2
name type age
0 omochi sabatora 3
1 annko chatora 4
2 tamago mike 5
| |
| PENGUINITIS | |