2011-10-17 2 views
4

나는 과거에 dealing with averaging two paired lists에 직면 해 있으며 성공적으로 제공된 답변을 사용했습니다.NumPy를 사용하여 파이썬에서 두 개의 쌍으로 구성된 목록의 평균 중복 값

그러나 큰 (20,000 개가 넘는) 항목에서는 절차가 다소 느리고 NumPy를 사용하면 속도가 빨라지는지 궁금합니다. 나는 그것을 적용한 후, 내가 할 거라고, 그래서 동일한 값의 평균을 계산하기 위해 노력하고있어

names = ["a", "b", "b", "c", "d", "e", "e"] 
values = [1.2, 4.5, 4.3, 2.0, 5.67, 8.08, 9.01] 

:

나는 두 개의리스트, 수레 중 하나 문자열 중 하나에서 시작

result_names = ["a", "b", "c", "d", "e"] 
result_values = [1.2, 4.4, 2.0, 5.67, 8.54] 

나는 결과 예를 들어 두 개의 목록을 넣어뿐만 아니라 (name, value) 튜플의리스트를 가진 것은 충분 :

result = [("a", 1.2), ("b", 4.4), ("d", 5.67), ("e", 8.54)] 
,

NumPy에서이 작업을 수행하는 가장 좋은 방법은 무엇입니까?

답변

4

numpy를 사용하여 직접 작성하거나 groupby 기능 (matplotlib.mlab의 rec_groupby 기능을 사용할 수 있지만 훨씬 더 느립니다.보다 강력한 groupby 기능을 보려면 pandas을 참조하십시오)과 사전 마이클 던의 대답은 :

import numpy as np 
import random 
from matplotlib.mlab import rec_groupby 

listA = [random.choice("abcdef") for i in range(20000)] 
listB = [20 * random.random() for i in range(20000)] 

names = np.array(listA) 
values = np.array(listB) 

def f_dict(listA, listB): 
    d = {} 

    for a, b in zip(listA, listB): 
     d.setdefault(a, []).append(b) 

    avg = [] 
    for key in d: 
     avg.append(sum(d[key])/len(d[key])) 

    return d.keys(), avg 

def f_numpy(names, values): 
    result_names = np.unique(names) 
    result_values = np.empty(result_names.shape) 

    for i, name in enumerate(result_names): 
     result_values[i] = np.mean(values[names == name]) 

    return result_names, result_values  

이 세 가지의 결과입니다

In [2]: f_dict(listA, listB) 
Out[2]: 
(['a', 'c', 'b', 'e', 'd', 'f'], 
[9.9003182717213765, 
    10.077784850173568, 
    9.8623915728699636, 
    9.9790599744319319, 
    9.8811096512807097, 
    10.118695410115953]) 

In [3]: f_numpy(names, values) 
Out[3]: 
(array(['a', 'b', 'c', 'd', 'e', 'f'], 
     dtype='|S1'), 
array([ 9.90031827, 9.86239157, 10.07778485, 9.88110965, 
     9.97905997, 10.11869541])) 

In [7]: rec_groupby(struct_array, ('names',), (('values', np.mean, 'resvalues'),)) 
Out[7]: 
rec.array([('a', 9.900318271721376), ('b', 9.862391572869964), 
     ('c', 10.077784850173568), ('d', 9.88110965128071), 
     ('e', 9.979059974431932), ('f', 10.118695410115953)], 
     dtype=[('names', '|S1'), ('resvalues', '<f8')]) 

그리고 그 NumPy와 조금 더 빨리이 테스트 것 같다 (및 미리 정의 된 groupby 함수) 훨씬 느린 :

In [32]: %timeit f_dict(listA, listB) 
10 loops, best of 3: 23 ms per loop 

In [33]: %timeit f_numpy(names, values) 
100 loops, best of 3: 9.78 ms per loop 

In [8]: %timeit rec_groupby(struct_array, ('names',), (('values', np.mean, 'values'),)) 
1 loops, best of 3: 203 ms per loop 
+0

