ค้นหาตัวเลขที่พบบ่อยที่สุดในเวกเตอร์ตัวเลข


123

สมมติว่าฉันมีรายการต่อไปนี้ใน python:

a = [1,2,3,1,2,1,1,1,3,2,2,1]

วิธีค้นหาหมายเลขที่พบบ่อยที่สุดในรายการนี้อย่างเป็นระเบียบ?

คำตอบ:


193

หากรายการของคุณมี ints ที่ไม่เป็นลบทั้งหมดคุณควรดูที่ 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ในลักษณะเดียวกันได้ หรือหากคุณต้องการทำงานใน python โดยไม่ต้องใช้ numpy 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))

58
+1 อาจเป็นเพียงnp.bincount([1, 2, 3, 1, 2, 1, 1, 1, 3, 2, 2, 1]).argmax()
Nikolai Fetissov

1
+1 อย่างน้อยนี่คือลำดับความสำคัญที่เร็วกว่าscipy.stats.modeแม้ว่าทั่วไปจะน้อยกว่าก็ตาม
Fred Foo

คำตอบที่ดี! อย่างไรก็ตามหากมีคนใช้ python 2.6 จะไม่สามารถใช้คอลเลคชันเคาน์เตอร์ได้ ในกรณีนี้โปรดดูคำตอบของฉันด้านล่าง
JJC

19
สำหรับพวกเราที่เยี่ยมชมหลังจากปี 2016: ฉันไม่ชอบคำตอบนี้เนื่องจาก bincount (arr) ส่งคืนอาร์เรย์ที่มีขนาดใหญ่เท่ากับองค์ประกอบที่ใหญ่ที่สุดใน arr ดังนั้นอาร์เรย์ขนาดเล็กที่มีช่วงขนาดใหญ่จะสร้างอาร์เรย์ที่มีขนาดใหญ่เกินไป คำตอบของ Apoengtus ด้านล่างดีกว่ามากแม้ว่าฉันจะไม่คิดว่า numpy.unique () มีอยู่ในปี 2011 เมื่อคำตอบนี้ถูกสร้างขึ้น
Wehrdo

2
Python 3 :Counter(array).most_common(1)[0][0]
diralik

80

คุณอาจใช้

(values,counts) = np.unique(a,return_counts=True)
ind=np.argmax(counts)
print values[ind]  # prints the most frequent element

หากองค์ประกอบบางส่วนเกิดบ่อยเท่ากับอีกองค์ประกอบหนึ่งรหัสนี้จะส่งคืนเฉพาะองค์ประกอบแรก


4
ฉันพบว่าสิ่งนี้มีประโยชน์ที่สุดเนื่องจากเป็นแบบทั่วไปสั้นและช่วยให้ดึงองค์ประกอบจากค่าหรือนับโดยดัชนีที่ได้รับ
ryanjdillon

2
หากเรามีค่าที่พบบ่อยหลายค่าvalues[counts.argmax()]จะส่งกลับค่าแรก values[counts == counts.max()]ที่จะได้รับทั้งหมดของพวกเขาเราสามารถใช้
W. Zhu

44

หากคุณยินดีที่จะใช้SciPy :

>>> from scipy.stats import mode
>>> mode([1,2,3,1,2,1,1,1,3,2,2,1])
(array([ 1.]), array([ 6.]))
>>> most_frequent = mode([1,2,3,1,2,1,1,1,3,2,2,1])[0][0]
>>> most_frequent
1.0

30

การแสดง (โดยใช้ iPython) สำหรับโซลูชันบางอย่างพบได้ที่นี่:

>>> # 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 องค์ประกอบอัลกอริทึม "max w / set" จะเป็นวิธีที่แย่ที่สุดในขณะที่วิธี "numpy bincount" เป็นวิธีที่ดีที่สุด


1
@IuliusCurt เพื่อชี้แนวทางที่ดีที่สุดที่เราต้องทดสอบกับหลาย ๆ กรณี: อาร์เรย์ขนาดเล็กอาร์เรย์ขนาดใหญ่อาร์เรย์แบบสุ่มอาร์เรย์โลกแห่งความจริง (เช่นtimsortสำหรับการเรียงลำดับ) ... แต่ฉันเห็นด้วยกับคุณ
iuridiniz

3
การใช้อาร์เรย์ขนาดเล็กตามแนวทางของคุณจะไม่สามารถแยกแยะความแตกต่างระหว่างอัลกอริทึมต่างๆได้ดีนัก
David Sanders

10
หากคุณเพิ่มขนาดรายการทดสอบเป็น 100000 ( a = (np.random.rand(100000) * 1000).round().astype('int'); a_list = list(a)) อัลกอริทึม "max w / set" ของคุณจะกลายเป็นสิ่งที่แย่ที่สุดในขณะที่วิธี "numpy bincount" นั้นดีที่สุด ฉันทำการทดสอบนี้โดยใช้a_listสำหรับโค้ด python ดั้งเดิมและaสำหรับโค้ด numpy เพื่อหลีกเลี่ยงค่าใช้จ่ายในการจัดเรียงที่ทำให้ผลลัพธ์เสียหาย
David Sanders

4

