NumPyはデータサイエンスや機械学習の分野において、高速な数値計算を支える非常に重要なライブラリです。
多次元配列(ndarray)を効率的に扱うことができる反面、多くの学習者が最初につまずくポイントの一つが「axis(軸)」の概念です。
特に行列計算において、行ごとに集計するのか、それとも列ごとに集計するのかを正しく指定できないと、意図しない計算結果を招くことになります。
本記事では、NumPyのaxisの仕組みを整理し、行ごと・列ごとの集計方法を具体的なコードとともに詳しく解説します。
NumPyの次元とaxisの基礎知識
NumPyの配列操作を理解する上で、axis(軸)というパラメータは計算の方向を決定する最も重要な要素です。
2次元配列(行列)を例に取ると、NumPyには「0番目の軸」と「1番目の軸」という2つの方向が存在します。
一般的に、axis=0は「行」の方向、すなわち縦の並びを指します。
一方で、axis=1は「列」の方向、すなわち横の並びを指します。
初心者のうちは、「axis=0が行ごとの集計」と勘違いしやすいですが、実際には「その軸に沿って計算を潰していく」というイメージを持つことが正解への近道です。
つまり、axis=0を指定すると行が潰されるため、結果として「列ごとの集計値」が算出されます。
逆に、axis=1を指定すると列が潰されるため、結果として「行ごとの集計値」が算出されます。
この関係性を正しく把握することが、NumPyを自在に操るための第一歩となります。
axis=0(列ごとの集計)の仕組みと具体例
axis=0を指定した場合、NumPyは「行をまたいで」計算を実行します。
以下のサンプルコードを用いて、実際に列ごとの合計値を算出してみましょう。
import numpy as np
# 3行4列の配列を作成
data = np.array([
[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12]
])
# axis=0で列ごとの合計を計算
column_sum = np.sum(data, axis=0)
print("元の配列:")
print(data)
print("列ごとの合計 (axis=0):")
print(column_sum)
元の配列:
[[ 1 2 3 4]
[ 5 6 7 8]
[ 9 10 11 12]]
列ごとの合計 (axis=0):
[15 18 21 24]
実行結果を見ると、出力された配列の要素数は「4」になっており、元の行列の列数と一致していることがわかります。
これは、1列目の「1, 5, 9」が足されて15、2列目の「2, 6, 10」が足されて18というように、垂直方向(縦方向)に集計が行われたことを示しています。
データ分析の現場では、各列が異なる変数(身長、体重、年齢など)を表している場合、このaxis=0を使って各変数の平均や最大値を求めることが非常に多いです。
例えば、クラス全体のテスト結果が格納された行列において、各教科ごとの平均点を出したいときは、このaxis=0を利用します。
axis=1(行ごとの集計)の仕組みと具体例
次に、axis=1を指定して「行ごと」に集計を行う方法を確認しましょう。
axis=1は「列をまたいで」計算を行うため、水平方向(横方向)にスキャンしていくイメージになります。
import numpy as np
# 同じ3行4列の配列を使用
data = np.array([
[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12]
])
# axis=1で行ごとの平均を計算
row_mean = np.mean(data, axis=1)
print("行ごとの平均 (axis=1):")
print(row_mean)
行ごとの平均 (axis=1):
[ 2.5 6.5 10.5]
この結果、出力された配列の要素数は「3」となり、元の行列の行数と一致しました。
1行目の「1, 2, 3, 4」の平均値である2.5が算出され、各行に対して独立した計算が行われています。
実務においては、個々のサンプル(個人や個体)ごとの特徴量を集計したい場合にaxis=1を多用します。
例えば、1人の生徒が受けた複数のテストの合計点や、ある日の1時間ごとの気温データからその日の平均気温を出すといったケースです。
「行ごとの処理は横方向への計算である」という点をしっかり記憶しておきましょう。
axis指定を省略した場合の挙動
NumPyの集計関数において、axis引数を指定しない場合、デフォルトではどのようになるのでしょうか。
多くの関数では、axis=Noneがデフォルト値として設定されており、これは「配列全体の全要素を対象とする」ことを意味します。
import numpy as np
data = np.array([[1, 2], [3, 4]])
# axisを指定しない場合
total_sum = np.sum(data)
print(f"全体の合計: {total_sum}")
全体の合計: 10
このように、行列の形状に関わらず、すべての数値が足し合わせられてスカラ値が返されます。
データの全体像を把握する際には便利ですが、行や列の構造を維持したい場合には、必ずaxisを明示的に指定する必要があります。
3次元以上の多次元配列におけるaxisの考え方
NumPyは2次元だけでなく、3次元以上のテンソルも扱うことができます。
2026年現在のデータ解析シーンでは、画像データ(縦・横・チャンネル)や時系列データ(時間・個体・特徴量)など、3次元以上の配列操作は日常的です。
3次元配列の場合、axis=0、axis=1に加えてaxis=2が登場します。
一般的に、3次元配列を (depth, row, column) と定義した場合、それぞれのaxisは以下の方向を指します。
| axisの値 | 対象となる方向 | 集計後のイメージ |
|---|---|---|
| axis=0 | 奥行き方向(複数の行列間) | 各行列の同じ位置にある要素同士を集計 |
| axis=1 | 縦方向(行方向) | 各行列内の列ごとの集計(2次元のaxis=0に近い) |
| axis=2 | 横方向(列方向) | 各行列内の行ごとの集計(2次元のaxis=1に近い) |
多次元になればなるほど混乱しやすくなりますが、「指定した次元のインデックスが変化するように計算を進める」と考えると理解がスムーズです。
例えば、shapeが (2, 3, 4) の配列に対して axis=0 で集計すると、最初の「2」という次元が消滅し、結果は (3, 4) の形状になります。
# 3次元配列の例
data_3d = np.ones((2, 3, 4))
# axis=0で集計(奥行きを潰す)
result_3d = np.sum(data_3d, axis=0)
print(f"元の形状: {data_3d.shape}")
print(f"集計後の形状: {result_3d.shape}")
元の形状: (2, 3, 4)
集計後の形状: (3, 4)
keepdimsオプションの重要性
集計操作を行うと、指定した軸の次元が消滅するため、元の配列と次元数が変わってしまいます。
これを防ぎ、計算後も元の次元数を維持したい場合に便利なのが keepdims=True というオプションです。
data = np.array([[1, 2, 3], [4, 5, 6]])
# keepdimsなし
res1 = np.sum(data, axis=1)
# keepdimsあり
res2 = np.sum(data, axis=1, keepdims=True)
print(f"keepdimsなしの形状: {res1.shape}")
print(f"keepdimsありの形状: {res2.shape}")
keepdimsなしの形状: (2,)
keepdimsありの形状: (2, 1)
keepdims=True を使用すると、集計された軸の要素数が「1」として残ります。
これにより、元の多次元配列と集計結果との間で「ブロードキャスト(自動的な次元補完)」を利用した計算が容易になります。
例えば、各行の要素からその行の平均値を引きたい(中心化したい)場合、形状が一致していないとエラーになりますが、keepdims=True を使えばスムーズに計算可能です。
このテクニックは、ニューラルネットワークの実装における正規化層の計算などで非常に重宝されます。
axis指定が可能な主要な関数一覧
NumPyには、axis引数を取ることができる関数が数多く存在します。
代表的なものを以下の表にまとめました。
| 関数名 | 役割 |
|---|---|
np.sum() | 合計値を算出する |
np.mean() | 算術平均を算出する |
np.max() / np.min() | 最大値 / 最小値を抽出する |
np.std() / np.var() | 標準偏差 / 分散を算出する |
np.argmin() / np.argmax() | 最小値 / 最大値を持つインデックスを取得する |
np.prod() | 要素の積を算出する |
np.any() / np.all() | 論理和(一つでもTrueか) / 論理積(すべてTrueか)を判定する |
これらの関数は、すべて同じ axis のロジックで動作します。
一度 axis=0 と axis=1 の違いを理解してしまえば、これらすべての関数を使いこなすことができるようになります。
よくあるミスと解決策
NumPyのaxis操作で最も多いエラーは、形状(shape)の不一致によるものです。
特に axis を間違えて指定すると、意図しない方向に集計が行われ、後続の行列演算で ValueError: operands could not be broadcast together といったエラーが発生します。
このようなミスを防ぐためには、計算の直後に必ず print(result.shape) を実行して、期待通りの形状になっているかを確認する癖をつけましょう。
また、行ごとの集計なのか列ごとの集計なのか迷ったときは、「結果として得たい配列の要素数はいくつか」を考えてみてください。
「各行の結果が欲しい」のであれば、結果の要素数は「行数」と一致するはずです。
そのためには「列(axis=1)」を潰す必要がある、という論理構成で導き出すことができます。
また、Pandasライブラリとの併用時にも注意が必要です。
Pandasの drop メソッドなどでは axis=1 が列の削除を指しますが、NumPyの集計と感覚的に一致しているものの、操作の種類によっては混乱を招くことがあります。
常に「どの軸に沿って操作が行われているか」を意識することが、バグの少ないコードを書く秘訣です。
まとめ
NumPyにおける「axis」は、多次元配列を効率的に処理するための羅針盤のような存在です。
2次元配列において、axis=0は列ごとの集計(縦方向)であり、axis=1は行ごとの集計(横方向)であることを正しく理解しましょう。
また、多次元配列へ応用する際は、「指定した軸が潰れて消える」というルールを思い出すことで、次元の迷子になるのを防ぐことができます。
さらに、keepdims オプションや各種統計関数の特性を組み合わせることで、複雑なデータ加工もシンプルに記述できるようになります。
データ分析や機械学習の実装において、NumPyの軸操作は避けては通れない道です。
本記事で紹介したコードを実際に手元で動かしながら、感覚的にaxisをマスターしていってください。
正確なaxis操作を身につけることで、あなたのPythonプログラミングの効率は飛躍的に向上するはずです。
