カテゴリー

Python基礎

【max(), sorted()&key】max関数とかで使うkeyを活用

2021年10月3日

こんな人にオススメ

pythonのmax関数とかで出てくるkeyって引数の役割って何?わざわざ使う必要ってあるの?

ということで、今回はpythonのmax()sorted()で使用される引数keyについて解説する。あまり使い所がないかもしれないけど、いざというときに役立つのかもしれない。

python環境は以下。

  • Python 3.9.7
  • numpy 1.21.2

運営者のメガネです。YouTubeTwitterInstagramも運営してます。

自己紹介はこちらから、お問い合わせはこちら。

運営者メガネ

作成したコード全文

下準備

import numpy as np

def check(*args, func, key, **kwargs):
    ans = func(*args, key=key, **kwargs)
    return ans

まずは下準備としてのimportと今回使用する関数。numpyNaNを作成するときと平均する時のみ使用する。check関数は作成しなくてもよかったけど、各関数を一括で見やすくなると思い定義した。

この関数の可変長引数argsに入れた要素に対して、funcで実行したい関数(maxとかsortedとか)をkeyの条件で適用する。また、追加で引数が欲しい場合はkwargsで追加できるようにした。

【plotly&kwargs】グラフ作成時の設定を後から追加できるように

こんな人にオススメ plotlyでグ} ...

続きを見る

文字列の長さを勘定

まずはkey=lenとして文字列の長さを勘定する。for文の内包表記を使用してもいいし他の方法を使用しても可能だけど、keyを使うことでスッキリ書くことができる。

シンプルに文字列の長さを勘定

# 数値文字列の中で検索
lst = ['123', '123456', '1234']
print(lst)
# ['123', '123456', '1234']

# 一番長い文字列
ans = check(lst, func=max, key=len)
print(ans)
# 123456

# 一番短い文字列
ans = check(lst, func=min, key=len)
print(ans)
# 123

ここでは文字列に数値を選択した。数値といっても文字にしてあるので長さを数えることは可能。maxを使用することで一番長い文字列を、minでは一番短い文字列を出力することができる。

maxの場合だと123456minだと123が出力される。シンプル。

複数要素を入れて勘定してもいい

# 単純な文字列でも可能
a = 'hoge'
b = 'foo'

ans = check(a, b, func=max, key=len)
print(ans)
# hoge

ans = check(a, b, func=min, key=len)
print(ans)
# foo

# 要素は増えてもいい
c = 'hogefoohoge'
ans = check(a, b, c, func=max, key=len)
print(ans)
# hogefoohoge

さっきは変数lstに全ての要素を入れて長さを計ったけど、もちろんバラバラに指定してもいい。*argsを使ったのもそのため。max(a, b, c)と同じ意味。

ここでは文字列3種類を使って文字列の長さを判定している。

数値の文字列と文字の組み合わせも可

print(lst + [c])
# ['123', '123456', '1234', 'hogefoohoge']

ans = check(lst + [c], func=max, key=len)
print(ans)
# hogefoohoge

ans = check(lst + [c], func=min, key=len)
print(ans)
# 123

もちろん文字列なので、文字列の数値と単なる文字列を組み合わせてもいい。

数値が入っている場合の挙動

今度は文字列ではない、単なる数値が入っているときの挙動について解説。文字列の数字と文字列ではない数字(ここでは数値とする)では意味合いが全く異なるので注意。

数値だと長さは測れない

lst = ['1', 20, '100']
print(lst)
# ['1', 20, '100']

# # intが入っているとエラー
# ans = check(lst, func=max, key=len)
# # TypeError: object of type 'int' has no len()

そもそも数値の場合は長さという概念がない。ということで、key=lenで長さを計ろうとするとエラーとなる。これは小数の場合でも同様。

# 小数も同様
lst = ['1', '20', 100.1]
print(lst)
# ['1', '20', 100.1]

ans = check(lst, func=max, key=len)
# TypeError: object of type 'float' has no len(

keyint, floatにすると大小の比較が可能

# 文字列の数字を変換すると、100.1が一番大きい
lst = ['10', 20, 100.1, '001']
ans = check(lst, func=max, key=int)
print(ans)
# 100.1

# 001は数値に変換すると1になるので一倍小さい
ans = check(lst, func=min, key=int)
print(ans)
# 001

一方で、数値の場合だとkey=intが使える。intにすると文字列の数字を全て数値に変換して、funcで指定した処理を実行してくれる。上の例だと最大値と最小値を計算してくれる。

'001'に関してはintにすると1になるので、最小値で出力されるようになる。したがって、0.1を配列に追加すると最小値は'001'から0.1になる。これは最大値の場合でもそう。

