HOWTO · Python
Python のルックアップテーブル
このチュートリアルでは、Python におけるルックアップテーブルの使用方法を示し、辞書、リスト、NumPy 配列、Pandas DataFrame などのメソッドを強調しています。プログラミングスキルを最適化し、アプリケーションのパフォーマンスを向上させるための効率的なデータ取得技術を実装する方法を学びます。Python におけるルックアップテーブルをマスターするための実用的なコード例と詳細な説明を探求してください。
このページの内容
ルックアップテーブルは、キーや位置を使ってコードから値を取り出せるように値を保存する仕組みです。商品コードと価格の対応付けのように、同じ関係を繰り返し参照するときに役立ちます。結果を事前に計算すれば同じ計算を繰り返さずに済みますが、テーブルにはメモリが必要で、元データが変わったときは更新しなければなりません。
キーの形と必要な処理に合わせて構造を選びます。辞書はハッシュ可能な任意のキーを受け取り、リストと配列は整数の位置で値を参照します。DataFrame は名前付きの列と表形式の操作に対応します。以下の例では、それぞれの構造で実際に値を検索する方法を示します。
データに合ったルックアップテーブルの構造を選ぶ
まず、値を何で特定するかを考えます。連続した整数位置ではない名前やコードなどのキーには辞書を使います。位置が0から始まる連続した整数で、値を Python のシーケンスに格納できるならリストが適しています。データが数値で、配列演算も必要なら NumPy 配列を使います。各レコードに複数の名前付きフィールドがある場合や、絞り込みなどの表形式の操作が必要な場合は Pandas DataFrame を使います。NumPy と Pandas はサードパーティ製の依存パッケージです。例を使う前に、プロジェクトの環境にインストールしてください。
キーによる検索には辞書を使う
辞書は、それぞれ異なるハッシュ可能なキーと値を対応付けます。連番ではないキーに適しており、get を使えば代替値を明示できます。キーがない場合、get は指定した既定値を返します。一方、角括弧でアクセスすると、キーが存在しないときに KeyError が発生します。
次の例を実行してください。出力は 2.5 と not listed です。not listed はコード内の代替メッセージで、英語のまま表示されます。
prices = {"tea": 2.5, "coffee": 3.0, "cocoa": 2.75}
print(prices["tea"])
print(prices.get("juice", "not listed"))
2.5
not listed
辞書の検索は平均的には O(1) ですが、あらゆる処理で保証されるわけではありません。キーはハッシュ可能である必要があり、既存のキーに値を代入すると以前の値が置き換わります。元のレコードが変わった場合は、検索結果が古くならないよう辞書も更新してください。
密な整数インデックスにはリストを使う
有効なインデックスが0から始まる連続した整数なら、リストはシンプルなテーブルになります。インデックス i は (i + 1) 番目の項目を指します。顧客番号のような疎な識別子には向きません。番号の欠けた部分に未使用の要素を用意するか、別の対応表が必要になるためです。リストのインデックス参照は O(1) で、格納される値は通常の Python オブジェクトです。
例の出力は gold と index out of range です。これらはコード内の文字列で、英語のまま表示されます。
labels = ["bronze", "silver", "gold"]
def label_for(index):
if 0 <= index < len(labels):
return labels[index]
return "index out of range"
print(label_for(2))
print(label_for(5))
gold
index out of range
明示的な範囲チェックにより、0未満のインデックスとリストの長さ以上のインデックスの両方を処理できます。Python のリストでは負のインデックスを使って末尾から参照できるため、テーブルのキーが0以上の位置に限られる場合は、先に検証してください。範囲外の場合に返す index out of range は、コード内の英語の文字列です。
数値データには NumPy 配列を使う
テーブルに数値が含まれ、ベクトル化計算、スライス、その他の配列操作も必要な場合は、NumPy 配列が役立ちます。NumPy は別途インストールしてください。Python の標準ライブラリには含まれていません。単一の値を検索する操作はインデックスによる取得なので、配列が辞書より常に速いとは限りません。
この例では有効なインデックスを使っているため、index out of range の分岐には入りません。無効なインデックスに対するそのメッセージは、コード内の英語の文字列です。
このセクションのコードと表示されるラベルや出力は英語のままです。
import numpy as np
rates = np.array([1.0, 1.5, 2.0, 2.5])
index = 2
if 0 <= index < len(rates):
print(rates[index])
else:
print("index out of range")
print((rates * 2).tolist())
2.0
[2.0, 3.0, 4.0, 5.0]
外部入力からインデックスを受け取る場合、範囲チェックが役立ちます。NumPy 配列は通常、単一の要素型を使うため、数値演算に便利です。格納するデータに合う要素型と形状を選んでください。
表形式のデータには Pandas DataFrame を使う
名前付きの列を持つレコードには DataFrame が適しています。特に、検索がより大きな絞り込みや分析の一部である場合に便利です。Pandas は別途インストールしてください。列による絞り込みは行を比較し、行だけを選択した場合は DataFrame を返します。以下の式では .loc で単一の price 列を選択するため、戻り値は Series です。この絞り込みでは全行を調べる場合があります。キー検索を繰り返すなら、意味のあるインデックスを設定して .loc を使う方法も検討してください。
この実行で表示されるのは 2.5 のみです。not found はコードにある代替メッセージで、一致するコードがない場合に使われます。変更されていないコード内では英語のままです。
import pandas as pd
items = pd.DataFrame({
"code": ["A", "B", "C"],
"price": [1.25, 2.50, 3.75],
})
match = items.loc[items["code"] == "B", "price"]
if not match.empty:
print(match.iloc[0])
else:
print("not found")
2.5
この方法では、一致するコードがない場合に空の結果をインデックス参照せずに処理できます。コードが一意で、繰り返し検索するなら、items.set_index("code").loc["B", "price"] でラベルによる検索を表せます。アクセス前にコードの存在を確かめ、重複ラベルをどう扱うかも決めてください。
キーの欠落と無効なインデックスを処理する
呼び出し側に合った欠損値の扱い方を選びます。dict.get(key, default) は代替値を返し、key in mapping を使うと、キーがない場合と、格納値がたまたま代替値と同じ場合を区別できます。キーの欠落が周囲の処理で例外に当たるなら、try/except KeyError を使います。リストや配列で負の位置を末尾からの参照として扱わない場合は、0 <= index < len(table) に照らしてインデックスを検証してください。DataFrame の絞り込みでは、最初の行を選ぶ前に結果が空でないか確認します。呼び出し側が欠損時に番兵値、既定値、エラーのどれを受け取るのか分かるよう、関数間で方針を統一してください。
性能と実用上のトレードオフを比較する
辞書のキー検索は平均的には O(1) で、リストや1次元配列の位置参照は O(1) です。DataFrame の真偽値による絞り込みは行を検査するため、一般に O(n) です。インデックスを使えばラベルによる参照ができますが、コストや挙動はインデックスの作成方法とデータの形に左右されます。これらの計算量だけでは、あらゆる入力サイズでの実時間は予測できません。準備コスト、キーの型、データ配置、ライブラリの処理、メモリなども影響します。
キーと必要な処理に合う、最もシンプルな構造を選びます。任意のキーには辞書、密な位置にはリスト、数値配列の処理には NumPy 配列、ラベル付きの表形式の処理には DataFrame が適しています。性能が判断材料になる場合は、実際の利用状況に近いデータで計測してください。メモリも考慮します。辞書はキーと値の対応を保持し、リストは Python オブジェクトへの参照を保持し、NumPy 配列は型付きの要素を格納します。実際の使用量はデータと実装によって異なります。検索テーブルが変化する元データから作られている場合は、古い結果を返さないよう、そのデータが変わるたびにテーブルを再構築するか更新してください。