1. ホーム
  2. Python

Python組み込み関数 - min関数とmax関数 - 詳細解説

2022-02-10 19:10:24

ブログ記事の主な内容は、以下の通りです。

<ブロッククオート

max関数とmin関数の使い方の紹介。

  1. 反復可能なオブジェクト要素を比較し、最大/最小を求める max(iterable, *[, default=obj, key=func])
  2. 渡された複数の引数を比較し、最大/最小値を求める(arg1, arg2, *args, *[, key=func])
  3. 複雑な構造のデータに対して、関数定義でキーを使用する方法を紹介 .

         要件 リスト内で最も出現回数の多い要素を探す。
         3つの実装をご紹介します。
             (1)max関数のキーパラメータをフレキシブルに利用できる。
             (2) 辞書の統計を使って要素を数え、手作業で関数にカプセル化する。
             (3) collections.Counter クラスを拡張し、Counter オブジェクトの most_common メソッドを使用する。

拡張機能:heapq モジュールの nlargest 関数のソースコード、collections.Counter クラスの紹介、most_common メソッドのソースコード。


max関数とmin関数の機能を調べるにはどうしたらいいのでしょうか?まずは、その関数の公式APIを読むことです   
IDEを使って関数を表示するか、インタラクティブな環境でコマンド: help(max) を入力すると、公式の関数の注釈が表示されます。

def max(*args, key=None): # known special case of max
    """
    max(iterable, *[, default=obj, key=func]) -> value
    max(arg1, arg2, *args, *[, key=func]) -> value
    
    With a single iterable argument, return its biggest item.
    default keyword-only argument specifies an object to return if
    the provided iterable is empty.
    With two or more arguments, return the largest argument.
    """
    pass

def min(*args, key=None): # known special case of min
    """
    min(iterable, *[, default=obj, key=func]) -> value
    min(arg1, arg2, *args, *[, key=func]) -> value
    
    With a single iterable argument, return its smallest item.
    default keyword-only argument specifies an object to return if
    The default keyword-only argument specifies an object to return if the provided iterable is empty.
    With two or more arguments, return the smallest argument.
    """
    pass

このように表示されるのは、一般に、2つの関数の基本的な実装がC言語プログラムであることを意味します。具体的な実装については、さらにソースコードを調査する必要があります。このブログの記事では、まずAPIを把握することから始めます。
         関数の説明から、max関数とmin関数の使い方は同じで、max関数は最大値を求めるもの、min関数は最小値を求めるものという違いだけです。ここでは、max関数とmin関数を、max関数だけを例にして説明します。

minとmaxでは、比較する要素同士が同等でないとエラーが出ることに注意してください。要素同士の比較については、以前の記事で説明しましたが、このブログ記事の最後の方では、要素同士の比較について説明をしています。ブログ記事のアドレスは データ型 - コンテナクラスのデータ型


I. 関数の2つの使用方法
1, max(iterable, *[, default=obj, key=func]) -> 値
      イテラブルです。比較する要素はイテラブルオブジェクトの中にあります。最初の位置引数はこのイテラブルオブジェクトに渡され、関数はイテラブルオブジェクトの中で最大の要素を返します。  辞書が渡された場合は、辞書のキーが比較され、これは dt.keys() を渡すことと同じである。

# Generate a distorted list of numbers 0-9 to test the max and min functions
>>> from random import shuffle
>>> data = list(range(10))
>>> shuffle(data)

>>> max(data)
9
>>> min(data)
0

    デフォルトのキーワード引数です。反復可能オブジェクトに要素がない場合、defaultキーワード引数が指定されていれば、その値が返されます。この場合、デフォルト引数が指定されていないと、プログラムはエラーを報告する。ValueError: max() arg is an empty sequence.

>>> max(range(10))
9
>>> max(range(0))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: max() arg is an empty sequence

次のスニペットは、heapqモジュールのnlargest関数のソースコードを読んでいるときに出会った、defaultキーワード引数の使用に関するものです。

def nlargest(n, iterable, key=None):
    """Find the largest n elements in the dataset
    Function equivalent to: sorted(iterable, key=key, reverse=True)[:n]
    """
    # Short-cut for n==1 is to use max()
    if n == 1:
        it = iter(iterable)
        sentinel = object()
        if key is None:
            result = max(it, default=sentinel)
        else:
            result = max(it, default=sentinel, key=key)
        return [] if result is sentinel else [result]
    pass
# Note: sentinel is the sentinel position, used to monitor if the incoming dataset is empty.
# Here it is the sentinel position that is set using the default argument of the max function to be able to return the empty list if the iterable object is empty []

2, max(arg1, arg2, *args, *[, key=func]) -> 値
    複数の引数を渡し、複数の引数を比較し、最大となる引数を求める。

>>> max(9, 1, 2, 3)
9

2つ目は、関数のデフォルトパラメーターキーの使用です。
キーは呼び出し可能なオブジェクトに渡す必要があり、一般的には関数に渡されます。keyが指定されると、max関数はkeyの処理に基づいて要素を比較します。

要件1:例えば、次のような場合、各果物の価格情報が辞書形式のリストに格納されており、最も高い果物と最も安い果物を見つけたい。

