Pythonで次のリストがあるとします。
a = [1,2,3,1,2,1,1,1,3,2,2,1]
このリストで最も頻度の高い番号をきちんと見つける方法は?
Pythonで次のリストがあるとします。
a = [1,2,3,1,2,1,1,1,3,2,2,1]
このリストで最も頻度の高い番号をきちんと見つける方法は?
回答:
リストに負でない整数がすべて含まれている場合は、numpy.bincountsを確認する必要があります。
http://docs.scipy.org/doc/numpy/reference/generated/numpy.bincount.html
そしておそらくnp.argmaxを使用します:
a = np.array([1,2,3,1,2,1,1,1,3,2,2,1])
counts = np.bincount(a)
print np.argmax(counts)
より複雑なリスト(負の数または非整数値を含む可能性があります)の場合もnp.histogram
、同様の方法で使用できます。あるいは、numpyを使用せずにpythonで作業したいだけの場合collections.Counter
は、この種のデータを処理するための良い方法です。
from collections import Counter
a = [1,2,3,1,2,1,1,1,3,2,2,1]
b = Counter(a)
print b.most_common(1)
scipy.stats.mode
一般的ではありませんが、少なくとも1桁高速です。
Counter(array).most_common(1)[0][0]
あなたは使うかもしれません
(values,counts) = np.unique(a,return_counts=True)
ind=np.argmax(counts)
print values[ind] # prints the most frequent element
一部の要素が別の要素と同じくらい頻繁である場合、このコードは最初の要素のみを返します。
values[counts.argmax()]
、最初の値を返します。それらすべてを取得するには、を使用できますvalues[counts == counts.max()]
。
>>> # small array
>>> a = [12,3,65,33,12,3,123,888000]
>>>
>>> import collections
>>> collections.Counter(a).most_common()[0][0]
3
>>> %timeit collections.Counter(a).most_common()[0][0]
100000 loops, best of 3: 11.3 µs per loop
>>>
>>> import numpy
>>> numpy.bincount(a).argmax()
3
>>> %timeit numpy.bincount(a).argmax()
100 loops, best of 3: 2.84 ms per loop
>>>
>>> import scipy.stats
>>> scipy.stats.mode(a)[0][0]
3.0
>>> %timeit scipy.stats.mode(a)[0][0]
10000 loops, best of 3: 172 µs per loop
>>>
>>> from collections import defaultdict
>>> def jjc(l):
... d = defaultdict(int)
... for i in a:
... d[i] += 1
... return sorted(d.iteritems(), key=lambda x: x[1], reverse=True)[0]
...
>>> jjc(a)[0]
3
>>> %timeit jjc(a)[0]
100000 loops, best of 3: 5.58 µs per loop
>>>
>>> max(map(lambda val: (a.count(val), val), set(a)))[1]
12
>>> %timeit max(map(lambda val: (a.count(val), val), set(a)))[1]
100000 loops, best of 3: 4.11 µs per loop
>>>
問題のような小さな配列の場合は、「max」と「set」が最適です。
@David Sandersによると、配列のサイズを100,000要素のようなものに増やすと、「最大w /セット」アルゴリズムが最悪となり、「numpy bincount」メソッドが最善です。
a = (np.random.rand(100000) * 1000).round().astype('int'); a_list = list(a)
)に増やすと、「最大w /セット」アルゴリズムが最悪となり、「numpy bincount」メソッドが最適です。a_list
ネイティブpythonコードとa
numpyコードを使用してこのテストを実行し、マーシャリングコストが結果を台無しにすることを回避しました。
また、モジュールをロードせずに最も頻繁な値(正または負)を取得したい場合は、次のコードを使用できます。
lVals = [1,2,3,1,2,1,1,1,3,2,2,1]
print max(map(lambda val: (lVals.count(val), val), set(lVals)))
max(set(lVals), key=lVals.count)
各一意の要素に対してO(n)カウントを行う読みやすいと同等ですlVals
(O(n)が一意であると仮定)要素)。JoshAdelによって提案されcollections.Counter(lVals).most_common(1)[0][0]
ているように、標準ライブラリからの使用はO(n)のみです。
上記の答えのほとんどは便利ですが、次の場合に役立ちます:1)非正の整数値をサポートする必要がある(例:浮動小数点数または負の整数;-))、および2)Python 2.7にない(どのcollections.Counter必要)、および3)scipy(またはnumpy)の依存関係をコードに追加しないことを好む場合、O(nlogn)(つまり、効率的)である純粋なpython 2.6ソリューションはこれだけです:
from collections import defaultdict
a = [1,2,3,1,2,1,1,1,3,2,2,1]
d = defaultdict(int)
for i in a:
d[i] += 1
most_frequent = sorted(d.iteritems(), key=lambda x: x[1], reverse=True)[0]
Python 3では、以下が機能するはずです。
max(set(a), key=lambda x: a.count(x))
以降Python 3.4
、標準ライブラリには、statistics.mode
最も一般的な単一のデータポイントを返す関数が含まれています。
from statistics import mode
mode([1, 2, 3, 1, 2, 1, 1, 1, 3, 2, 2, 1])
# 1
同じ頻度のモードが複数ある場合statistics.mode
は、最初に見つかったモードを返します。
からPython 3.8
、statistics.multimode
関数は最も頻繁に発生する値のリストを、最初に遭遇した順序で返します。
from statistics import multimode
multimode([1, 2, 3, 1, 2])
# [1, 2]
以下は、値に関係なく、純粋にナンピーを使用して軸に沿って適用できる一般的なソリューションです。また、一意の値が多数ある場合、これはscipy.stats.modeよりもはるかに高速であることもわかりました。
import numpy
def mode(ndarray, axis=0):
# Check inputs
ndarray = numpy.asarray(ndarray)
ndim = ndarray.ndim
if ndarray.size == 1:
return (ndarray[0], 1)
elif ndarray.size == 0:
raise Exception('Cannot compute mode on empty array')
try:
axis = range(ndarray.ndim)[axis]
except:
raise Exception('Axis "{}" incompatible with the {}-dimension array'.format(axis, ndim))
# If array is 1-D and numpy version is > 1.9 numpy.unique will suffice
if all([ndim == 1,
int(numpy.__version__.split('.')[0]) >= 1,
int(numpy.__version__.split('.')[1]) >= 9]):
modals, counts = numpy.unique(ndarray, return_counts=True)
index = numpy.argmax(counts)
return modals[index], counts[index]
# Sort array
sort = numpy.sort(ndarray, axis=axis)
# Create array to transpose along the axis and get padding shape
transpose = numpy.roll(numpy.arange(ndim)[::-1], axis)
shape = list(sort.shape)
shape[axis] = 1
# Create a boolean array along strides of unique values
strides = numpy.concatenate([numpy.zeros(shape=shape, dtype='bool'),
numpy.diff(sort, axis=axis) == 0,
numpy.zeros(shape=shape, dtype='bool')],
axis=axis).transpose(transpose).ravel()
# Count the stride lengths
counts = numpy.cumsum(strides)
counts[~strides] = numpy.concatenate([[0], numpy.diff(counts[~strides])])
counts[strides] = 0
# Get shape of padded counts and slice to return to the original shape
shape = numpy.array(sort.shape)
shape[axis] += 1
shape = shape[transpose]
slices = [slice(None)] * ndim
slices[axis] = slice(1, None)
# Reshape and compute final counts
counts = counts.reshape(shape).transpose(transpose)[slices] + 1
# Find maximum counts and return modals/counts
slices = [slice(None, i) for i in sort.shape]
del slices[axis]
index = numpy.ogrid[slices]
index.insert(axis, numpy.argmax(counts, axis=axis))
return sort[index], counts[index]
私は最近プロジェクトをやっていて、collections.Counterを使用しています(これは私を拷問しました)。
コレクションのカウンターは私の意見では非常に非常に悪いパフォーマンスを持っています。これは、dict()をラップするクラスです。
さらに悪いことに、cProfileを使用してそのメソッドのプロファイルを作成すると、多くの '__missing__'と '__instancecheck__'が時間を浪費していることがわかります。
most_common()の使用には注意してください。毎回ソートを呼び出すため、処理が非常に遅くなります。また、most_common(x)を使用すると、ヒープソートが呼び出されますが、これも低速です。
ところで、numpyのbincountにも問題があります。np.bincount([1,2,4000000])を使用すると、4000000要素の配列が得られます。
np.bincount([1, 2, 3, 1, 2, 1, 1, 1, 3, 2, 2, 1]).argmax()