Giả sử tôi có danh sách sau trong python:
a = [1,2,3,1,2,1,1,1,3,2,2,1]
Làm thế nào để tìm số thường xuyên nhất trong danh sách này một cách gọn gàng?
Giả sử tôi có danh sách sau trong python:
a = [1,2,3,1,2,1,1,1,3,2,2,1]
Làm thế nào để tìm số thường xuyên nhất trong danh sách này một cách gọn gàng?
Câu trả lời:
Nếu danh sách của bạn chứa tất cả các int không âm, bạn nên xem qua numpy.bincounts:
http://docs.scipy.org/doc/numpy/reference/generated/numpy.bincount.html
và sau đó có thể sử dụng 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))
Đối với danh sách phức tạp hơn (có thể chứa số âm hoặc giá trị không phải số nguyên), bạn có thể sử dụng np.histogram
theo cách tương tự. Ngoài ra, nếu bạn chỉ muốn làm việc trong python mà không sử dụng numpy, collections.Counter
là một cách tốt để xử lý loại dữ liệu này.
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
, mặc dù ít tổng quát hơn.
Counter(array).most_common(1)[0][0]
Bạn có thể sử dụng
(values,counts) = np.unique(a,return_counts=True)
ind=np.argmax(counts)
print values[ind] # prints the most frequent element
Nếu một số phần tử thường xuyên như một phần tử khác, mã này sẽ chỉ trả về phần tử đầu tiên.
values[counts.argmax()]
sẽ trả về giá trị đầu tiên. Để có được tất cả chúng, chúng ta có thể sử dụng 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
>>>
Tốt nhất là 'max' với 'set' cho các mảng nhỏ như vấn đề.
Theo @David Sanders, nếu bạn tăng kích thước mảng lên khoảng 100.000 phần tử, thuật toán "max w / set" sẽ là tệ nhất cho đến nay, trong khi phương pháp "numpy bincount" là tốt nhất.
a = (np.random.rand(100000) * 1000).round().astype('int'); a_list = list(a)
), thuật toán "max w / set" của bạn sẽ là tệ nhất cho đến nay, trong khi phương pháp "numpy bincount" là tốt nhất. Tôi đã tiến hành thử nghiệm này bằng cách sử dụng a_list
mã python gốc và a
mã numpy để tránh chi phí điều chỉnh làm ảnh hưởng đến kết quả.
Ngoài ra, nếu bạn muốn nhận giá trị thường xuyên nhất (dương hoặc âm) mà không cần tải bất kỳ mô-đun nào, bạn có thể sử dụng mã sau:
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)
, tính một O (n) cho mỗi phần tử duy nhất của lVals
khoảng O (n ^ 2) (giả sử O (n) duy nhất phần tử). Sử dụng collections.Counter(lVals).most_common(1)[0][0]
từ thư viện tiêu chuẩn, theo đề xuất của JoshAdel , chỉ là O (n).
Mặc dù hầu hết các câu trả lời ở trên đều hữu ích, trong trường hợp bạn: 1) cần nó hỗ trợ các giá trị không phải số nguyên dương (ví dụ: số thực hoặc số nguyên âm ;-)) và 2) không có trên Python 2.7 (bộ sưu tập nào. yêu cầu) và 3) không muốn thêm sự phụ thuộc của scipy (hoặc thậm chí là numpy) vào mã của bạn, thì một giải pháp python 2.6 thuần túy là O (nlogn) (tức là hiệu quả) chỉ là:
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]
Mở rộng trên phương pháp này , được áp dụng để tìm chế độ dữ liệu nơi bạn có thể cần chỉ mục của mảng thực tế để xem giá trị cách tâm phân phối bao xa.
(_, idx, counts) = np.unique(a, return_index=True, return_counts=True)
index = idx[np.argmax(counts)]
mode = a[index]
Nhớ bỏ chế độ khi len (np.argmax (counts))> 1
Trong Python 3, những điều sau sẽ hoạt động:
max(set(a), key=lambda x: a.count(x))
Bắt đầu từ Python 3.4
, thư viện tiêu chuẩn bao gồm statistics.mode
chức năng trả về một điểm dữ liệu chung nhất.
from statistics import mode
mode([1, 2, 3, 1, 2, 1, 1, 1, 3, 2, 2, 1])
# 1
Nếu có nhiều chế độ có cùng tần số, statistics.mode
trả về chế độ đầu tiên gặp phải.
Bắt đầu từ Python 3.8
, statistics.multimode
hàm trả về danh sách các giá trị xuất hiện thường xuyên nhất theo thứ tự chúng gặp lần đầu tiên:
from statistics import multimode
multimode([1, 2, 3, 1, 2])
# [1, 2]
Đây là một giải pháp chung có thể được áp dụng dọc theo một trục, bất kể các giá trị, sử dụng hoàn toàn numpy. Tôi cũng thấy rằng điều này nhanh hơn nhiều so với scipy.stats.mode nếu có nhiều giá trị duy nhất.
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]
Gần đây tôi đang thực hiện một dự án và sử dụng collection.Counter. (Điều này đã hành hạ tôi).
Theo ý kiến của tôi, Counter trong bộ sưu tập có hiệu suất rất tệ. Nó chỉ là một lớp bao gói dict ().
Điều tồi tệ hơn, Nếu bạn sử dụng cProfile để lập hồ sơ phương thức của nó, bạn sẽ thấy rất nhiều thứ '__missing__' và '__instancecheck__' lãng phí toàn bộ thời gian.
Hãy cẩn thận khi sử dụng most_common () của nó, bởi vì mọi lúc nó sẽ gọi một loại làm cho nó cực kỳ chậm. và nếu bạn sử dụng most_common (x), nó sẽ gọi một loại đống, cũng chậm.
Btw, bincount của numpy cũng có một vấn đề: nếu bạn sử dụng np.bincount ([1,2,4000000]), bạn sẽ nhận được một mảng có 4000000 phần tử.
np.bincount([1, 2, 3, 1, 2, 1, 1, 1, 3, 2, 2, 1]).argmax()