2015-11-26 10 views
10

Mam ramka danych Pandy tworzone w następujący sposób:Szybka alternatywa uruchomić funkcję opartego numpy nad wszystkich wierszy w Pandy DataFrame

import pandas as pd 
def create(n): 
    df = pd.DataFrame({ 'gene':["foo", 
          "bar", 
          "qux", 
          "woz"], 
          'cell1':[433.96,735.62,483.42,10.33], 
          'cell2':[94.93,2214.38,97.93,1205.30], 
          'cell3':[1500,90,100,80]}) 
    df = df[["gene","cell1","cell2","cell3"]] 
    df = pd.concat([df]*n) 
    df = df.reset_index(drop=True) 
    return df 

wygląda to tak:

In [108]: create(1) 
Out[108]: 
    gene cell1 cell2 cell3 
0 foo 433.96 94.93 1500 
1 bar 735.62 2214.38  90 
2 qux 483.42 97.93 100 
3 woz 10.33 1205.30  80 

Potem posiada funkcję, która przyjmuje wartości każdego z genów (rzędzie) obliczyć pewną ocenę:

enter image description here

import numpy as np 
def sparseness(xvec): 
    n = len(xvec) 
    xvec_sum = np.sum(np.abs(xvec)) 
    xvecsq_sum = np.sum(np.square(xvec)) 

    denom = np.sqrt(n) - (xvec_sum/np.sqrt(xvecsq_sum)) 
    enum = np.sqrt(n) - 1 
    sparseness_x = denom/enum 

    return sparseness_x 

W rzeczywistości muszę zastosować tę funkcję na 40K nad wierszami. I obecnie pracuje bardzo powolne pomocą Pandy „Zastosuj”:

In [109]: df = create(10000) 
In [110]: express_df = df.ix[:,1:] 
In [111]: %timeit express_df.apply(sparseness, axis=1) 
1 loops, best of 3: 8.32 s per loop 

Jaka jest szybsza alternatywa do wdrożenia tego?

Odpowiedz

12

Szybszym sposobem jest zaimplementowanie wektorowej wersji funkcji, która działa bezpośrednio na dwuwymiarowym ndarray. Jest to bardzo wykonalne, ponieważ wiele funkcji w numpy może działać na dwuwymiarowej ndarray, kontrolowanej za pomocą parametru axis. Ewentualna realizacja:

def sparseness2(xs): 
    nr = np.sqrt(xs.shape[1]) 
    a = np.sum(np.abs(xs), axis=1) 
    b = np.sqrt(np.sum(np.square(xs), axis=1)) 
    sparseness = (nr - a/b)/(nr - 1) 
    return sparseness 

res_arr = sparseness2(express_df.values) 
res2 = pd.Series(res_arr, index=express_df.index) 

Niektóre badania:

from pandas.util.testing import assert_series_equal 
res1 = express_df.apply(sparseness, axis=1) 
assert_series_equal(res1, res2) #OK 
%timeit sparseness2(express_df.values) 
# 1000 loops, best of 3: 655 µs per loop 
8

Oto jeden wektorowy podejście używając np.einsum wykonywać wszystkie te czynności za jednym razem na całej dataframe. Teraz ta np.einsum jest podobno całkiem skuteczna do takich celów mnożenia i sumowania. W naszym przypadku możemy go użyć do wykonania podsumowania wzdłuż jednego wymiaru dla przypadku xvec_sum i kwadratury oraz sumy dla przypadku xvecsq_sum. Implmentation wyglądałby następująco -

def sparseness_vectorized(A): 
    nsqrt = np.sqrt(A.shape[1]) 
    B = np.einsum('ij->i',np.abs(A))/np.sqrt(np.einsum('ij,ij->i',A,A))  
    denom = nsqrt - B 
    enum = nsqrt - 1 
    return denom/enum 

Runtime testy -

Ta sekcja porównuje wszystkie wymienione dotychczas podejścia do rozwiązania problemu w tym jeden w pytaniu.

In [235]: df = create(1000) 
    ...: express_df = df.ix[:,1:] 
    ...: 

In [236]: %timeit express_df.apply(sparseness, axis=1) 
1 loops, best of 3: 1.36 s per loop 

In [237]: %timeit sparseness2(express_df.values) 
1000 loops, best of 3: 247 µs per loop 

In [238]: %timeit sparseness_vectorized(express_df.values) 
1000 loops, best of 3: 231 µs per loop 



In [239]: df = create(5000) 
    ...: express_df = df.ix[:,1:] 
    ...: 

In [240]: %timeit express_df.apply(sparseness, axis=1) 
1 loops, best of 3: 6.66 s per loop 

In [241]: %timeit sparseness2(express_df.values) 
1000 loops, best of 3: 1.14 ms per loop 

In [242]: %timeit sparseness_vectorized(express_df.values) 
1000 loops, best of 3: 1.06 ms per loop