2012-11-02 11 views
24

Es gibt Beispiele für here mit C benutzerdefinierten numpy dtypes erstellen:Wie ein benutzerdefinierten erstellen numpy mit cython dtype

Zusätzlich ist es seems to be possible zu erstellen individuelle ufuncs in cython:

Es scheint, wie es auch sein sollte möglich, einen dtype mit Cython zu erstellen (und dann benutzerdefinierte ufuncs dafür zu erstellen). Ist es möglich? Wenn ja, können Sie ein Beispiel veröffentlichen?

USE CASE:

Ich möchte einige Überlebensanalyse zu tun. Die grundlegenden Datenelemente sind Überlebenszeiten (Gleitkommazahlen) mit zugeordneten Zensorwerten (Falsch, wenn die zugehörige Zeit eine Fehlerzeit darstellt, und Wahr, wenn sie stattdessen eine Zensierungszeit darstellt (d. H. Kein Fehler trat während des Beobachtungszeitraums auf)).

Offensichtlich könnte ich einfach zwei numpy Arrays verwenden, um diese Werte zu speichern: ein Float-Array für die Zeiten und ein Bool-Array für die Zensor-Werte. Ich möchte jedoch auf die Möglichkeit hinweisen, dass ein Ereignis mehrere Male auftritt (dies ist ein gutes Modell für beispielsweise Herzinfarkte - Sie können mehrere haben). In diesem Fall brauche ich ein Array von Objekten, die ich MultiEvent s rufe. Jede MultiEvent enthält eine Folge von Floats (unzensierte Fehlerzeiten) und eine Beobachtungsperiode (auch Float). Beachten Sie, dass die Anzahl der Fehler nicht für alle MultiEvent s gleich ist.

Ich muss in der Lage, ein paar Operationen auf einer Reihe von MultiEvent s auszuführen:

  1. Erhalten Sie die Anzahl der Fehler für jeden

  2. die zensierte Zeit Brauchen Sie (das ist der Zeitraum von Beobachtung minus der Summe aller Fehlerzeiten)

  3. Berechnen Sie eine Log-Likelihood basierend auf zusätzlichen Parameterarrays (z. B. einem Array von Gefahrenwerten). Zum Beispiel wäre das Log-Likelihood für einen einzelnen MultiEventM und konstanten Gefahrenwert h so etwas wie:

    sum(log(h) + h*t for t in M.times) - h*(M.period - sum(M.times))

wo M.times die Liste (Array, was auch immer) von Ausfallzeiten und M.period ist die Gesamtbeobachtungszeitraum. Ich mag die richtigen numpy Rundfunk-Regeln anzuwenden, so dass ich tun kann:

log_lik = logp(M_vec,h_vec) 

und es wird so lange wie die Abmessungen von M_vec und h_vec sind kompatibel zu arbeiten.

Meine aktuelle Implementierung verwendet numpy.vectorize. Das funktioniert gut genug für 1 und 2, aber es ist zu langsam für 3. Beachten Sie auch, dass ich this nicht tun kann, weil die Anzahl der Fehler in meinen MultiData-Objekten nicht vorher bekannt ist.

+1

Ist Ihr Grund zu fragen, weil Sie finden das Schreiben cython einfacher als C zu schreiben? Ich vermute, dass, wenn es möglich ist (was ich nicht weiß), Sie mit Code enden werden, der genauso komplex und unordentlich ist wie C, also kann es keinen Nutzen geben. – DaveP

+2

@DaveP Es gibt zwei Gründe. Eine ist, dass ich es einfacher finde, in Cython als C zu schreiben. Die andere ist, dass ich diesen Prozess für Python-Programmierer leicht machen möchte, um sie für neue dtypes und ufuncs zu wiederholen. Ich hoffe, dass ich den größten Teil der Komplexität umhüllen kann und dtypes in Cython einfach machen kann. Das heißt, Cython ist etwas, über das ich erst letzte Woche erfahren habe. Ich habe damit gespielt, aber zu diesem Zeitpunkt verstehe ich seine Fähigkeiten nicht vollständig. – jcrudy

+0

