numpy: ความถี่ที่มีประสิทธิภาพที่สุดจะนับสำหรับค่าที่ไม่ซ้ำในอาร์เรย์


244

ในnumpy/ scipyมีวิธีที่มีประสิทธิภาพในการรับความถี่นับสำหรับค่าที่ไม่ซ้ำกันในอาร์เรย์หรือไม่?

บางสิ่งบางอย่างตามสายเหล่านี้:

x = array( [1,1,1,2,2,2,5,25,1,1] )
y = freq_count( x )
print y

>> [[1, 5], [2,3], [5,1], [25,1]]

(สำหรับคุณผู้ใช้ R ออกมีฉันโดยทั่วไปกำลังมองหาtable()ฟังก์ชั่น)


5
คือcollections.Counter(x)เพียงพอ?
pylang

1
มันจะดีกว่าที่ผมคิดว่าถ้าคุณติ๊กตอนนี้คำตอบนี้เป็นที่ถูกต้องสำหรับคำถามของคุณ: stackoverflow.com/a/25943480/9024698
ถูกขับไล่

Collections.counter ค่อนข้างช้า ดูโพสต์ของฉัน: stackoverflow.com/questions/41594940/ …
Sembei Norimaki

คำตอบ:


161

ดูที่np.bincount:

http://docs.scipy.org/doc/numpy/reference/generated/numpy.bincount.html

import numpy as np
x = np.array([1,1,1,2,2,2,5,25,1,1])
y = np.bincount(x)
ii = np.nonzero(y)[0]

แล้ว:

zip(ii,y[ii]) 
# [(1, 5), (2, 3), (5, 1), (25, 1)]

หรือ:

np.vstack((ii,y[ii])).T
# array([[ 1,  5],
         [ 2,  3],
         [ 5,  1],
         [25,  1]])

หรืออย่างไรก็ตามคุณต้องการรวมการนับและค่าที่ไม่ซ้ำกัน


42
สวัสดีสิ่งนี้จะไม่ทำงานหากองค์ประกอบของ x มี dtype นอกเหนือจาก int
Manoj

7
มันจะไม่ทำงานหากพวกมันเป็นอย่างอื่นที่ไม่ใช่ ints เชิงลบและมันจะไม่มีประสิทธิภาพในพื้นที่มากถ้า ints นั้นเว้นระยะออกไป
Erik

ด้วย numpy version 1.10 ฉันพบว่าสำหรับการนับจำนวนเต็มจะเร็วกว่า np.unique ประมาณ 6 เท่า นอกจากนี้โปรดทราบว่าจะนับ ints เชิงลบเช่นกันหากมีการระบุพารามิเตอร์ที่ถูกต้อง
Jihun

@Manoj: องค์ประกอบของฉันคืออาร์เรย์ ฉันกำลังทดสอบวิธีแก้ปัญหาของ jme
Catalina Chircu

508

ตั้งแต่ Numpy 1.9 วิธีที่ง่ายที่สุดและเร็วที่สุดคือการใช้numpy.uniqueซึ่งตอนนี้มีreturn_countsอาร์กิวเมนต์คำหลัก:

import numpy as np

x = np.array([1,1,1,2,2,2,5,25,1,1])
unique, counts = np.unique(x, return_counts=True)

print np.asarray((unique, counts)).T

ซึ่งจะช่วยให้:

 [[ 1  5]
  [ 2  3]
  [ 5  1]
  [25  1]]

เปรียบเทียบอย่างรวดเร็วด้วยscipy.stats.itemfreq:

In [4]: x = np.random.random_integers(0,100,1e6)

In [5]: %timeit unique, counts = np.unique(x, return_counts=True)
10 loops, best of 3: 31.5 ms per loop

In [6]: %timeit scipy.stats.itemfreq(x)
10 loops, best of 3: 170 ms per loop

22
ขอบคุณสำหรับการอัปเดต! นี่คือตอนนี้ IMO คำตอบที่ถูกต้อง
Erve1879

1
BAM! นี่คือเหตุผลที่เราอัปเดต ... เมื่อเราพบคำตอบเช่นนี้ 1.8 นานเท่าไหร่ เราจะนำสิ่งนี้ไปสู่จุดสูงสุดของรายการได้อย่างไร?
user1269942

หากคุณได้รับข้อผิดพลาด: TypeError: unique () มีอาร์กิวเมนต์คำหลักที่ไม่คาดคิด 'return_counts' ให้ทำ: unique, counts = np.unique (x, True)
NumesSanguis

