閉じる

NumPyで「行ごと・列ごと」に集計する方法|axisの指定と使い分けを解説

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は「行をまたいで」計算を実行します。

以下のサンプルコードを用いて、実際に列ごとの合計値を算出してみましょう。

Python
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は「列をまたいで」計算を行うため、水平方向(横方向)にスキャンしていくイメージになります。

Python
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がデフォルト値として設定されており、これは「配列全体の全要素を対象とする」ことを意味します。

Python
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=0axis=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) の形状になります。

Python
# 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 というオプションです。

Python
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=0axis=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プログラミングの効率は飛躍的に向上するはずです。

URLをコピーしました!