Es wäre nett, einen Anwendungsfall zu haben, damit wir Ihnen helfen können. – jlengrand

Antwort

0

Ich entschuldige mich dafür, die Frage nicht direkt zu beantworten, aber ich hatte ähnliche Probleme zuvor, und wenn ich richtig verstehe, ist das wirkliche Problem, das Sie jetzt haben, dass Sie Daten variabler Länge haben, die wirklich, wirklich ist nicht eine der Stärken von Numpy, und das ist der Grund, warum Sie Leistungsprobleme auftreten.Wenn Sie die maximale Anzahl von Einträgen für ein Multievent nicht im Voraus kennen, haben Sie Probleme und selbst dann verschwenden Sie viel Speicherplatz/Nullen für Ereignisse, die keine Multi-Events sind.

Sie haben Datenpunkte mit mehr als einem Feld, von denen einige zu anderen Feldern gehören und von denen einige in Gruppen identifiziert werden müssen. Dies weist stark darauf hin, dass Sie eine Datenbank in irgendeiner Form zum Speichern dieser Informationen aus Gründen der Leistung, des Speichers, des Speicherplatzes auf der Festplatte und der Vernunft in Betracht ziehen sollten.

Es wird für eine Person, die neu in Ihrem Code ist, viel einfacher sein, ein einfaches Datenbankschema zu verstehen als eine komplizierte, gehackte-auf-numpy Struktur, die frustrierend langsam und aufgebläht sein wird. SQL-Abfragen sind im Vergleich schnell und einfach zu schreiben.

Ich würde vorschlagen, basierend auf meinem Verständnis Ihrer Erklärung mit Event-und MultiEvent-Tabellen, wo jeder Event-Eintrag hat einen Fremdschlüssel in die MultiEvent-Tabelle, wo relevant.

+0

Das Problem mit dieser Lösung ist, dass ich dann nicht die große Sammlung von Tools verwenden konnte, die bereits auf numpy erstellt wurden. Eine Erweiterung dieser Antwort wäre die Erstellung eines numpy arrays (oder eines pandas DataFrame) basierend auf einem Join zwischen meiner MultiEvent-Tabelle und der Tabelle, die meine skalaren Variablen enthält. Für meinen Zweck (mit einem bestimmten Satz von Pymc-Modellen) ist das leider auch keine Option. – jcrudy

+0

@ user1572508 hast du etwas wie [PyTables] (http://www.pytables.org/moin) in Betracht gezogen? – jozzas

1

Numpy-Arrays eignen sich am besten für Datentypen mit fester Größe. Wenn die Objekte im Array keine feste Größe haben (z. B. Ihr MultiEvent), können die Vorgänge viel langsamer werden.

Ich würde Ihnen empfehlen, alle Überlebenszeiten in einem 1d linearen Datensatzarray mit 3 Feldern zu speichern: event_id, time, period. Jedes Ereignis kann mutliple mal im Array:

>>> import numpy as np 
>>> rawdata = [(1, 0.4, 4), (1, 0.6, 6), (2,2.6, 6)] 
>>> npdata = np.rec.fromrecords(rawdata, names='event_id,time,period') 
>>> print npdata 
[(1, 0.40000000000000002, 4) (1, 0.59999999999999998, 6) (2, 2.6000000000000001, 6)] 

Um Daten für einen bestimmten Index bekommen könnten Sie Lust Indizierung verwenden:

>>> eventdata = npdata[npdata.event_id==1] 
>>> print eventdata 
[(1, 0.40000000000000002, 4) (1, 0.59999999999999998, 6)] 

Der Vorteil dieses Ansatzes ist, dass Sie leicht intergrate mit Ihre NDarray-basierten Funktionen. Sie können auch diese Arrays von cython zugreifen, wie in der manual beschrieben:

cdef packed struct Event: 
    np.int32_t event_id 
    np.float64_t time 
    np.float64_6 period 

def f(): 
    cdef np.ndarray[Event] b = np.zeros(10, 
     dtype=np.dtype([('event_id', np.int32), 
         ('time', np.float64), 
         ('period', np.float64)])) 
    <...> 
Verwandte Themen