3
@NumesSanguis คุณใช้เวอร์ชั่นอะไรอยู่? ก่อนหน้า v1.9 return_countsอาร์กิวเมนต์ของคำหลักไม่มีอยู่ซึ่งอาจอธิบายถึงข้อยกเว้น ในกรณีนั้นเอกสารแนะนำว่าnp.unique(x, True)เทียบเท่ากับnp.unique(x, return_index=True)ซึ่งไม่ส่งคืนจำนวน
jme

1
ในรุ่นเก่า numpy unique, idx = np.unique(x, return_inverse=True); counts = np.bincount(idx)สำนวนทั่วไปที่จะได้รับในสิ่งเดียวกันคือ เมื่อเพิ่มฟีเจอร์นี้ (ดูที่นี่ ) การทดสอบแบบไม่เป็นทางการมีการใช้การreturn_countsตอกบัตรได้เร็วกว่า 5x
Jaime

133

อัปเดต: วิธีการที่กล่าวถึงในคำตอบเดิมเลิกใช้แล้วเราควรใช้วิธีใหม่แทน:

>>> import numpy as np
>>> x = [1,1,1,2,2,2,5,25,1,1]
>>> np.array(np.unique(x, return_counts=True)).T
    array([[ 1,  5],
           [ 2,  3],
           [ 5,  1],
           [25,  1]])

คำตอบเดิม:

คุณสามารถใช้scipy.stats.itemfreq

>>> from scipy.stats import itemfreq
>>> x = [1,1,1,2,2,2,5,25,1,1]
>>> itemfreq(x)
/usr/local/bin/python:1: DeprecationWarning: `itemfreq` is deprecated! `itemfreq` is deprecated and will be removed in a future version. Use instead `np.unique(..., return_counts=True)`
array([[  1.,   5.],
       [  2.,   3.],
       [  5.,   1.],
       [ 25.,   1.]])

1
ดูเหมือนว่าจะเป็นวิธีการทำเสียงไพเราะที่สุด นอกจากนี้ฉันยังพบปัญหาเกี่ยวกับ "วัตถุที่ลึกเกินไปสำหรับอาเรย์ที่ต้องการ" ด้วย np.bincount ในการฝึกอบรม 100k x 100k
metasequoia

1
ฉันอยากจะแนะนำคำถามที่ตอบยากกว่าเดิมเพื่อเปลี่ยนคำตอบที่ถูกต้องจากคำถามแรกเป็นคำถามนี้เพื่อเพิ่มการมองเห็น
wiswit

มันช้าสำหรับรุ่นก่อน 0.14 แม้ว่า
Jason S

โปรดทราบว่าหากอาร์เรย์เต็มไปด้วยสตริงองค์ประกอบทั้งสองในแต่ละรายการที่ส่งคืนจะเป็นสตริงด้วย
user1269942

ดูเหมือนว่า itemfreq ถูกเลิกใช้แล้ว
Terence Parr

48

ฉันสนใจสิ่งนี้เช่นกันดังนั้นฉันจึงทำการเปรียบเทียบประสิทธิภาพเล็กน้อย (โดยใช้perfplotโครงการสัตว์เลี้ยงของฉัน) ผลลัพธ์:

y = np.bincount(a)
ii = np.nonzero(y)[0]
out = np.vstack((ii, y[ii])).T

เร็วที่สุด (หมายเหตุการปรับขนาดบันทึก)

ป้อนคำอธิบายรูปภาพที่นี่


รหัสเพื่อสร้างพล็อต:

import numpy as np
import pandas as pd
import perfplot
from scipy.stats import itemfreq


def bincount(a):
    y = np.bincount(a)
    ii = np.nonzero(y)[0]
    return np.vstack((ii, y[ii])).T


def unique(a):
    unique, counts = np.unique(a, return_counts=True)
    return np.asarray((unique, counts)).T


def unique_count(a):
    unique, inverse = np.unique(a, return_inverse=True)
    count = np.zeros(len(unique), np.int)
    np.add.at(count, inverse, 1)
    return np.vstack((unique, count)).T


def pandas_value_counts(a):
    out = pd.value_counts(pd.Series(a))
    out.sort_index(inplace=True)
    out = np.stack([out.keys().values, out.values]).T
    return out