# 0.1を追加すると0.1が一番小さくなる
ans = check(lst + [0.1], func=min, key=int)
print(ans)
# 0.1

# '1200'を追加すると1200が一番大きい
ans = check(lst + ['1200'], func=max, key=int)
print(ans)
# 1200

なお、文字列をそのまま入れると、これは数値に変換することができないのでエラー。

# 文字列が入るとintは使えない
ans = check(lst + ['a'], func=min, key=int)
# ValueError: invalid literal for int() with base 10: 'a'

また、intで比較するので100.1100.5も同じ100となる。しかし、最初に当てはまった要素が出力されるのでintの場合は100.1が出力される。

一方でkey=floatにすると小数も反映されるので、この場合は100.5が最大となる。

# intで比較するので、100.1も100.5も100となる
# しかし、出力されるのは初めに見つけたもの
ans = check(lst + [100.5], func=max, key=int)
print(ans)
# 100.1

# key=floatにすると小数も含めての判定になるので100.5が一番大きい
ans = check(lst + [100.5], func=max, key=float)
print(ans)
# 100.5

NaNが入っていても大丈夫

lst = ['10', 20, 100.1, np.nan]
print(lst)
# ['10', 20, 100.1, nan]

要素の中にNaNが入っている場合はどうだろうか。NaNfloatの仲間なのでintにできない。したがって、key=intはエラー。

# NaNはfloatなのでintにできない
ans = check(lst, func=max, key=int)
# ValueError: cannot convert float NaN to integer

floatにすると解決する。変にNaNが出力されることはない。なので、NaNが出て欲しいなら予めNaNを検出するようにnp.nanなどを実行する必要がある。

# key=floatにすると解決
ans = check(lst, func=max, key=float)
print(ans)
# 100.1

ans = check(lst, func=min, key=float)
print(ans)
# 10

sortedに適用

lst = ['hoge', 'foo', 'hogefoohoge']
ans = check(lst, func=sorted, key=len)
print(ans)
# ['foo', 'hoge', 'hogefoohoge']

funcsortedにすることも可能。sortedにすると昇順で並び替えられる。並び替えの方法をkeyで指定する。上の例では文字列の長さを基準に並び替えを行なっている。

もちろん降順にすることも可能。その場合は逆順を示すreverse=Trueにする必要がある。

# 逆順も可能
ans = check(lst, func=sorted, key=len, reverse=True)
print(ans)
# ['hogefoohoge', 'hoge', 'foo']

2次元配列で適用

今までは1次元配列での適用だったが、2次元配列の場合はどうだろうか。2次元配列となると、合計値を出せたりどの要素を基準にするかといった要素が絡んでくるのでややこしくなる。

合計値で検索

lst = [[3, 7], [8, 5], [2, 9], [0, 10, 3]]
print(lst)
# [[3, 7], [8, 5], [2, 9], [0, 10, 3]]

# 各listの合計値で判定
# 該当する要素が複数個ある場合は初めの要素が出力
ans = check(lst, func=max, key=sum)
print(ans)
# [8, 5]

合計値で判断するならkey=sumとすればいい。上の場合だと[8, 5][0, 10, 3]がともに合計値13で最大だが、初めにヒットするのは[8, 5]なので、出力は[8, 5]となる。

先頭の値でソート

# 0番目=先頭の数値を基準にソート
ans = check(lst, func=sorted, key=lambda x: x[0])
print(ans)
# [[0, 10, 3], [2, 9], [3, 7], [8, 5]]

先頭の数値を基準としてソートする場合はlambda関数を使用して0番目の値を基準にするように指定する。もちろん自作関数でもいいけど、これについては後述。

x[1]とすることで、1番目の要素を基準にソートすることができる。ただし、2番目の要素となると[0, 10, 3]以外は範囲外となるためエラー。

# 1番目の数値を基準にソート
ans = check(lst, func=sorted, key=lambda x: x[1])
print(ans)
# [[8, 5], [3, 7], [2, 9], [0, 10, 3]]

# 2番目の数値を基準にソート
ans = check(lst, func=sorted, key=lambda x: x[2])
# IndexError: list index out of range

自作関数を使用

最後は自作関数を使用する方法。これまではmaxlenといった標準関数を使用しての処理だったが、平均値や余りの値で判断することも可能。

余りで判断

# 余りを出力する関数
def remainder(x):
    return x % 3

remainder関数は入力された数値の余りを出力する関数。この関数をkeyに使用して値を判断する。

# 3で割った余りが最小
lst = [6, 7, 11]  # それぞれ余りが0, 1, 2
# lambda式で書いてもいいし関数にしてもいい
ans = check(lst, func=min, key=lambda x: x % 3)
ans = check(lst, func=min, key=remainder)