numpy가 그럴 가치가있는 것 같습니다. 귀하의 스크립트가이 150 배를 수행한다면 dict 솔루션은 ~ 2 초의 지연을 초래합니다. –

+1

그러나 타이밍에 나는 목록의 numpy 배열로의 변환을 세지 않았다. 그리고 이것은 numpy (나는 위의 경우에서 그것을 테스트 한 다음 f_numpy가 거의 같은 속도를가집니다 : 19.3ms)에서 약간의 시간 증가를 보완 할 수 있습니다. 어쩌면 목록을 매번 배열로 변환해야하는지 여부에 달려 있습니다. – joris

+0

테스트가 진행되는 한, 전환 목록 -> 배열에 큰 영향을 미치지는 않지만 두 버전 간의 포괄적 인 비교는 실행하지 않았습니다. – Einar

3

아마도 numpy 솔루션은 필요한 것보다 더 정교합니다. 20000 여부에 따라

import random 

listA = [random.choice("abcdef") for i in range(20000)] 
listB = [20 * random.random() for i in range(20000)] 

d = {} 

for a, b in zip(listA, listB): 
    d.setdefault(a, []).append(b) 

for key in d: 
    print key, sum(d[key])/len(d[key]) 

귀하의 사용량이 다를 수 있습니다 : 공상 아무것도하지 않고, 나는이 "플래시 빨리"로 다음의 발견 (같이 목록에서 20000 개 항목이 더 눈에 띄는 대기가 없었다) 목록의 일반적인 길이이며, 스크립트에서이 작업을 두 번 수행하는지 또는 수백/수천 번 수행하는지 여부를 나타냅니다.

+0

내가 말했어 야해, 당신 말이 맞아. 나는 이것을 약 150 번하고 평균 길이는 약 20K이다. – Einar

0

다소 늦게 파티에,하지만 NumPy와 같이 보는 것은 여전히 ​​키를 기준으로 그룹화를 달성하기 위해 순수한 NumPy와 솔루션에서 최선 시도가 여기에이 기능이 부족한 것 같다. 상당한 크기의 문제 세트에 대해 제안 된 다른 솔루션보다 훨씬 빠릅니다. 여기에 핵심은 멋진 reduceat 기능입니다.

import numpy as np 

def group(key, value): 
    """ 
    group the values by key 
    returns the unique keys, their corresponding per-key sum, and the keycounts 
    """ 
    #upcast to numpy arrays 
    key = np.asarray(key) 
    value = np.asarray(value) 
    #first, sort by key 
    I = np.argsort(key) 
    key = key[I] 
    value = value[I] 
    #the slicing points of the bins to sum over 
    slices = np.concatenate(([0], np.where(key[:-1]!=key[1:])[0]+1)) 
    #first entry of each bin is a unique key 
    unique_keys = key[slices] 
    #sum over the slices specified by index 
    per_key_sum = np.add.reduceat(value, slices) 
    #number of counts per key is the difference of our slice points. cap off with number of keys for last bin 
    key_count = np.diff(np.append(slices, len(key))) 
    return unique_keys, per_key_sum, key_count 


names = ["a", "b", "b", "c", "d", "e", "e"] 
values = [1.2, 4.5, 4.3, 2.0, 5.67, 8.08, 9.01] 

unique_keys, per_key_sum, key_count = group(names, values) 
print per_key_sum/key_count 
0

VA0 의해 인덱싱된다 numpy.arrays로 VA0 및 VB0 가정 NumPy와 통해 간단한 해결책.

import numpy as np 

def avg_group(vA0, vB0): 
    vA, ind, counts = np.unique(vA0, return_index=True, return_counts=True) # get unique values in vA0 
    vB = vB0[ind] 
    for dup in vB[counts>1]: # store the average (one may change as wished) of original elements in vA0 reference by the unique elements in vB 
     vB[np.where(vA==dup)] = np.average(vB0[np.where(vA0==dup)]) 
    return vA, vB 
관련 문제