perfplot.show(
    setup=lambda n: np.random.randint(0, 1000, n),
    kernels=[bincount, unique, itemfreq, unique_count, pandas_value_counts],
    n_range=[2 ** k for k in range(26)],
    logx=True,
    logy=True,
    xlabel="len(a)",
)

1
ขอขอบคุณที่โพสต์รหัสเพื่อสร้างพล็อต ไม่เคยรู้เรื่องperfplotมาก่อนในตอนนี้ ดูมีประโยชน์
ruffsl

ผมสามารถที่จะเรียกใช้รหัสของคุณโดยการเพิ่มตัวเลือกในequality_check=array_sorteq perfplot.show()สิ่งที่ทำให้เกิดข้อผิดพลาด (ใน Python 2) คือpd.value_counts(ถึงแม้จะมี sort = False)
user2314737

33

ใช้โมดูลหมีแพนด้า:

>>> import pandas as pd
>>> import numpy as np
>>> x = np.array([1,1,1,2,2,2,5,25,1,1])
>>> pd.value_counts(x)
1     5
2     3
25    1
5     1
dtype: int64

5
pd.Series () ไม่จำเป็น มิฉะนั้นเป็นตัวอย่างที่ดี อ้วนเตี้ยเช่นกัน นุ่นสามารถทำรายการง่าย ๆ เป็นอินพุต
โยฮันโอบาดี

1
@YohanObadia - ขึ้นอยู่กับขนาดของอาร์เรย์ก่อนอื่นการแปลงเป็นซีรีย์ทำให้การดำเนินการขั้นสุดท้ายเร็วขึ้นสำหรับฉัน ฉันเดาว่ามีค่าประมาณ 50,000 ค่า
n1k31t4

1
ฉันแก้ไขคำตอบของฉันเพื่อพิจารณาความคิดเห็นที่เกี่ยวข้องจาก @YohanObadia
ivankeller

19

นี่เป็นวิธีการแก้ปัญหาที่ใช้กันทั่วไปและมีประสิทธิภาพที่สุด แปลกใจที่มันยังไม่ได้โพสต์

import numpy as np

def unique_count(a):
    unique, inverse = np.unique(a, return_inverse=True)
    count = np.zeros(len(unique), np.int)
    np.add.at(count, inverse, 1)
    return np.vstack(( unique, count)).T

print unique_count(np.random.randint(-10,10,100))

ต่างจากคำตอบที่ยอมรับในปัจจุบันมันทำงานกับประเภทข้อมูลใด ๆ ที่สามารถจัดเรียงได้ (ไม่ใช่แค่ ints เชิงบวก) และมีประสิทธิภาพที่ดีที่สุด ค่าใช้จ่ายที่สำคัญเพียงอย่างเดียวคือการเรียงลำดับโดย np.unique


ไม่ทำงาน:AttributeError: 'numpy.ufunc' object has no attribute 'at'
ประชาสัมพันธ์

วิธีที่ง่ายกว่าคือการโทรnp.bincount(inverse)
ali_m

15

numpy.bincountอาจเป็นตัวเลือกที่ดีที่สุด หากอาร์เรย์ของคุณมีสิ่งใดนอกเหนือจากจำนวนเต็มขนาดเล็กที่หนาแน่นอาจเป็นประโยชน์ในการห่อสิ่งนี้:

def count_unique(keys):
    uniq_keys = np.unique(keys)
    bins = uniq_keys.searchsorted(keys)
    return uniq_keys, np.bincount(bins)

ตัวอย่างเช่น:

>>> x = array([1,1,1,2,2,2,5,25,1,1])
>>> count_unique(x)
(array([ 1,  2,  5, 25]), array([5, 3, 1, 1]))

8

แม้ว่าจะได้รับคำตอบแล้วฉันขอแนะนำวิธีการอื่นที่ใช้ประโยชน์numpy.histogramได้ ฟังก์ชั่นดังกล่าวได้รับลำดับที่มันกลับความถี่ขององค์ประกอบของกลุ่มในถังขยะ

แต่ระวัง : มันทำงานในตัวอย่างนี้เพราะตัวเลขเป็นจำนวนเต็ม หากพวกเขาอยู่ที่จำนวนจริงแล้ววิธีนี้จะไม่ใช้เป็นอย่างดี

