2017-04-27 8 views
0

Ich habe ein Dataset, das ich Zeile für Zeile in Cython gelesen habe. Jede Zeile wird als Zeichenfolge zurückgegeben. Was ich tun möchte, ist die Zeichenfolge in ein Array von Zahlen (Inte und Floats) mit der Länge der Anzahl der Spalten in jeder Zeile (die durch das Trennzeichen ';' gegeben ist) zu konvertieren.Cython - String in Ganzzahlen und Gleitkommazahlen konvertieren

Zum Beispiel

import pandas as pd 
import numpy as np 

df = pd.DataFrame(np.c_[np.random.rand(3,2),np.random.randint(0,10,(3,2))], columns = ['a','b','c','d']) 

filename = r'H:\mydata.csv' 
df.to_csv('filename',sep=';',index=False) 

Jetzt möchte ich zufällig über die Zeilen in cython iterieren und in jeder Zeile einige Berechnungen zu tun.

import numpy as np 
from readc_csv import row_pos, read_file_and_compute 

filename = r'H:\mydata.csv' 
row_position = row_pos(filename)[:-1] # returns the position of the start 
             # of each row in the file 
             # (excluding the header) 

rows = np.random.choice(row_position,size=len(row_position),replace=False) 
read_file_and_compute(filename,rows) 

Die readc_csv.pyx Datei sieht wie folgt aus

from libc.stdio cimport FILE, fopen, fgets, fclose, fseek, SEEK_SET, ftell 
import numpy as np 
cimport numpy as np 

def row_pos(str filename): 
    filename_byte_string = filename.encode("UTF-8") 

    cdef: 
     char* fname = filename_byte_string 
     FILE* cfile 
     char line[50] 
     list pos = [] 

    cfile = fopen(fname, "r") 

    while fgets(line, 50, cfile)!=NULL: 
     pos.append(ftell(cfile)) 

    fclose(cfile) 

    return pos    


def read_file_and_compute(str filename, int [:] rows): 
    filename_byte_string = filename.encode("UTF-8") 
    cdef: 
     char* fname = filename_byte_string 
     FILE* cfile 
     char line[50] 
     size_t j 
     int n = rows.shape[0] 

    cfile = fopen(fname, "r") 

    for j in range(n): 
     r = rows[j] 
     fseek(cfile,r,SEEK_SET) 
     fgets(line, 50, cfile) 

     # line is now e.g. 
     # '0.659933520847;0.471779123704;1.0;2.0\n' 
     # I want to convert it into an array with 4 elements 
     # each element corresponding to one of the numbers we 
     # see in the string 
     # and do some computations 


    fclose(cfile) 

    return 

(Hinweis: Der cython Code ist noch nicht optimierter) Hintergrundinformation: Dies ist Teil eines Skripts I für stochastische Gradienten schreiben wollen Abstieg auf einem Datensatz, der zu groß ist, um in den Speicher gelesen zu werden. Ich möchte die innere Schleife über die zufällig angeordneten Samples in Cython durchführen. Daher muss ich in der Lage sein, die Daten aus einer gegebenen Zeile in einer csv-Datei in Cython zu lesen.

+0

Dies ist, was ich denke, ist ein nützlicher Kommentar aus einer Antwort, die den Punkt verfehlt (und so habe ich gelöscht): Wenn Sie eine Binärdatei anstelle eines CSV verwenden können, dann hat [numpy eine Funktion namens Memory Mapped Arrays] (https://docs.scipy.org/doc/numpy/reference/generated/numpy.memmap.html), die dies für Binärdateien implementieren - das ist natürlich viel einfacher als das Schreiben Ihrer eigenen. – DavidW

+0

Ein zweiter Kommentar, der hilfreich sein kann: der folgende Python-Code wird funktionieren 'return np.array ([float (l) für l in str (line) .split (';')])'. Es ist nicht optimiert, aber Sie könnten es als Platzhalter verwenden, während Sie versuchen, etwas besseres zu finden. – DavidW

Antwort

0

Ich fand die c-Funktionen strtok und atof, die von libc.string und libc.stdlib importiert werden können. Sie machen den Trick.

Fortsetzung des obigen Beispiel die read_file_and_compute Funktion dann so etwas wie dieses

aussehen könnte
def read_file_and_compute(str filename, int [:] rows, int col_n): 
    filename_byte_string = filename.encode("UTF-8") 
    cdef: 
     char* fname = filename_byte_string 
     FILE* cfile 
     char line[50] 
     char *token 
     double *col = <double *>malloc(col_n * sizeof(double)) 
     size_t j, i 
     int count 
     double num 
     int n = rows.shape[0] 

    cfile = fopen(fname, "r") 

    for j in range(n): 
     r = rows[j] 
     fseek(cfile,r,SEEK_SET) 
     fgets(line, 50, cfile) 

     token = strtok(line, ';') # splits the string at the delimiter ';' 
     count = 0 
     while token!=NULL and count<col_n: 
      num = atof(token) # converts the string into a float 

      col[count] = num 
      token = strtok(NULL,';\n') 
      count +=1 

     # now do some computations on col ... 

fclose(cfile) 
free(col)  

return 

Es gibt mehr Funktionen für die Umwandlung von Strings in verschiedenen Typen finden Sie here.

+0

Ein Wort der Warnung: 'strtok' ist nicht garantiert threadsicher, also, wenn der Grund, warum Sie zu einer weitgehend C-basierten Implementierung gegangen ist, Dinge parallel zu betreiben, dann seien Sie vorsichtig! Wenn Sie nicht mehrere Versionen gleichzeitig ausführen, machen Sie sich keine Sorgen. – DavidW

Verwandte Themen