fruit_shop = [
    {'name': 'apple', 'price': 5.68},
    {'name': 'orange', 'price': 4.22},
    {'name': 'banana', 'price': 2.65}, {'name': 'orange', 'price': 4.22}, {'name': 'banana', 'price': 2.65},
    {'name': 'berries', 'price': 10.75}]

cheap = min(fruit_shop, key=lambda s: s['price'])
expensive = max(fruit_shop, key=lambda s: s['price'])
print('The cheapest fruit is:', cheap)
print('The most expensive fruit is:', expensive)

####
# The cheapest fruit is: {'name': 'banana', 'price': 2.65}
# The most expensive fruit is: {'name': 'berries', 'price': 10.75}


要件2:リスト内で最も出現回数の多い要素を探す。

>>> lt = [2, 2, 3, 1, 2, 2, 1, 8, 5]
>>> max(set(lt), key=lt.count)
2

注意:countメソッドをmax関数の引数として渡した場合、ここでのmax関数は要素の出現頻度を比較する。

1. set(lt) は、lt のリストに含まれる一意な値をすべて取得することができます。単純にリスト要素の重み付けを解除してセット(つまり集合型)を取得することができます。

>>> set(lt)

{1, 2, 3, 5, 8}

2. リストのcountメソッドは、リスト要素の出現回数を求めるものである

>>> lt.count(2)
4
>>> lt.count(1)
2

max関数を使わずに、どのように実現するのでしょうか?


最初の方法:辞書を使って手動で要素をカウントするように関数をカスタマイズする

# 要素 (キー) と要素の出現回数 (値) を数えるために、辞書 dt を使用します。
# 最も出現回数の多い要素がmax_count_keyで格納されます。
# リストの要素を繰り返し、その要素の出現回数がすでに辞書でカウントされている場合は、再度カウントしない。
                そうでない場合は、リストの count メソッドを使用して要素の出現回数をカウントし、max_count_key と比較します。

def max_count(lt):
    dt = {}
    max_count_key = None
    for i in lt:
        # Elements that already exist in the dictionary are no longer counted
        if i not in dt:
            count = lt.count(i)
            dt[i] = count
            if count > dt.get(max_count_key, 0):
                max_count_key= i
    return max_count_key





2 番目の方法(推奨)は、collections.Counter クラスを使用することです。このクラスの most_common() メソッドは、シーケンス内で最も頻繁に発生する箇所を見つけます。

反復可能なオブジェクトの要素を数え、Counter オブジェクトにキーと値のペアとして格納する collections.Counter クラスです。
Counterオブジェクトはマッピングの一部であり、dict()でCounterオブジェクトから辞書に変換することができます。

>>> from collections import Counter
>>> c = Counter('MyNameisMarsenHelloWorld')
>>> c
Counter({'e': 3, 'l': 3, 'M': 2, 'a': 2, 's': 2, 'r': 2, 'o': 2, 'y': 1, 'N': 1, 'm': 1, 'i': 1, 'n': 1, 'H': 1, 'W': 1, 'd': 1})
>>> dict(c)
{'M': 2, 'y': 1, 'N': 1, 'a': 2, 'm': 1, 'e': 3, 'i': 1, 's': 2, 'r': 2, 'n': 1, 'H': 1, 'l': 3, 'o': 2, 'W': 1, 'd': 1}

Counter クラスの most_common(n=None) メソッド:最も一般的な n 個の要素とその個数を、最も一般的なものから最も一般的でないものへとリストアップします。n が None の場合は、すべての要素がリストアップされます。また、最も一般的なものから最も少ないものへとリストアップされます。
この関数は、結果の要素で構成されるリストを返す。

>>> c.most_common()
[('e', 3), ('l', 3), ('M', 2), ('a', 2), ('s', 2), ('r', 2), ('o', 2), ('y', 1), ('N', 1), ('m', 1), ('i', 1), ('n', 1), ('H', 1), ('W', 1), ('d', 1)]
>>> c.most_common(3)
[('e', 3), ('l', 3), ('M', 2)]

most_commonメソッドのソースコードを読み取ります。
# n==Noneのとき。
メソッドは,組み込み関数: sorted(iterable[, key=None [,reverse=False]]) で実装されており,要素数の統計値を逆順にソートします。
# メソッド呼び出しでnの値が指定された場合
このメソッドはheapqモジュールのnlargest(n, iterable [ , key=None ])関数で実装されており、前述の通り、iterableオブジェクトから最大のn個の要素を見つける役割を担っています。この関数を直接使うことで、比較する要素の数を使うだけで、最も多いn個の要素を得ることができます。

def most_common(self, n=None):
    if n is None:
        return sorted(self.items(), key=_itemgetter(1), reverse=True)
    return _heapq.nlargest(n, self.items(), key=_itemgetter(1))

Counterクラスを知っていると、データ技術の場合、Counterクラスを好んで使って機能を実装することができ、手作業で辞書カウントをするよりもずっと便利です。

今回の要件2:max関数やmin関数を使って、要素の出現回数の最大値や最小値を直接求める

最も一般的なn個の要素を見つけることが要件である場合、関連する機能を実装するためにCounterクラスが好まれます。

>>> lt = [2, 2, 3, 1, 2, 2, 1, 8, 5]
>>> Counter(lt).most_common(1)
[(2, 4)]
>>> Counter(lt).most_common(1)[0][0]
2