け日記

最近はPythonでいろいろやってます

Python: NumPyで縦持ちから横持ちへ変換する小ワザ

以前、DataFrameの縦持ち・横持ちの変換につ いてまとめましたが、今回はNumPyの行列を縦持ちから横持ちへ変換する小ワザを紹介します。

ohke.hateblo.jp

以下のような5行3列の値を、列1の値を行インデックス、列2を列インデックス、列3を各要素の値として行列化します。前提として、行と列になる値 (ここでは列1と列2) の組み合わせはユニークである必要があります (合計・平均などの集計が必要な場合は、事前に行ってください) 。

列1 列2 列3
1 1 1
1 2 2
2 1 2
2 3 1
3 3 3


\begin{pmatrix}
1 \ 2 \ 0 \\
2 \ 0 \ 1 \\
0 \ 0 \ 3
\end{pmatrix}

変換の実装は以下です。

キモはnumpy.uniqueメソッドの使い方です。
return_inverceをTrueにすると、ユニーク値のarrayに加えて、引数 (array) の各要素をユニーク値リストのインデックスに変換したarrayの2つがタプルで返ってきます。例えば、np.unique([1, 2, 1, 3, 3], return_inverse=True)では、array([1, 2, 3]), array([0, 1, 0, 2, 2])が返り値となります (引数の0番目と2番目の要素に、ユニーク値arrayの0番目の要素、つまり1であることを意味します)。

あとは、列1のユニーク値数×列2のユニーク値数の行列を作り、インデックス (uniqueの2つ目の返り値) を使って、列3の値をセットしてます。

import numpy as np

# 縦持ち行列
vertical_matrix = np.array([[1, 1, 1], [1, 2, 2], [2, 1, 2], [2, 3, 1], [3, 3, 3]])
#array([[1, 1, 1],
#       [1, 2, 2],
#       [2, 1, 2],
#       [2, 3, 1],
#       [3, 3, 3]])

# 行・列のインデックスを取得
rows, row_pos = np.unique(vertical_matrix[:, 0], return_inverse=True)
cols, col_pos = np.unique(vertical_matrix[:, 1], return_inverse=True)

# 横持ち行列の初期化
horizontal_matrix = np.zeros((len(rows), len(cols)))

# 行・列のインデックスを指定して、3列目の値をセット
horizontal_matrix[row_pos , col_pos ] = vertical_matrix[:, 2]
#array([[ 1.,  2.,  0.],
#       [ 2.,  0.,  1.],
#       [ 0.,  1.,  3.]])

参考

stackoverflow.com