Pythonで辞書のデータに対して計算(最小値・最大値・ソート)を行う方法
はじめに:やりたいこと
Pythonで辞書型のデータを扱っていると、「最小値や最大値を求めたい」「値で並べ替えたい」といった計算処理を行いたい場面が出てきます。しかし、辞書はキーと値のペアで構成されているため、リストのように単純に計算関数を適用しても、意図した結果にならないことがあります。
本記事では、テニス選手のグランドスラム優勝回数を例に、辞書に対して正しく計算を行うための方法を段階的に解説します。
サンプルデータの準備
まず、選手名をキー、グランドスラム優勝回数を値とする辞書を作成します。
PlayerTitles = {
'Federer': 20,
'Nadal': 20,
'Djokovic': 17,
'Murray': 3,
'Theim': 1,
'Zverev': 0
}
ステップ1:min()をそのまま使うと何が起きるか
優勝回数が最も少ない選手を調べるために、辞書に直接min()を適用してみます。
#type(PlayerTitles)
print(f"Output \n*** The minimum value in the dictionary is {min(PlayerTitles)} ")
出力:
*** The minimum value in the dictionary is Djokovic
ステップ2:values()メソッドで値に対して計算する
おそらくこれは期待した結果ではないでしょう。min()を辞書にそのまま適用すると、デフォルトではキーが比較対象になるためです。実際には辞書の値に対して計算を行いたいので、values()メソッドを使って修正しましょう。
print(f"Output \n*** The minimum value in the dictionary is {min(PlayerTitles.values())} ")
出力:
*** The minimum value in the dictionary is 0
ステップ3:値だけでなく対応するキーも知りたい
しかし、これでもまだ十分とは言えません。例えば、「最小値に対応するキー」、つまり優勝回数が最も少ない選手名を知りたい場合もあるでしょう。
ステップ4:key引数を使って最小値・最大値のキーを取得する
min()やmax()にkey引数として関数を渡すことで、最小値・最大値に対応するキーを取得できます。
print(f"Output \n***{min(PlayerTitles, key=lambda k: PlayerTitles[k])} ")
出力:
***Zverev
ステップ5:最小値そのものを取得するには追加の参照が必要
ただしこの方法で最小値そのものを取得するには、もう一度辞書を参照する必要があります。
min_titles = PlayerTitles[min(PlayerTitles, key=lambda k: PlayerTitles[k])]
print(f"Output \n***{min_titles} ")
出力:
***0
ステップ6:zip()で辞書を(値, キー)ペアに「反転」する
よりスマートな解決策がzip()を使った方法です。辞書を「反転」して、(値, キー)のペアからなるシーケンスを作ります。タプル同士の比較では、まず最初の要素(値)が比較され、それが同じ場合にのみ次の要素(キー)が比較されます。
このアプローチにより、まさに望む動作が実現でき、たった1つのステートメントで辞書の中身に対する集約処理やソートが簡単に行えるようになります。
min_titles = min(zip(PlayerTitles.values(), PlayerTitles.keys()))
max_titles = max(zip(PlayerTitles.values(), PlayerTitles.keys()))
print(f"Output \n***{min_titles , max_titles} ")
出力:
***((0, 'Zverev'), (20, 'Nadal'))
ステップ7:sorted()と組み合わせてランキングを作る
同様に、データをランキング形式で並べたい場合は、zip()とsorted()を組み合わせます。
titles_sorted = sorted(zip(PlayerTitles.values(), PlayerTitles.keys()))
print(f"Output \n***{titles_sorted} ")
出力:
***[(0, 'Zverev'), (1, 'Theim'), (3, 'Murray'), (17, 'Djokovic'), (20, 'Federer'), (20, 'Nadal')]
ステップ8:注意点① zip()のイテレータは一度しか使えない
これらの計算を行う際は、zip()が返すのは一度しか消費できないイテレータであることに注意してください。
titles_and_players = zip(PlayerTitles.values(), PlayerTitles.keys())
print(f"Output \n***{min(titles_and_players)} ")
出力:
***(0, 'Zverev')
ステップ9:再利用するとエラーになる
同じイテレータに対して再度min()などを呼び出すと、シーケンスが空になっているためValueError例外が発生します。繰り返し使いたい場合は、list()で事前にリスト化しておくと安全です。
ステップ10:注意点② 値が重複した場合の挙動
(値, キー)ペアを使った計算では、複数のエントリが同じ値を持つ場合、キーが結果の決定に使われる点にも留意が必要です。
例えばmin()やmax()のような計算では、値が重複しているとき、キーが最小または最大のエントリが返されます。ステップ7の出力を見ると、FedererとNadalはどちらも20回ですが、キーの比較では'Nadal'の方が大きいため、最大優勝回数の選手としてはNadalだけが返されるのです。
まとめ
辞書に対する計算では、以下のポイントを押さえておきましょう。
- 辞書にmin()/max()を直接適用すると、キーが比較対象になる
- 値だけが必要ならvalues()メソッドを使う
- キーと値の両方が必要なら、key引数(lambda)を使う方法と、zip()で(値, キー)ペアに変換する方法がある
- zip()のイテレータは使い捨てなので、再利用する場合はlist()化しておく
- 値が重複する場合はキーが比較に使われることを理解しておく
-
Python Pandas入門:マイクロ秒単位の頻度でDateTimeIndexにフロア(切り下げ)操作を実行する方法
DateTimeIndexに対してマイクロ秒単位の頻度でフロア(切り下げ)操作を実行するには、DateTimeIndex.floor()メソッドを使用します。マイクロ秒の頻度を指定する場合は、freqパラメータにusを設定します。floor()メソッドは、指定した頻度より細かい単位の値を切り捨てることで、日時データをきれいな間隔に丸めることができます。時系列データの前処理や集計において非常に便利な機能です。必要なライブラリのインポートまず、必要なライブラリをインポートします。import pandas as pdDateTimeIndexの作成次に、期間5、頻度40秒(40S)のDatetim
-
PythonのBokehで画像を操作・表示する方法
Pythonの可視化ライブラリBokehで画像を扱うには、image_url()メソッドを使い、画像URLのリストを渡すのが基本です。このメソッドを使うことで、ローカルやWeb上にある画像をプロット領域内に簡単に配置できます。実装の手順output_file()でデフォルトの出力先を設定し、show()を呼び出したときに結果がHTMLファイルとして保存されるようにします。figure()でプロット用の新しいFigure(図)オブジェクトを作成します。image_url()メソッドで、指定したURLから読み込んだ画像を描画します。show()を呼び出して、Bokehオブジェクトやアプリケーション