>>> from numpy import histogram
>>> y = histogram (x, bins=x.max()-1)
>>> y
(array([5, 3, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       1]),
 array([  1.,   2.,   3.,   4.,   5.,   6.,   7.,   8.,   9.,  10.,  11.,
        12.,  13.,  14.,  15.,  16.,  17.,  18.,  19.,  20.,  21.,  22.,
        23.,  24.,  25.]))

5
import pandas as pd
import numpy as np
x = np.array( [1,1,1,2,2,2,5,25,1,1] )
print(dict(pd.Series(x).value_counts()))

สิ่งนี้ให้คุณ: {1: 5, 2: 3, 5: 1, 25: 1}


1
collections.Counter(x)ยังให้ผลลัพธ์เดียวกัน ฉันเชื่อว่า OP ต้องการเอาต์พุตที่คล้ายกับtableฟังก์ชันR การรักษาSeriesอาจมีประโยชน์มากกว่า
pylang

โปรดทราบว่าจำเป็นต้องทำการโอนย้ายpd.Series(x).reshape(-1)หากเป็นอาเรย์หลายมิติ
natsuapo

4

ในการนับจำนวนที่ไม่ใช่จำนวนเต็ม - คล้ายกับคำตอบของ Eelco Hoogendoorn แต่เร็วกว่ามาก (ปัจจัย 5 ในเครื่องของฉัน) ฉันเคยweave.inlineรวมnumpy.uniqueกับ c-code เล็กน้อย

import numpy as np
from scipy import weave

def count_unique(datain):
  """
  Similar to numpy.unique function for returning unique members of
  data, but also returns their counts
  """
  data = np.sort(datain)
  uniq = np.unique(data)
  nums = np.zeros(uniq.shape, dtype='int')

  code="""
  int i,count,j;
  j=0;
  count=0;
  for(i=1; i<Ndata[0]; i++){
      count++;
      if(data(i) > data(i-1)){
          nums(j) = count;
          count = 0;
          j++;
      }
  }
  // Handle last value
  nums(j) = count+1;
  """
  weave.inline(code,
      ['data', 'nums'],
      extra_compile_args=['-O2'],
      type_converters=weave.converters.blitz)
  return uniq, nums

ข้อมูลโปรไฟล์

> %timeit count_unique(data)
> 10000 loops, best of 3: 55.1 µs per loop

numpyเวอร์ชั่นบริสุทธิ์ของ Eelco :

> %timeit unique_count(data)
> 1000 loops, best of 3: 284 µs per loop

บันทึก

มีความซ้ำซ้อนที่นี่ ( uniqueทำการเรียงลำดับด้วย) ซึ่งหมายความว่ารหัสอาจปรับให้เหมาะสมยิ่งขึ้นโดยการใส่uniqueฟังก์ชันการทำงานลงในลูป c-code


4

คำถามเก่า แต่ฉันต้องการที่จะให้ทางออกของตัวเองซึ่งกลายเป็นวิธีที่เร็วที่สุดใช้แบบปกติlistแทนที่จะnp.arrayเป็นอินพุต (หรือถ่ายโอนไปยังรายการก่อน) ตามการทดสอบของฉัน

ตรวจสอบว่าคุณพบมันเช่นกัน

def count(a):
    results = {}
    for x in a:
        if x not in results:
            results[x] = 1
        else:
            results[x] += 1
    return results

ตัวอย่างเช่น,

>>>timeit count([1,1,1,2,2,2,5,25,1,1]) would return:

100,000 ลูป, ดีที่สุดคือ 3: 2.26 pers ต่อลูป

>>>timeit count(np.array([1,1,1,2,2,2,5,25,1,1]))

100,000 ลูปดีที่สุด 3: 8.8 µs ต่อลูป

>>>timeit count(np.array([1,1,1,2,2,2,5,25,1,1]).tolist())

100,000 ลูป, ดีที่สุดคือ 3: 5.85 pers ต่อลูป

ในขณะที่คำตอบที่ได้รับการยอมรับจะช้าลงและการscipy.stats.itemfreqแก้ปัญหาก็ยิ่งเลวร้ายลง


การทดสอบเชิงลึกยิ่งขึ้นไม่ได้ยืนยันความคาดหวังที่กำหนดไว้

from zmq import Stopwatch
aZmqSTOPWATCH = Stopwatch()

aDataSETasARRAY = ( 100 * abs( np.random.randn( 150000 ) ) ).astype( np.int )
aDataSETasLIST  = aDataSETasARRAY.tolist()

