整数をある範囲にクランプする方法は?


92

私は次のコードを持っています:

new_index = index + offset
if new_index < 0:
    new_index = 0
if new_index >= len(mylist):
    new_index = len(mylist) - 1
return mylist[new_index]

基本的に、私は新しいインデックスを計算し、それを使用してリストからいくつかの要素を見つけます。インデックスがリストの境界内にあることを確認するために、これらの2つのifステートメントを4行に分割して記述する必要がありました。それはかなり冗長で、少し醜いです...あえて言ってください、それは非常に非Pythonicです。

他にもっとシンプルでコンパクトなソリューションはありますか?(そしてもっとpythonic

はい、if else1行で使用できることはわかっていますが、判読できません。

new_index = 0 if new_index < 0 else len(mylist) - 1 if new_index >= len(mylist) else new_index

またmax()min()一緒にチェーンできることも知っています。よりコンパクトですが、タイプを間違えた場合、バグを見つけるのは少しあいまいで、わかりにくく感じます。言い換えれば、私はそれが非常に簡単であるとは思いません。

new_index = max(0, min(new_index, len(mylist)-1))

2
それが「ちょっと曖昧」に感じたら、それから機能を作りますか?
サンタ

1
ええ、私は関数を書くことができますが、それはポイントではありません。問題は、それをどのように実装するかです(インラインまたは関数で)。
DenilsonSáMaia

clamp = lambda value, minv, maxv: max(min(value, maxv), minv)arma.sourceforge.net/docs.html#clamp
Dima Tisnek

回答:


119

実際、これはかなり明確です。多くの人々はそれをすぐに学びます。あなたはそれらを助けるためにコメントを使用することができます。

new_index = max(0, min(new_index, len(mylist)-1))

12
本来のpythonicではないように感じますが、これが現在の最高のソリューションだと感じています。
DenilsonSáMaia 2010

49
def clamp(n, smallest, largest): return max(smallest, min(n, largest))
csl 2015

3
@cslフォークは常にこれらの小さなヘルパー関数を提供しますが、どこに置くかわかりません。helperFunctions.py?別のモジュール?これが完全に異なるもののためのさまざまな「ヘルパー関数」で散らかされたらどうなるでしょうか?
Mateen Ulhaq 2017年

1
私は知りませんが、それらの多くを収集してそれらを賢明なモジュールに分類する場合は、GitHubを使用してPyPiパッケージを作成してみませんか?おそらく人気になるでしょう。
csl 2017年

@MateenUlhaqutils.py
Wouterr

85
sorted((minval, value, maxval))[1]

例えば:

>>> minval=3
>>> maxval=7
>>> for value in range(10):
...   print sorted((minval, value, maxval))[1]
... 
3
3
3
3
4
5
6
7
7
7

10
sorted()ビルトインの創造的な使用のための+1 。非常にコンパクトですが、少しあいまいです。とにかく、他の創造的な解決策を見るのはいつも素晴らしいことです!
DenilsonSáMaia

10
非常にクリエイティブで、実際にはmin(max())建設とほぼ同じ速さです。数値が範囲内にあり、スワップが不要な場合、非常にわずかに速くなります。
2010年

40

ここに多くの興味深い答えがありますが、ほぼ同じですが...どちらが速いですか?

import numpy
np_clip = numpy.clip
mm_clip = lambda x, l, u: max(l, min(u, x))
s_clip = lambda x, l, u: sorted((x, l, u))[1]
py_clip = lambda x, l, u: l if x < l else u if x > u else x
>>> import random
>>> rrange = random.randrange
>>> %timeit mm_clip(rrange(100), 10, 90)
1000000 loops, best of 3: 1.02 µs per loop

>>> %timeit s_clip(rrange(100), 10, 90)
1000000 loops, best of 3: 1.21 µs per loop

>>> %timeit np_clip(rrange(100), 10, 90)
100000 loops, best of 3: 6.12 µs per loop

>>> %timeit py_clip(rrange(100), 10, 90)
1000000 loops, best of 3: 783 ns per loop

paxdiabloはそれを持っています!、プレーンol 'pythonを使用します。派手なバージョンは、おそらく驚くことではないが、ロットの中で最も遅い。おそらくそれは、他のバージョンが引数を順序付けるだけの配列を探しているためです。


7
@LenarHoyt Numpyのパフォーマンスは単一の数値ではなく大きな配列を中心に設計されていることを考えると、それほど驚くべきことではありません。また、最初に整数を内部データ型に変換する必要があり、いくつかの異なる種類の入力を受け入れるため、入力の種類と変換先を理解するにはかなりの時間がかかります。数千の値の配列(できればリストやタプルではなく、最初に変換する必要がある)をフィードすると、Numpyのパフォーマンスが大幅に向上します。
blubberdiblub

Pythonは3桁遅い。783 ns = 783,000 µs。過去にも同じ間違いをしたことがあります。表記は微妙です。
ダスティンアンドリュース

5
@DustinAndrewsあなたはそれを逆に持っています。1 µsは10 ^ -6秒、1 nsは10 ^ -9秒です。Pythonの例では、0.784 µsで1ループを完了します。または、少なくとも、私がテストしたマシンで実行されました。このマイクロベンチマークは、他のどのマイクロベンチマークと同じくらい有用です。それは本当に悪いアイデアからあなたを遠ざけることができますが、おそらくあなたが有用なコードを書くための実際に最速の方法を見つけるのを助けることはおそらくないでしょう。
SingleNegationElimination 2018年

関数の呼び出しにはわずかなオーバーヘッドがあります。私はベンチマークを行っていませんが、PyPyのようなJITコンパイラーを使用すれば、mm_clipそれpy_clipが可能であり、同じくらい高速になるでしょう。前者を除いて、より読みやすく、Pythonの哲学では、ほとんどの場合、わずかなパフォーマンスの向上よりも読みやすさが重要です。
Highstaker 2018

@DustinAndrews事実に反するコメントは削除したので、削除した方がいいです。
Acumenus

38

numpy.clipを参照してください:

index = numpy.clip(index, 0, len(my_list) - 1)

ドキュメントによると、の最初のパラメータclipa、「クリップする要素を含む配列」です。ですから、書く必要はありnumpy.clip([index], …ませんnumpy.clip(index, …
Rory O'Kane 2013

13
@ RoryO'Kane:やってみましたか?
Neil G

1
パンダはまた、これをシリーズ、データフレーム、およびパネルで許可します。
Nour Wolf

17

チェーンmax()min()一緒に私が見た通常のイディオムです。読みにくい場合は、操作をカプセル化するヘルパー関数を記述します。

def clamp(minimum, x, maximum):
    return max(minimum, min(x, maximum))

14

私の最愛の読みやすいPython言語はどうなりましたか?:-)

真剣に、それを関数にしてください:

def addInRange(val, add, minval, maxval):
    newval = val + add
    if newval < minval: return minval
    if newval > maxval: return maxval
    return newval

次に、次のように呼び出します。

val = addInRange(val, 7, 0, 42)

または、自分で計算を行う、よりシンプルで柔軟なソリューション:

def restrict(val, minval, maxval):
    if val < minval: return minval
    if val > maxval: return maxval
    return val

x = restrict(x+10, 0, 42)

必要に応じて、最小/最大をリストにして、「数学的に純粋」に見えるようにすることもできます。

x = restrict(val+7, [0, 42])

6
関数に入れるのは問題ありませんが(多くの場合はお勧めします)、たくさんの条件文よりもはるかに明確だminと思いmaxます。(何をするのかわからないadd-ただ言うだけclamp(val + 7, 0, 42)です。)
Glenn Maynard、

1
@GlennMaynard。minとmaxがよりクリーンであることに同意できるかどうかはわかりません。それらを使用する全体のポイントは、1行に多く詰め込むことができ、コードを読みにくくすることです。
マッド

10

これは私にはもっとpythonicのようです:

>>> def clip(val, min_, max_):
...     return min_ if val < min_ else max_ if val > max_ else val

いくつかのテスト:

>>> clip(5, 2, 7)
5
>>> clip(1, 2, 7)
2
>>> clip(8, 2, 7)
7

8

コードが扱いにくいと思われる場合は、関数が役立ちます。

def clamp(minvalue, value, maxvalue):
    return max(minvalue, min(value, maxvalue))

new_index = clamp(0, new_index, len(mylist)-1)

2

頻繁に適用しない限り、そのような小さなタスクの関数を作成しないでください。コードが煩雑になります。

個々の値:

min(clamp_max, max(clamp_min, value))

値のリストの場合:

map(lambda x: min(clamp_max, max(clamp_min, x)), values)
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.