Python | データ構造強化:zip のアンパック

Python Python
スポンサーリンク

概要(zip のアンパックは「行と列をひっくり返す」基本テクニック)

zip は複数のイテラブルを「対応する位置でペア化」する関数です。アンパック(*演算子)を組み合わせると、zip で作ったペアを「元の列」へ戻したり、2次元データの行列転置を一行で実現できます。要点は「zip は最短の長さに揃える」「アンパックは“イテラブルを個別の引数に展開する”」の2つ。これを体に入れると、データ整形が驚くほど短く安全に書けます。

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

ペアのリストを“元の2列”へ戻す(アンパックで解凍)

zip が作ったペア(タプル)の並びを、*でアンパックして zip へそのまま渡すと「列の分解(unzip)」ができます。

pairs = [("alice", 25), ("bob", 30), ("charlie", 35)]
names, ages = zip(*pairs)
print(names)  # ('alice', 'bob', 'charlie')
print(ages)   # (25, 30, 35)
Python

タプルで返るので、必要なら list(…) に変換します。

行列の転置(2次元リストの行⇔列入れ替え)

「行のリスト」をアンパックして zip に渡すと、列方向で束ねられます。一行で転置が完了します。

matrix = [
    [1, 2, 3],
    [4, 5, 6],
]
transposed = list(zip(*matrix))
print(transposed)  # [(1,4), (2,5), (3,6)]
Python

元がリストなら、列もリストで欲しい場合は map(list, …) を組み合わせます。

transposed_lists = list(map(list, zip(*matrix)))
Python

複数列の同期処理(zip で並列、アンパックで分解)

zip で同時に処理した後、結果をアンパックして別々の列へ戻せます。

names = ["alice", "bob", "charlie"]
scores = [70, 85, 92]
paired = list(zip(names, scores))   # 並列処理しやすい形
# ... 何か処理した後で分解
new_names, new_scores = zip(*paired)
Python

重要ポイントの深掘り(長さの扱い・一回性・型整形)

zip は「最短の長さ」に揃える(余りは捨てられる)

zip(a, b) は、短い方の要素数に合わせて打ち切られます。欠損を埋めたいなら itertools.zip_longest を使います(埋め値 fillvalue を指定)。

from itertools import zip_longest

a = [1, 2, 3]
b = ["x", "y"]
print(list(zip(a, b)))                       # [(1, 'x'), (2, 'y')]
print(list(zip_longest(a, b, fillvalue=None)))  # [(1,'x'), (2,'y'), (3,None)]
Python

転置やアンパックでは「列長が揃っている」ことが前提になるため、前処理で合わせるのが安全です。

zip オブジェクトは「一度きり」。必要なら list へ

zip(…) は遅延生成のイテレータです。一度反復すると使い切るため、後で再利用するなら list(…) にしておきます。

z = zip([1, 2], ["a", "b"])
print(list(z))  # 使い切る
# print(list(z))  # 空(再利用不可)
Python

アンパック結果はタプルになる。用途に応じて変換する

アンパック+zip の戻りはタプル列です。ループに流すだけならそのままで十分、更新が必要なら list へ変換します。

cols = list(zip(*[[1,2,3],[4,5,6]]))    # 列タプルのリスト
cols_lists = [list(c) for c in cols]    # 列をリスト化
Python

実務での使いどころ(転置・整形・マージ)

CSV の列⇔行変換(ヘッダ付きデータの転置)

ヘッダ行とデータ行の「列ごとの集計」や「縦持ち・横持ちの変換」に役立ちます。

rows = [
    ["name", "alice", "bob"],
    ["score", 70, 85],
    ["age", 25, 30],
]
cols = list(zip(*rows))   # 列方向へ
print(cols)
Python

辞書化・結合の前処理(キー列と値列の合体/分解)

zip でキーと値を結合して辞書へ、アンパックで列へ戻して検証や再構成ができます。

keys = ["id", "name", "age"]
vals = [101, "alice", 25]
d = dict(zip(keys, vals))
# 後で列へ戻す例
kk, vv = zip(*d.items())
Python

同時ソートや並列加工(複数列を一緒に並べ替え)

列を zip で束ねてソートし、アンパックで列へ戻すと「対応関係を保ったまま」並べ替えできます。

names = ["alice", "bob", "charlie"]
scores = [92, 85, 70]

paired = sorted(zip(scores, names), reverse=True)  # スコア降順
sorted_scores, sorted_names = zip(*paired)
print(sorted_names)  # ('alice','bob','charlie')
Python

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

例題1:ペア配列を列へ分解(unzip)

pairs = [(1, "one"), (2, "two"), (3, "three")]
nums, words = zip(*pairs)
print(nums)   # (1, 2, 3)
print(words)  # ('one','two','three')
Python

例題2:行列転置を一行で

M = [[1, 2, 3],
     [4, 5, 6],
     [7, 8, 9]]
T = list(map(list, zip(*M)))
print(T)  # [[1,4,7],[2,5,8],[3,6,9]]
Python

例題3:列を保ったソートとアンパック

names  = ["A","B","C","D"]
heights= [160,170,165,155]
paired = sorted(zip(heights, names))   # 身長で昇順
heights_s, names_s = zip(*paired)
print(list(names_s))  # ['D','A','C','B']
Python

例題4:長さ不一致を zip_longest で安全転置

from itertools import zip_longest

rows = [
    ["name", "alice", "bob"],
    ["score", 70],
]
cols = list(zip_longest(*rows, fillvalue=None))
print(cols)  # 列長不一致でも転置可能
Python

まとめ

zip は「複数列を対応位置で束ねる」、アンパックは「束ねたものを列にほどく」。この組み合わせで、行列転置、ペアの分解、対応関係を保った並列加工とソートが一気に短く、正確に書けます。必須の注意点は、zip が最短列に合わせて打ち切ること、zip オブジェクトは一回きりなこと、アンパックの戻りがタプルであること。長さ不一致には zip_longest、列の型整形には map(list, …) を使い分ける。まずは「zip で束ねる→*で解く」を標準手段として手に馴染ませましょう。

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