print(ans)
# 6

remainder関数を使用してもいいけど、lambda式でもいい。簡単な関数かつその時にしか使用しないならlambda式でもいいだろう。

平均値で判断

def ave(x):
    return np.average(x)

平均値に関してはnumpyの平均する関数を使用。別に合計値を個数で割ってもいい。こちらも先ほど同様にkeyに適用できる。

# 平均値は2, 3, 5.5, 4.3
lst = [[3, 1], [1, 5], [2, 9], [0, 10, 3]]
# lambda式で書いてもいいし関数にしてもいい
ans = check(lst, func=max, key=lambda x: sum(x) / len(x))
ans = check(lst, func=max, key=ave)
print(ans)
# [2, 9]

サクッと使用したい時に使えそう

今回はmax関数やsorted関数の引数keyについて解説した。もちろんnumpyなどを使用すると簡単にできるのかもしれないけど、わざわざimportする必要がなかったりサクッとしたいときに使えそう。

あとはいちいちnumpyの関数の引数の使い方を調べなくてもkey=lenとすれば長さが計れるなどのお手軽さもあると思う。

関連記事

【python3.7以降&dictのkeys】ネスト(入れ子)されたdictのkeys一覧をカッコで出力

こんな人にオススメ dictとやら ...

続きを見る

【astropy&単位】astropyで変数に単位を付与

こんな人にオススメ 数値計 ...

続きを見る

【inspect&引数名取得】defのパラメータ名を取り出す

こんな人にオススメ curve_fitで得 ...

続きを見る

【python&関数化】defとかargsとかを使って関数を作成する

こんな人にオススメ pythonのdefっ ...

続きを見る

スイッチボット

2022/11/28

【SwitchBotロックレビュー】これからのスタンダードになりうるスマートロック

こんな人にオススメ SwitchBotからスマートロック「SwitchBotロック」が発売された ...

生活に役立つ

2022/11/28

【メガネ厳選】クソ便利に使っているサービスやアイテム達

このページでは執筆者「メガネ」が実際に使って便利だと感じているサ ...

マウス

2022/9/11

【Logicool MX ERGO vs MX Master 3】ERGOをメインにした決定的な理由

こんな疑問・お悩みを持っている人におすすめ 執筆者はLogicoolのハイエンӠ ...

完全ワイヤレスイヤホン(TWS)

2022/11/21

【ながら聴きイヤホン比較】SONY LinkBuds、ambie、BoCoはどれがおすすめ?

こんな人におすすめ 耳を塞がない開放型のイヤホンに完全ワイヤレスӟ ...

macOSアプリケーション

2022/10/15

【M1 Mac】MacBook Proに入れている便利でニッチなアプリを21個紹介する

こんな人におすすめ MacBookを購入してLINEとか必要最低限のアプリは入れた。 ...

完全ワイヤレスイヤホン(TWS)

2022/10/23

【SENNHEISER MOMENTUM True Wireless 3レビュー】高レベルでバランス型の高音質イヤホン

こんな人におすすめ SENNHEISER MOMENTUM True Wireless 3って実際のところどうなの? 評判は良い ...

完全ワイヤレスイヤホン(TWS)

2022/11/21

【SONY WF-1000XM4レビュー】神とゴミのハーフ&ハーフ

こんな人におすすめ SONYのフラグシップモデル「SONY WF-1000XM4」ってどれくらい性 ...

完全ワイヤレスイヤホン(TWS)

2022/8/19

【Nothing ear (1)レビュー】ライトな完成度、アップデートに期待

こんな人にオススメ 完全ワイヤレスイヤホン(TWS)でスケルトンボディ ...

Pythonを学びたいけど独学できる時間なんてない人へのすゝめ

執筆者は大学の研究室・大学院にて独学でPythonを習得した。

でも社会人になったら独学で行うには時間も体力もなくて大変だ。

時間がない社会人だからこそプロの教えを乞うのが効率的。

ここでは色んなタイプに合ったプログラミングスクールの紹介をする。

  • この記事を書いた人

メガネ

ベンチャー企業のWebエンジニア駆け出し。独学のPythonで天文学系の大学院を修了→新卒を1.5年で辞める→転職→今に至る。
常時金欠のガジェット好きでM1 MacBook Pro x Galaxy S22 Ultraの狂人。
人見知りで根暗だったけど、人生楽しもうと思って良い方向に狂う→人生が楽しい

ガジェットのレビューとPythonコードを記事にしています。ぜひ楽しんでください🦊
自己紹介と半生→変わって楽しいの繰り返し

-Python基礎
-, ,