概要(二次元リストのループは「外側=行、内側=列」を丁寧にまわすのが基本)
二次元リストは「リストの中にリスト」を持つ入れ子構造です。ループは外側で行(row)を、内側で列(value)を走査するのが基本形になります。安全で読みやすいループにするために、enumerate で行・列のインデックスを得る、サイズ整合性を確認する、境界チェックを先に入れる、重い処理の前取りを行う、といったポイントを押さえましょう。転置や列抽出、条件付きの走査、集計、近傍の走査などの定番パターンを体に入れると、実務の表操作が短く堅牢に書けます。
基本の回し方(ここが重要)
外側=行、内側=列の二重ループ
二次元リストの最もシンプルな走査は、外側で行を取り、内側で列の値を取り出す形です。行番号・列番号が必要なら enumerate を使います。
M = [
[1, 2, 3],
[4, 5, 6],
]
for r, row in enumerate(M):
for c, val in enumerate(row):
print(r, c, val)
Python行や列を一括処理したいときは、外側ループで行単位にまとめて書くと読みやすくなります。
for row in M:
s = sum(row) # 行の合計
print(s)
Python範囲ループ(range)で厳密にインデックスを管理する
インデックスを明示的に扱いたい場合は、range と len を使い、M[r][c] へアクセスします。長さ不一致があり得る場合はガードを入れます。
rows, cols = len(M), len(M[0])
for r in range(rows):
for c in range(cols):
print(r, c, M[r][c])
Python行ごとに列数が異なる可能性があるなら、行の長さで回すようにします。
for r in range(len(M)):
for c in range(len(M[r])):
print(r, c, M[r][c])
Pythonよく使う走査パターン(抽出・転置・条件・集計)
列の抽出と列方向のループ
列 c を取り出したいときは、各行から同じ列インデックスを取りに行きます。前提として列数の整合性を確認しておくと安全です。
c = 1
col = [row[c] for row in M]
for i, v in enumerate(col):
print(i, v)
Python行列の転置を使った「列ループ」
zip とアンパックで転置すると、列を行のように回せます。列もリストで欲しい場合は整形します。
T = list(map(list, zip(*M))) # 転置
for j, col in enumerate(T):
print(j, sum(col))
Python条件付き走査(スキップと早期終了)
予め軽い条件でスキップし、必要な場面では break を使って早期終了します。これだけで大きなマトリクスでも体感速度が変わります。
target = 5
found = None
for r, row in enumerate(M):
if not row: # 空行はスキップ
continue
for c, val in enumerate(row):
if val == target:
found = (r, c)
break
if found:
break
print(found)
Python行・列の集計(合計・最大・平均)
集計は「行単位」か「列単位」かを決め、内側の計算を最小化します。小さな関数に切り出すと再利用しやすくなります。
row_sums = [sum(row) for row in M]
col_sums = [sum(col) for col in zip(*M)]
print(row_sums, col_sums)
Python近傍走査と境界ガード(グリッドで強い書き方)
上下左右の近傍(4近傍)を安全に回す
境界チェックを先に入れ、ループ内の条件分岐を減らすと読みやすくなります。
H, W = len(M), len(M[0])
def neighbors4(r, c):
for dr, dc in ((-1,0), (1,0), (0,-1), (0,1)):
nr, nc = r + dr, c + dc
if 0 <= nr < H and 0 <= nc < W:
yield nr, nc, M[nr][nc]
for r in range(H):
for c in range(W):
vals = [v for _, _, v in neighbors4(r, c)]
print(r, c, vals)
Python斜めも含めた近傍(8近傍)
相対位置のリストを事前に定義し、同じ境界ガードで回せば拡張が容易です。
OFFSETS8 = [(dr, dc) for dr in (-1, 0, 1) for dc in (-1, 0, 1) if not (dr == 0 and dc == 0)]
def neighbors8(r, c):
for dr, dc in OFFSETS8:
nr, nc = r + dr, c + dc
if 0 <= nr < H and 0 <= nc < W:
yield nr, nc, M[nr][nc]
Python安全性と性能の勘所(整合チェック・コピー・前取り)
行長の整合性をチェックして事故を防ぐ
転置や列操作をする前に、全行の列数が揃っているか簡単に確認します。これだけで例外を多く回避できます。
def is_rectangular(M) -> bool:
return len({len(row) for row in M}) <= 1
assert is_rectangular(M), "行ごとに列数が異なるため、転置・列操作が安全に行えません"
Python走査中の破壊的変更を避ける(必要なら防御的コピー)
ループで参照している構造を同時に書き換えると、意図しない動作や例外の原因になります。編集が必要なら、対象階層だけコピーしてから操作します。
safe = [row[:] for row in M] # 行単位で独立
for r, row in enumerate(safe):
for c, val in enumerate(row):
safe[r][c] = val * 2
Python重い参照や関数の前取りで軽くする
ループ内で何度も呼ぶ関数や属性参照は、外で変数に束縛してから使うと高速です。キー計算や検証も先に済ませます。
lower = str.lower
normalized = [[lower(str(x)) for x in row] for row in M]
Python巨大データは「作らず流す」(ジェネレータで遅延処理)
すべての結果リストが不要なら、ジェネレータで流し先へ直接渡します。合計や最大のような集計に向いています。
def iter_values(M):
for row in M:
for v in row:
yield v
total = sum(iter_values(M))
Python例題で身につける(定番から一歩先まで)
例題1:二重ループで条件付き抽出(奇数だけ)
M = [[1,2,3],[4,5,6],[7,8,9]]
odds = []
for r, row in enumerate(M):
for c, v in enumerate(row):
if v % 2 == 1:
odds.append((r, c, v))
print(odds)
Python例題2:行フィルタと列選択(ヘッダ付き表)
rows = [
["name","score","age"],
["alice",85,25],
["bob",92,30],
["cara",70,28],
]
header, data = rows[0], rows[1:]
score_i = header.index("score")
name_i = header.index("name")
age_i = header.index("age")
result = [[row[name_i], row[age_i]] for row in data if row[score_i] >= 80]
print(result) # [['alice',25], ['bob',30]]
Python例題3:列の追加(整合チェックつき)
def add_column(M, values):
assert len(M) == len(values), "行数と追加列の長さが一致していません"
for row, v in zip(M, values):
row.append(v)
return M
M = [[1,2],[3,4],[5,6]]
add_column(M, [10,20,30])
print(M) # [[1,2,10],[3,4,20],[5,6,30]]
Python例題4:近傍合計(4近傍の合計を新しい行列に)
M = [[1,2,3],
[4,5,6],
[7,8,9]]
H, W = len(M), len(M[0])
def neighbors4_sum(r, c):
s = 0
for dr, dc in ((-1,0),(1,0),(0,-1),(0,1)):
nr, nc = r + dr, c + dc
if 0 <= nr < H and 0 <= nc < W:
s += M[nr][nc]
return s
out = [[neighbors4_sum(r, c) for c in range(W)] for r in range(H)]
print(out)
Pythonまとめ
二次元リストのループは「外側=行、内側=列」という基本形に、enumerate でのインデックス取得、範囲ループでの厳密アクセス、軽い条件の早期スキップ、break による早期終了を組み合わせると読みやすく堅牢になります。列抽出や転置、条件付き抽出、集計、近傍走査といった定番パターンを身につけ、整合チェック・防御的コピー・前取りによる高速化で安全性と性能を両立させましょう。巨大データは「作らず流す」発想も有効です。まずは小さな表で手を動かし、失敗しやすいところ(行長不一致、走査中の破壊的変更)を体で覚えるのが上達の近道です。