import numba
@numba.jit
def numba_bincount( anObject ):
    np.bincount(    anObject )
    return

aZmqSTOPWATCH.start();np.bincount(    aDataSETasARRAY );aZmqSTOPWATCH.stop()
14328L

aZmqSTOPWATCH.start();numba_bincount( aDataSETasARRAY );aZmqSTOPWATCH.stop()
592L

aZmqSTOPWATCH.start();count(          aDataSETasLIST  );aZmqSTOPWATCH.stop()
148609L

อ้าง ความคิดเห็นด้านล่างเกี่ยวกับแคชและผลข้างเคียงอื่น ๆ ใน RAM ที่มีผลต่อชุดข้อมูลขนาดเล็กผลการทดสอบที่ซ้ำซ้อนอย่างหนาแน่น


คำตอบนี้เป็นสิ่งที่ดีมากเพราะมันแสดงให้เห็นว่าnumpyไม่จำเป็นต้องไป
มาห์ดี

@ ฝนลีที่น่าสนใจ คุณตรวจสอบรายการสมมติฐานด้วยขนาดชุดข้อมูลที่ไม่สามารถแคชได้หรือไม่ ให้ถือว่าสุ่มรายการ 150.000 ในการเป็นตัวแทนอย่างใดอย่างหนึ่งและวัดความแม่นยำมากขึ้นเล็กน้อยในการวิ่งครั้งเดียวโดยตัวอย่างของaZmqStopwatch.start (); count (aRepresentation); aZmqStopwatch.stop () ?
3666197

ทำการทดสอบและใช่มีความแตกต่างอย่างมากในประสิทธิภาพของชุดข้อมูลจริง การทดสอบต้องมีความเข้าใจลึกซึ้งยิ่งขึ้นเกี่ยวกับกลไกภายในของไพ ธ อนมากกว่าการใช้ลูปสเกลกำลังดุร้ายและอ้างถึง nanoseconds ในหลอดทดลองที่ไม่สมจริง ตามที่ได้ทดสอบ - np.bincount ()สามารถจัดการกับอาร์เรย์ 150.000 ภายในน้อยกว่า 600 [เรา]ในขณะที่จำนวนdef -ed ด้านบน()ในการแสดงรายการก่อนการแปลงใช้มากกว่า122.000 [เรา]
user3666197

ใช่กฎของหัวแม่มือของฉันคือnumpyสำหรับสิ่งที่สามารถจัดการกับขนาดเล็กจำนวนแฝง แต่มีศักยภาพที่จะมีขนาดใหญ่มาก, รายการขนาดเล็กสำหรับชุดข้อมูลที่แฝงที่สำคัญและแน่นอนจริงเปรียบเทียบ FTW :)
เดวิด

1

บางสิ่งเช่นนี้ควรทำ:

#create 100 random numbers
arr = numpy.random.random_integers(0,50,100)

#create a dictionary of the unique values
d = dict([(i,0) for i in numpy.unique(arr)])
for number in arr:
    d[j]+=1   #increment when that value is found

นอกจากนี้โพสต์ก่อนหน้านี้ที่มีประสิทธิภาพใน การนับองค์ประกอบที่ไม่ซ้ำกันดูเหมือนสวยคล้ายกับคำถามของคุณเว้นแต่ฉันจะหายไปบางสิ่งบางอย่าง


คำถามที่เชื่อมโยงนั้นคล้ายกัน แต่ดูเหมือนว่าเขากำลังทำงานกับชนิดข้อมูลที่ซับซ้อนมากขึ้น
Abe

1

การนับความถี่แบบหลายมิติเช่นการนับอาร์เรย์

>>> print(color_array    )
  array([[255, 128, 128],
   [255, 128, 128],
   [255, 128, 128],
   ...,
   [255, 128, 128],
   [255, 128, 128],
   [255, 128, 128]], dtype=uint8)


>>> np.unique(color_array,return_counts=True,axis=0)
  (array([[ 60, 151, 161],
    [ 60, 155, 162],
    [ 60, 159, 163],
    [ 61, 143, 162],
    [ 61, 147, 162],
    [ 61, 162, 163],
    [ 62, 166, 164],
    [ 63, 137, 162],
    [ 63, 169, 164],
   array([     1,      2,      2,      1,      4,      1,      1,      2,
         3,      1,      1,      1,      2,      5,      2,      2,
       898,      1,      1,  


โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.