[PYTHON] Verwenden Sie Cython mit Jupyter Notebook

Es ist ein Hinweis, weil ich festgestellt habe, dass ich Cython mit Jupyter Notebook (iPython Notebook) einfacher als erwartet ausprobieren kann. Cython beschleunigt die Verarbeitung, indem es vor der Ausführung kompiliert und statisch eingegeben wird.

(Die .ipynb-Datei für diesen Artikel wurde auf Github [hier] hochgeladen (https://github.com/matsuken92/Qiita_Contents/blob/master/General/Cython_test.ipynb).)

</ i> Umgebung

Die Umgebung, die ich ausprobiert habe, ist wie folgt. Ich versuche es auf Mac und Anaconda. Wenn Sie Anaconda installiert haben, ist keine besondere Vorbereitung erforderlich.

Python 3.5.1 |Anaconda custom (x86_64)| (default, Jun 15 2016, 16:14:02) 
[GCC 4.2.1 Compatible Apple LLVM 4.2 (clang-425.0.28)] on darwin 

IPython 5.0.0 

</ i> Probieren wir es aus

Führen Sie den magischen Befehl für die Cython-Kompilierung aus

#Ermöglichen das Kompilieren von Cython-Dateien auf Jupyter Notebook
%load_ext Cython

Deklarieren Sie Cython-Funktionen

Schreiben Sie den Cython-Code mit "%% Cython" am Anfang. Das Beispiel wird aus Cython Tutorial Basics verwendet.

# ↓ -n <Dateiname>Durch Hinzufügen wird es einfacher, die Datei später zu überprüfen.
%%cython -n test_cython_code
def fib(int n):
    cdef int i
    cdef double a=0.0, b=1.0

    for i in range(n):
        a, b = a+b, a
    return a

def primes(int kmax):
    cdef int n, k, i
    cdef int p[1000]
    result = []

    if kmax > 1000:
        kmax = 1000

    k = 0
    n = 2
    while k < kmax:
        i = 0
        while i < k and n % p[i] != 0:
            i += 1

        if i == k:
            p[k] = n
            k += 1
            result.append(n)
        n += 1
    return result

Ausprobieren

print(fib(90))
print(primes(20))

out


2.880067194370816e+18
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71]

erledigt!

</ i> Geschwindigkeitsvergleich mit rohem Python

Schreiben wir den gleichen Prozess in rohem Python und vergleichen die Ausführungszeiten.

import numpy as np
#Python-Funktion zum Leistungsvergleich
def pyfib(n):
    a, b = 0.0, 1.0
    for i in range(n):
        a, b = a+b, a
    return a

def pyprimes(kmax):
    p = np.zeros(1000)
    result = []

    #Die maximale Anzahl beträgt 1000
    if kmax > 1000:
        kmax = 1000

    k = 0
    n = 2
    while k < kmax:
        i = 0
        while i < k and n % p[i] != 0:
            i += 1

        if i == k:
            p[k] = n
            k += 1
            result.append(n)
        n += 1
    return result

Fibonacci-Nummer

#Generieren und messen Sie wiederholt die 1000. Fibonacci-Zahl
%timeit fib(1000)
%timeit pyfib(1000)

Cython ist ungefähr 50 mal schneller!

out


1000000 loops, best of 3: 786 ns per loop
10000 loops, best of 3: 42.5 µs per loop

Extrahieren Sie eine Primzahl

%timeit primes(1000)
%timeit pyprimes(1000)

out


100 loops, best of 3: 2.12 ms per loop
1 loop, best of 3: 218 ms per loop

Diese Berechnung ist ungefähr 100 mal schneller!

</ i> Versuchen Sie, es für Pandas zu verwenden

1000 ganze Zahlen

df = pd.DataFrame(np.arange(1, 10**4), columns=['num'] )

Sie können es einfach verwenden, indem Sie die Funktion in der Apply-Funktion angeben: erröten:

%timeit df['fib'] = df.num.apply(fib)
%timeit df['pyfib'] = df.num.apply(pyfib)

out


10 loops, best of 3: 39.2 ms per loop
1 loop, best of 3: 2.02 s per loop
print(df.head())

out


   num  fib  pyfib
0    1  1.0    1.0
1    2  1.0    1.0
2    3  2.0    2.0
3    4  3.0    3.0
4    5  5.0    5.0

Die kompilierte Cython-Datei wird in ~ / .ipython / cython gespeichert. Wenn beim Kompilieren ein Dateiname mit %% cython -n angegeben wurde, wird er hier mit diesem Dateinamen gespeichert.

</ i> Behandle ndarray

#Daten erstellen
rd.seed(71)
n_data = 10**5
X = pd.DataFrame(rd.normal(size=3*n_data).reshape((n_data,3)), columns=["a", "b", "c"])
print(X.shape)
print(X.head())

out


(100000, 3)
          a         b         c
0 -0.430603 -1.193928 -0.444299
1  0.489412 -0.451557  0.585696
2  1.177320 -0.965009  0.218278
3 -0.866144 -0.323006  1.412919
4 -0.712651 -1.362191 -1.705966

Schreiben Sie Cython-Code, der ndarray als Argument verwendet

%%cython -n sample_calc 
import numpy as np
cimport numpy as np

cpdef np.ndarray[double] sample_calc(np.ndarray col_a, np.ndarray col_b, np.ndarray col_c):
    #Typprüfung für jede Spalte
    assert (col_a.dtype == np.float and col_b.dtype == np.float and col_c.dtype == np.float)
    
    #Überprüfen Sie, ob die Größe jeder Spalte gleich ist
    cdef Py_ssize_t n = len(col_c)
    assert (len(col_a) == len(col_b) == n)
    cdef np.ndarray[double] res = np.empty(n)
    
    # (a-b)/Berechnen Sie c
    for i in range(n):
        res[i] = (col_a[i] - col_b[i])/col_c[i]
    return res

Anruf von Python Seite

sample_calc(X.a.values, X.b.values, X.c.values)

out


array([-1.71804336,  1.60658332,  9.81468496, ..., -0.44683095,
        0.46970409, -0.28352272])
#Zum Vergleich
def pysample_calc(col_a, col_b, col_c):
    #Typprüfung für jede Spalte
    assert (col_a.dtype == np.float and col_b.dtype == np.float and col_c.dtype == np.float)
    
    #Überprüfen Sie, ob die Größe jeder Spalte gleich ist
    n = len(col_c)
    assert (len(col_a) == len(col_b) == n)
    res = np.empty(n)
    
    # (a-b)/Berechnen Sie c
    for i in range(n):
        res[i] = (col_a[i] - col_b[i])/col_c[i]
    return res
%timeit sample_calc(X.a.values, X.b.values, X.c.values)
%timeit pysample_calc(X.a.values, X.b.values, X.c.values)

out


100 loops, best of 3: 16.7 ms per loop
10 loops, best of 3: 37.2 ms per loop

</ i> Berechnen Sie das Umfangsverhältnis nach der Monte-Carlo-Methode

#Datengenerierung
rd.seed(71)
n_data = 10**7
X2 = rd.random(size=(n_data,2)).astype(np.float)
X2.dtype

Definition der Cython-Funktion

%%cython -n calc_pi
import numpy as np
cimport numpy as np

cpdef np.ndarray[long]  calc_pi(np.ndarray[double, ndim=2] data):
    cdef Py_ssize_t n = len(data)
    cdef np.ndarray[long] res = np.empty(n, dtype=np.int)
    
    for i in range(n):
        res[i] = 1 if (data[i,0]**2 + data[i,1]**2) < 1 else 0
    return res

Python-Funktion zum Vergleich

#Python-Funktion zum Vergleich
def pycalc_pi(data):
    n = len(data)
    res = [1 if (data[i,0]**2 + data[i,1]**2) < 1 else 0 for i in range(n)]
    return res

Ich werde es messen.

%time calc_pi(X2)
%time pycalc_pi(X2)

out


CPU times: user 25.2 ms, sys: 5.98 ms, total: 31.2 ms
Wall time: 31.1 ms
CPU times: user 7.7 s, sys: 46.1 ms, total: 7.75 s
Wall time: 7.75 s

Cython ist viel schneller!

#Überprüfen Sie, ob die Ergebnisse identisch sind
np.all(res == respy)

richtig!

out


True
#Berechnung des Umfangsverhältnisses
np.sum(res)/n_data*4

out


3.1413555999999998

Versuchen Sie zu zeichnen.

import matplotlib.pyplot as plt
%matplotlib inline
import seaborn as sns
from matplotlib.colors import LinearSegmentedColormap

sns.set(style="darkgrid", palette="muted", color_codes=True)
#zeichnen
n_plot = 10**4  #Anzahl der zu ziehenden Punkte
plt.figure(figsize=(8,8))
plt.scatter(X2[:n_plot,0], X2[:n_plot,1], c=res[:n_plot], s=10)

plot.png

Sie beurteilen das Innere und Äußere des Kreises richtig.

Referenz

Grundlagen des Cython-Tutorials http://omake.accense.com/static/doc-ja/cython/src/userguide/tutorial.html

O'Reilly "Cython" https://www.oreilly.co.jp/books/9784873117270/

pandas 0.18.1 documentation Enhancing Performance http://pandas.pydata.org/pandas-docs/stable/enhancingperf.html

Recommended Posts