2014-09-02 8 views
46

Poniższy kod działa dobrze. Po prostu sprawdzam: czy używam i wyczuwam Pandy prawidłowo i czy jest jakiś szybszy sposób? Dzięki.Czy to najszybszy sposób na grupowanie w Pandach?

$ python3 
Python 3.4.0 (default, Apr 11 2014, 13:05:11) 
[GCC 4.8.2] on linux 
Type "help", "copyright", "credits" or "license" for more information. 
>>> import pandas as pd 
>>> import numpy as np 
>>> import timeit 
>>> pd.__version__ 
'0.14.1' 

def randChar(f, numGrp, N) : 
    things = [f%x for x in range(numGrp)] 
    return [things[x] for x in np.random.choice(numGrp, N)] 

def randFloat(numGrp, N) : 
    things = [round(100*np.random.random(),4) for x in range(numGrp)] 
    return [things[x] for x in np.random.choice(numGrp, N)] 

N=int(1e8) 
K=100 
DF = pd.DataFrame({ 
    'id1' : randChar("id%03d", K, N),  # large groups (char) 
    'id2' : randChar("id%03d", K, N),  # large groups (char) 
    'id3' : randChar("id%010d", N//K, N), # small groups (char) 
    'id4' : np.random.choice(K, N),   # large groups (int) 
    'id5' : np.random.choice(K, N),   # large groups (int) 
    'id6' : np.random.choice(N//K, N),  # small groups (int)    
    'v1' : np.random.choice(5, N),   # int in range [1,5] 
    'v2' : np.random.choice(5, N),   # int in range [1,5] 
    'v3' : randFloat(100,N)    # numeric e.g. 23.5749 
}) 

Teraz 5 różnych grup, powtarzając dwa razy, aby potwierdzić rozrządu. [Rozumiem, że timeit(2) uruchamia go dwa razy, ale potem raportuje sumę. Interesuje mnie czas pierwszego i drugiego uruchomienia osobno.] Python używa około 10G pamięci RAM zgodnie z htop podczas tych testów.

>>> timeit.Timer("DF.groupby(['id1']).agg({'v1':'sum'})"       ,"from __main__ import DF").timeit(1) 
5.604133386000285 
>>> timeit.Timer("DF.groupby(['id1']).agg({'v1':'sum'})"       ,"from __main__ import DF").timeit(1) 
5.505057081000359 

>>> timeit.Timer("DF.groupby(['id1','id2']).agg({'v1':'sum'})"      ,"from __main__ import DF").timeit(1) 
14.232032927000091 
>>> timeit.Timer("DF.groupby(['id1','id2']).agg({'v1':'sum'})"      ,"from __main__ import DF").timeit(1) 
14.242601240999647 

>>> timeit.Timer("DF.groupby(['id3']).agg({'v1':'sum', 'v3':'mean'})"    ,"from __main__ import DF").timeit(1) 
22.87025260900009 
>>> timeit.Timer("DF.groupby(['id3']).agg({'v1':'sum', 'v3':'mean'})"    ,"from __main__ import DF").timeit(1) 
22.393589012999655 

>>> timeit.Timer("DF.groupby(['id4']).agg({'v1':'mean', 'v2':'mean', 'v3':'mean'})" ,"from __main__ import DF").timeit(1) 
2.9725865330001398 
>>> timeit.Timer("DF.groupby(['id4']).agg({'v1':'mean', 'v2':'mean', 'v3':'mean'})" ,"from __main__ import DF").timeit(1) 
2.9683854739996605 

>>> timeit.Timer("DF.groupby(['id6']).agg({'v1':'sum', 'v2':'sum', 'v3':'sum'})" ,"from __main__ import DF").timeit(1) 
12.776488024999708 
>>> timeit.Timer("DF.groupby(['id6']).agg({'v1':'sum', 'v2':'sum', 'v3':'sum'})" ,"from __main__ import DF").timeit(1) 
13.558292575999076 

Oto informacje o systemie:

$ lscpu 
Architecture:   x86_64 
CPU op-mode(s):  32-bit, 64-bit 
Byte Order:   Little Endian 
CPU(s):    32 
On-line CPU(s) list: 0-31 
Thread(s) per core: 2 
Core(s) per socket: 8 
Socket(s):    2 
NUMA node(s):   2 
Vendor ID:    GenuineIntel 
CPU family:   6 
Model:     62 
Stepping:    4 
CPU MHz:    2500.048 
BogoMIPS:    5066.38 
Hypervisor vendor:  Xen 
Virtualization type: full 
L1d cache:    32K 
L1i cache:    32K 
L2 cache:    256K 
L3 cache:    25600K 
NUMA node0 CPU(s):  0-7,16-23 
NUMA node1 CPU(s):  8-15,24-31 

$ free -h 
      total  used  free  shared buffers  cached 
Mem:   240G  74G  166G  372K  33M  550M 
-/+ buffers/cache:  73G  166G 
Swap:   0B   0B   0B 

Nie wierzę, że to istotne, ale tylko w przypadku, funkcja randChar powyżej jest obejście błędu pamięci w mtrand.RandomState.choice:

How to solve memory error in mtrand.RandomState.choice?

+4

'df.groupby' jest całkiem dobrze zoptymalizowany. Jakie alternatywy bierzesz pod uwagę? Jedyne co mogę wymyślić to ustawić kolumny 'id' jako indeks, a następnie użyć' df.groupby (level = id_whatever) '. –

+0

@PaulH Dzięki temu wypróbuję kolumny 'id' jako indeks. Porównywam do 'data.table 'R.' (który utrzymuję). –

+0

oh fajnie. drugą rzeczą, o której wspomnę, jest to, że robienie tego w Notatniku IPython i używanie magii "timeit" prawdopodobnie utrzyma część twojego zdrowia psychicznego. http://nbviewer.ipython.org/github/ipython/ipython/blob/1.x/examples/notebooks/Cell%20Magics.ipynb#-Some-simple-cell-magics –

Odpowiedz

4

Jeśli chcesz zainstalować powłokę iPython, możesz łatwo wyminić kod za pomocą% timeit. Po zainstalowaniu, zamiast wpisywania python w celu uruchomienia interpretera python, należy wpisać ipython.

Następnie możesz wpisać swój kod dokładnie tak, jak wpisałeś go w zwykłym tłumaczu (tak jak to zrobiłeś powyżej).

Wtedy można wpisać na przykład:

%timeit DF.groupby(['id1']).agg({'v1':'sum'}) 

Będzie to osiągnąć dokładnie to samo, co zrobiłeś, ale jeśli używasz Pythona dużo Uważam, że będzie to zaoszczędzić znaczące czas pisania :).

ipython ma wiele innych przydatnych funkcji (jak %paste, którego użyłem, aby wkleić w kodzie i przetestować, czy %run uruchomić skrypt już zapisany w pliku), zakończenie zakładki itp http://ipython.org/

Powiązane problemy