2017-05-08 4 views
2

내가 큰 항목의 개수에 dataframe 잡고 매핑 사용자 (인덱스) (열)이 각 사용자에 대해커스텀 그룹핑과 관련된 판다 계산을 벡터화하는 방법은 무엇입니까?

users_items = pd.DataFrame(np.array([[0, 1, 1, 0],  # user 0 
            [1, 0, 0, 0],  # user 1 
            [5, 0, 0, 9],  # user 2 
            [0, 3, 5, 0],  # user 3 
            [0, 2, 2, 0],  # user 4 
            [7, 0, 0, 1],  # user 5 
            [3, 5, 0, 4]]), # user 6 
          columns=list('ABCD')) 

을, 나는 적어도 동일한 0이 아닌 수를 가진 모든 사용자를 찾으려면 항목 수를 합산합니다. 따라서 사용자 1의 경우 사용자 1, 2, 5 및 6이고 개수 합은 [16, 5, 0, 14]입니다. 이것은 "유사한"사용자가 얻은 항목을 기반으로 사용자에게 새 항목을 제안하는 데 사용할 수 있습니다.

이 순진 구현은 관련 행을 필터링하는 정규 표현식으로 서명을 사용하고 모든 서명을 통해 루프 루프 :

def create_signature(request_counts): 
    return ''.join('x' if count else '.' for count in request_counts) 

users_items['signature'] = users_items.apply(create_signature, axis=1).astype('category') 

current_items = users_items.groupby('signature').sum() 

similar_items = pd.DataFrame(index=current_items.index, 
          columns=current_items.columns) 

for signature in current_items.index: 
    row = current_items.filter(regex=signature, axis='index').sum() 
    similar_items.loc[signature] = row 

결과는 다음과 같습니다

  A B C D 
signature    
.xx.  0 6 8 0 
x...  16 5 0 14 
x..x  15 5 0 14 
xx.x  3 5 0 4 

이 작품 실제로 100k 사용자와 약 600 개의 항목으로 구성된 실제 데이터 세트에는 너무 느립니다. 서명을 생성하는 데는 10 초 밖에 걸리지 않지만 모든 (40k) 서명을 반복하는 데는 몇 시간이 걸립니다.

루프를 벡터화하면 성능이 크게 향상되지만 팬더에 대한 경험이 제한되어 있으므로 어떻게해야하는지 잘 모르겠습니다. 이 계산 유형을 벡터화 할 수 있습니까? 마스크를 쓰고 있니? 대신 서명으로 string

+0

에 결국

for signature in current_items.index: row = current_items[signature <= current_items.index].sum() similar_items.loc[signature] = row 

결과를 필터를 벡터화 할 수 있습니다 .index

전에 reset_index()에 대한 호출을 삽입있다 예를 들어 4 개의 서명 만 있습니까? .x .., .xxx, x.xx 등과 같은 다른 조합이 필요합니까? 이 4 가지는 왜 선택 되었습니까? – Allen

+0

@Allen 서명 세트는 users_items 데이터 프레임에 어떤 데이터가 발생했는지에 따라 간단히 생성됩니다. 가능한 모든 서명이 발생하지는 않습니다. –

+0

그래서 'x ...'는 서명의 하위 집합 인 모든 행의 합계 여야합니다. –

답변

0

, 당신은 내가 하나가보다 빠른 여부를 확인하기에 충분히 큰 데이터 집합이없는 frozenset

def create_signature(request_counts): 
    return frozenset(request_counts[request_counts != 0].index) 

대안

def create_signature(request_counts): 
    return frozenset(request_counts.replace({0: None}).dropna().index) 

입니다 사용할 수 있습니다 다른.

중복 열이있는 경우, 이것은 당신이 왜

signature     A B C D 
frozenset({'B', 'C'})  0 6 8 0 
frozenset({'A'})   16 5 0 14 
frozenset({'A', 'D'})  15 5 0 14 
frozenset({'B', 'A', 'D'}) 3 5 0 4 
+0

나는 frozensets (나는 비트 배열을 생각하고 있었다) 대신에 문자열을 사용하는 것이 훨씬 더 빠를 것이라고 생각했을 것이다. 불행히도, frozenset 서명을 생성하는 데 훨씬 오래 걸립니다 (2 초 이상 10 초). 즉, fronzesets를 사용하면 루프가 8 시간이 아니라 6 시간 만 걸릴 것이므로 확실한 개선이 있습니다. 그러나, for 루프 만 벡터화하면이 작업을 충분히 빠르게 수행 할 수 있다고 생각합니다. –

+0

for 루프는'set' 비교가'regex'보다 빠르기 때문에 문자열 1만큼 오래 걸립니다. 비트 배열이 더 빠를 수도 있습니다. –

+0

내 의견을 제출 한 후 했었습니다. 나는 그것을 지금 새롭게했다. –