2014-09-25 21 views
7

Sayısal bir dizide benzersiz değerleri saymaya çalışıyorum.Sayısal olmayan bir dizideki benzersiz öğeleri sayma: neden scipy.stats.itemfreq çok yavaş?

import numpy as np 
from collections import defaultdict 
import scipy.stats 
import time 

x = np.tile([1,2,3,4,5,6,7,8,9,10],20000) 
for i in [44,22,300,403,777,1009,800]: 
    x[i] = 11 

def getCounts(x): 
    counts = defaultdict(int) 
    for item in x: 
     counts[item] += 1 
    return counts 

flist = [getCounts, scipy.stats.itemfreq] 

for f in flist: 
    print f 
    t1 = time.time() 
    y = f(x) 
    t2 = time.time() 
    print y 
    print '%.5f sec' % (t2-t1) 

Bunu yapmak için ilk başta bir yerleşik işlev bulamadık, bu yüzden getCounts() yazdım; Daha sonra scipy.stats.itemfreq buldum, bunun yerine bunu kullanacağımı düşündüm. Ama yavaş! İşte benim bilgisayarımda ne var. Böyle basit bir el yazısı işleviyle karşılaştırıldığında neden bu kadar yavaş? İlk

<function getCounts at 0x0000000013C78438> 
defaultdict(<type 'int'>, {1: 19998, 2: 20000, 3: 19999, 4: 19999, 5: 19999, 6: 20000, 7: 20000, 8: 19999, 9: 20000, 10: 19999, 11: 7}) 
0.04700 sec 
<function itemfreq at 0x0000000013C5D208> 
[[ 1.00000000e+00 1.99980000e+04] 
[ 2.00000000e+00 2.00000000e+04] 
[ 3.00000000e+00 1.99990000e+04] 
[ 4.00000000e+00 1.99990000e+04] 
[ 5.00000000e+00 1.99990000e+04] 
[ 6.00000000e+00 2.00000000e+04] 
[ 7.00000000e+00 2.00000000e+04] 
[ 8.00000000e+00 1.99990000e+04] 
[ 9.00000000e+00 2.00000000e+04] 
[ 1.00000000e+01 1.99990000e+04] 
[ 1.10000000e+01 7.00000000e+00]] 
2.04100 sec 
+3

Bu işlevde bir sorun bildirimi var [burada] (https://github.com/scipy/scipy/issues/599). Bakıcıların nasıl yazıldığına dair benzer endişeleri vardı gibi görünüyor. Neden bu kadar yavaş olduğunu bilmek istiyorsanız, belki de [bu taahhüt] 'e bakın (https://github.com/scipy/scipy/commit/7e04d6630f229693cca3522b62aa16226f174053). Bu işlemden önce veya sonra bir scipy sürümü kullanıp kullanmadığınıza bağlı olarak, nasıl uygulandığını görebilmeniz gerekir. – jedwards

cevap

14

Eğer numpy 1,9 kullanabilirsiniz varsa, argüman return_counts=True ile numpy.unique kullanabilirsiniz. Yani

unique_items, counts = np.unique(x, return_counts=True) 

Aslında itemfreqnp.unique kullanmak güncellendi, ancak şu anda geri 1.5 numpy sürümlerini desteklemektedir scipy, bu nedenle return_counts argüman kullanmaz oldu. yanıtlar için

def itemfreq(a): 
    items, inv = np.unique(a, return_inverse=True) 
    freq = np.bincount(inv) 
    return np.array([items, freq]).T 
+0

Ben numpy işlevinin hızını fark ettim. Scipy işlevinde neden kullanılmadığını merak ediyor. –

+0

Aslında, çok sayıda küçük tamsayıya sahip olduğum için, uygulamada 'np.bincount' kullanıyorum. –

3

, time.time o duvar saatini ölçer olarak zamanlama, değil cpu süresi (this question bakınız) kullanılacak yanlış işlevdir. İdeal olarak, timeit modülünü kullanırsınız, ancak time.clock da daha iyidir.

Ayrıca, eski bir scipy sürümü kullanıyor olabilirsiniz. Ben Python 3.4 ve scipy 0.14.0 kullanıyorum ve bu benim zamanlamaları şunlardır:

x = np.tile([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 20000) 
for i in [44, 22, 300, 403, 777, 1009, 800]: 
    x[i] = 11 

%timeit getCounts(x) 
# 10 loops, best of 3: 55.6 ms per loop 

%timeit scipy.stats.itemfreq(x) 
# 10 loops, best of 3: 20.8 ms per loop 

%timeit collections.Counter(x) 
# 10 loops, best of 3: 39.9 ms per loop 

%timeit np.unique(x, return_counts=True) 
# 100 loops, best of 3: 4.13 ms per loop 
+0

Tamam, haklı olabilirsin. Ben scipy 0.13.0 kullanıyorum –

0

Teşekkür:

İşte scipy 0.14 yılında itemfreq tam uygulanması bulunuyor. Henüz 1.9 numpy kullanabilir veya scipy 0,14 çünkü benim uygulamada bazı modül çatışmaların ancak yeni scipy.stats.itemfreq benziyor olamaz çok daha hızlıdır:

benim PC'de verir
import numpy as np 
from collections import defaultdict, Counter 
import scipy.stats 
import time 
import timeit 

x = np.tile([1,2,3,4,5,6,7,8,9,10],20000) 
for i in [44,22,300,403,777,1009,800]: 
    x[i] = 11 

def getCounts(x): 
    counts = defaultdict(int) 
    for item in x: 
     counts[item] += 1 
    return counts 

def itemfreq_scipy14(x): 
    '''this is how itemfreq works in 0.14: 
    https://github.com/scipy/scipy/commit/7e04d6630f229693cca3522b62aa16226f174053 
    ''' 
    items, inv = np.unique(x, return_inverse=True) 
    freq = np.bincount(inv) 
    return np.array([items, freq]).T 

flist = [getCounts, scipy.stats.itemfreq, np.bincount, itemfreq_scipy14, Counter] 


for f in flist: 
    print f 
    print timeit.timeit(lambda: f(x),number=3) 

:

<function getCounts at 0x0000000013F8EB38> 
0.148138969181 
<function itemfreq at 0x0000000013C5D208> 
6.15385023664 
<built-in function bincount> 
0.00313706656675 
<function itemfreq_scipy14 at 0x0000000013F8EDD8> 
0.0757223407165 
<class 'collections.Counter'> 
0.255281199559 
İlgili konular