Python | データ構造強化:リストの二次元構造

Python Python
スポンサーリンク

概要(二次元リストは「表形式(行×列)」を素直に表す入れ子構造)

二次元リストは「リストの中にリスト」を入れる構造で、表や行列のようなデータを自然に扱えます。行は外側リストの要素、列は内側リストの要素として表現します。作成・アクセス・反復・転置・平坦化・追加更新まで、基本と落とし穴(特に初期化時の誤ったコピー)を押さえると、実務の表操作が短く安全に書けます。

# 3行×3列の例
matrix = [
    [1, 2, 3],   # 行0
    [4, 5, 6],   # 行1
    [7, 8, 9],   # 行2
]
Python

基本の使い方(ここが重要)

要素アクセスと更新(行→列の順でインデックスを指定)

print(matrix[0])      # 行0 → [1, 2, 3]
print(matrix[1][2])   # 行1・列2 → 6

matrix[2][0] = 10     # 行2・列0を更新
Python

行ごとの処理は「外側ループが行、内側ループが列」という順序が基本形です。

for r, row in enumerate(matrix):
    for c, val in enumerate(row):
        print(r, c, val)
Python

正しい初期化(内包表記で“独立した行”を作る)

「同じ行オブジェクトの再利用」は罠です。必ず内包表記で各行を新規作成します。

rows, cols = 3, 4

# 正しい:各行が独立
grid = [[0 for _ in range(cols)] for _ in range(rows)]

# 誤り:全行が同じリストを参照(変更が全行へ波及)
bad = [[0] * cols] * rows
bad[0][0] = 99
print(bad)  # 全行の先頭が 99 に変わる
Python

行や列の抽出・転置・平坦化(定番操作を一行で)

# 列の抽出(列cを取り出す)
c = 1
col = [row[c] for row in matrix]   # [2, 5, 8]

# 転置(行⇔列の入れ替え)
T = list(map(list, zip(*matrix)))  # [[1,4,7],[2,5,8],[3,6,9]]

# 平坦化(一次元へ)
flat = [val for row in matrix for val in row]  # [1,2,3,4,5,6,7,8,9]
Python

重要ポイントの深掘り(サイズ安全性・コピー・追加更新の作法)

サイズの整合性(“長さが揃っているか”を前提にする)

転置・列抽出・列追加は「全行の列数が同じ」前提で動きます。整合チェックを簡潔に入れて事故を防ぎます。

def is_rectangular(M) -> bool:
    return len(set(len(row) for row in M)) <= 1

assert is_rectangular(matrix), "行ごとに列数を揃えてください"
Python

シャローコピーと防御的コピー(入れ子なら“行単位”で独立させる)

外側だけのコピーでは行が共有されます。編集対象の階層だけ手動コピーして副作用を避けます。

# 外側のコピーだけだと行の共有が残る
shallow = matrix.copy()

# 行単位でコピー(一次入れ子の独立)
safe = [row[:] for row in matrix]
safe[0][0] = 999
print(matrix[0][0])   # 変わらない
Python

完全独立が必要なら deepcopy。ただし重いので必要最小限が原則です。

行・列の追加更新(列追加は“全行へ”一貫して行う)

M = [[1, 2], [3, 4]]

# 行の追加(1行分の長さを揃える)
M.append([5, 6])

# 列の追加(全行へ同じ位置で追加)
new_col = [10, 20, 30]                 # 行数と一致させる
for row, v in zip(M, new_col):
    row.append(v)
Python

列削除・挿入も「全行へ同じ操作」を行います。位置ずれが最も壊れやすいバグです。

高コスト処理の前取り(高速化の基本)

内側ループで何度も呼ぶ関数や属性参照はループ外で束縛し、キー計算・検証は先に済ませます。行列サイズが大きいと差が出ます。

# 例:小文字化して比較する前処理
lower = str.lower
targets = [[lower(x) for x in row] for row in matrix_of_strings]
Python

実務での使いどころ(表操作・グリッド・検証・交換)

表形式データの加工(列の並べ替え・選択)

rows = [
    ["name", "score", "age"],
    ["alice", 85, 25],
    ["bob",   92, 30],
]
# 列順を並べ替え(name, age を選択)
order = [0, 2]
reduced = [[row[i] for i in order] for row in rows]
Python

ヘッダ行がある場合はヘッダに基づいて order を作ると壊れにくくなります。

グリッド演算(近傍・境界チェック)

grid = [
    [1, 0, 1],
    [0, 1, 0],
    [1, 0, 1],
]
H, W = len(grid), len(grid[0])

def neighbors(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 grid[nr][nc]

print(list(neighbors(1, 1)))  # 十字近傍
Python

境界条件のガードを先に入れると例外を防げます。

行や列の入れ替え(スワップ)

# 行の入れ替え
r1, r2 = 0, 2
matrix[r1], matrix[r2] = matrix[r2], matrix[r1]

# 列の入れ替え(全行へ適用)
c1, c2 = 0, 2
for row in matrix:
    row[c1], row[c2] = row[c2], row[c1]
Python

例題で身につける(定番から一歩先まで)

例題1:安全な 0 初期化とインデックスで更新

rows, cols = 3, 3
M = [[0 for _ in range(cols)] for _ in range(rows)]
M[1][2] = 7
print(M)  # [[0,0,0],[0,0,7],[0,0,0]]
Python

例題2:列抽出・転置・平坦化の一連操作

M = [[1,2,3],[4,5,6],[7,8,9]]
col1 = [row[1] for row in M]                 # [2,5,8]
T    = list(map(list, zip(*M)))              # 転置
flat = [v for row in M for v in row]         # 平坦化
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:行フィルタと列選択(条件付き抽出)

rows = [
    ["name","score","age"],
    ["alice",85,25],
    ["bob",92,30],
    ["cara",70,28],
]
# ヘッダを分離
header, data = rows[0], rows[1:]
# score >= 80 の行だけ、name と age を抽出
score_idx = header.index("score")
name_idx  = header.index("name")
age_idx   = header.index("age")

result = [[row[name_idx], row[age_idx]] for row in data if row[score_idx] >= 80]
print(result)  # [['alice',25], ['bob',30]]
Python

まとめ

二次元リストは「表形式」を素直に表す入れ子構造です。最重要ポイントは、初期化で各行を独立に作る(内包表記を使う)、転置や列操作は“列数の整合性”が前提、編集時はコピーの階層を意識して副作用を避けること。列抽出・転置・平坦化は一行で書け、列追加・入れ替えは「全行へ同じ操作」を適用します。性能は前処理と参照の前取りで十分に改善できます。大規模・数値中心なら NumPy の行列も検討しつつ、まずは標準の二次元リストを安全に扱えるようにしておくと、現場の表操作が短く、読みやすく、堅牢になります。

タイトルとURLをコピーしました