นอกจากนี้หากคุณต้องการรับค่าบ่อยที่สุด (บวกหรือลบ) โดยไม่ต้องโหลดโมดูลใด ๆ คุณสามารถใช้รหัสต่อไปนี้:

lVals = [1,2,3,1,2,1,1,1,3,2,2,1]
print max(map(lambda val: (lVals.count(val), val), set(lVals)))

1
สิ่งนี้เกิดขึ้นเมื่อไม่นานมานี้ แต่สำหรับคนรุ่นหลัง: สิ่งนี้เทียบเท่ากับค่าที่อ่านง่ายกว่าmax(set(lVals), key=lVals.count)ซึ่งจะนับ O (n) สำหรับแต่ละองค์ประกอบที่ไม่ซ้ำกันlValsโดยประมาณ O (n ^ 2) (สมมติว่า O (n) ไม่ซ้ำกัน องค์ประกอบ) การใช้collections.Counter(lVals).most_common(1)[0][0]จากไลบรารีมาตรฐานตามที่ JoshAdel แนะนำคือ O (n) เท่านั้น
Dougal

3

แม้ว่าคำตอบส่วนใหญ่ข้างต้นจะมีประโยชน์ในกรณีที่คุณ: 1) ต้องการคำตอบเพื่อรองรับค่าที่ไม่ใช่จำนวนเต็มบวก (เช่นลอยหรือจำนวนเต็มลบ ;-)) และ 2) ไม่ได้อยู่ใน Python 2.7 (คอลเลกชันใด ต้องการ) และ 3) ไม่ต้องการเพิ่มการอ้างอิงของ scipy (หรือแม้กระทั่ง numpy) ให้กับโค้ดของคุณจากนั้นโซลูชัน python 2.6 ล้วน ๆ ที่เป็น O (nlogn) (กล่าวคือมีประสิทธิภาพ) เป็นเพียงแค่นี้:

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]

2

ฉันชอบวิธีแก้ปัญหาโดย JoshAdel

แต่มีเพียงหนึ่งที่จับ

np.bincount()โซลูชั่นที่ใช้งานได้เฉพาะกับตัวเลข

หากคุณมีสตริงcollections.Counterโซลูชันจะเหมาะกับคุณ


1

การขยายวิธีนี้ใช้กับการค้นหาโหมดของข้อมูลที่คุณอาจต้องการดัชนีของอาร์เรย์จริงเพื่อดูว่าค่าอยู่ห่างจากจุดศูนย์กลางของการกระจายเท่าใด

(_, idx, counts) = np.unique(a, return_index=True, return_counts=True)
index = idx[np.argmax(counts)]
mode = a[index]

อย่าลืมทิ้งโหมดเมื่อ len (np.argmax (counts))> 1



1

เริ่มต้นใน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]

0

นี่คือวิธีแก้ปัญหาทั่วไปที่อาจนำไปใช้กับแกนโดยไม่คำนึงถึงค่าโดยใช้ตัวเลขล้วนๆ ฉันยังพบว่าสิ่งนี้เร็วกว่า 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]

-1

ฉันเพิ่งทำโปรเจ็กต์และใช้คอลเลกชั่นเคาน์เตอร์ (ซึ่งทรมานฉัน)

เคาน์เตอร์ในคอลเลกชันมีประสิทธิภาพที่แย่มากในความคิดของฉัน มันเป็นเพียงคลาสการตัดคำสั่ง ()

ที่แย่ไปกว่านั้นคือหากคุณใช้ cProfile เพื่อกำหนดโปรไฟล์วิธีการของมันคุณจะเห็นสิ่งที่ "__missing__" และ "__instancecheck__" จำนวนมากเสียเวลาไปเปล่า ๆ

ระวังการใช้ most_common () เพราะทุกครั้งมันจะเรียกใช้การเรียงลำดับซึ่งทำให้ช้ามาก และถ้าคุณใช้ most_common (x) มันจะเรียกใช้การเรียงลำดับฮีปซึ่งช้าเช่นกัน

Btw, bincount ของ numpy ก็มีปัญหาเช่นกัน: ถ้าคุณใช้ np.bincount ([1,2,4000000]) คุณจะได้อาร์เรย์ที่มีองค์ประกอบ 4000000


3
Dict เป็นโครงสร้างข้อมูลที่ปรับแต่งอย่างละเอียดที่สุดใน Python และเหมาะสำหรับการนับวัตถุโดยพลการ ในทางตรงกันข้าม binning ใช้ได้เฉพาะกับค่าตัวเลขและไม่อนุญาตให้คุณป้องกันการใช้นามแฝงระหว่างค่าที่ไม่ต่อเนื่องที่เว้นระยะห่างอย่างใกล้ชิด ในกรณีของตัวนับเมธอด __missing__ จะถูกเรียกใช้เมื่อเห็นองค์ประกอบเป็นครั้งแรกเท่านั้น มิฉะนั้นการมีอยู่นั้นไม่มีค่าใช้จ่าย หมายเหตุเมธอด most_common ()นั้นเร็วมากในกรณีส่วนใหญ่เนื่องจากฮีปมีขนาดเล็กมากเมื่อเทียบกับชุดข้อมูลทั้งหมด ในกรณีส่วนใหญ่most_common ()วิธีการทำเพียงการเปรียบเทียบเล็กน้อยกว่านาที ()
Raymond Hettinger
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.