2017-04-26 6 views
1

Ich baue Simulationssoftware, und ich muss (tausende) 2D-Numpy-Arrays in Tabellen in einer HDF5-Datei schreiben, wobei eine Dimension des Arrays variabel ist. Der eingehende ist vom Typ float32; Um Speicherplatz zu sparen, wird jedes Array als Tabelle mit entsprechenden Datentypen für die Spalten gespeichert (daher keine Arrays). Wenn ich Tabellen lese, möchte ich ein numpy.darray des Typs float32 abrufen, damit ich schöne Berechnungen für die Analyse machen kann. Unten ist ein Beispielcode mit einem Array mit den Spezies A, B und C plus Zeit.Numerische Arrays von und zu HDF5-Dateien lesen und schreiben

Die Art, wie ich gerade lese und schreibe, funktioniert ', aber es ist sehr langsam. Die Frage ist also: Was ist die geeignete Art, array in table schnell zu speichern und auch wieder in ndarrays einzulesen? Ich habe mit numpy.recarray experimentiert, aber ich kann das nicht zum Laufen bringen (Typfehler, Dimensionsfehler, völlig falsche Zahlen usw.)?

Code:

import tables as pt 
import numpy as np 

# Variable dimension 
var_dim=100 

# Example array, rows 0 and 3 should be stored as float32, rows 1 and 2 as uint16 
array=(np.random.random((4, var_dim)) * 100).astype(dtype=np.float32) 

filename='test.hdf5' 
hdf=pt.open_file(filename=filename,mode='w') 
group=hdf.create_group(hdf.root,"group") 

particle={ 
    'A':pt.Float32Col(), 
    'B':pt.UInt16Col(), 
    'C':pt.UInt16Col(), 
    'time':pt.Float32Col(), 
    } 
dtypes=np.array([ 
    np.float32, 
    np.uint16, 
    np.uint16, 
    np.float32 
    ]) 

# This is the table to be stored in 
table=hdf.create_table(group,'trajectory', description=particle, expectedrows=var_dim) 

# My current way of storing 
for i, row in enumerate(array.T): 
    table.append([tuple([t(x) for t, x in zip(dtypes, row)])]) 
table.flush() 
hdf.close() 


hdf=pt.open_file(filename=filename,mode='r') 
array_table=hdf.root.group._f_iter_nodes().__next__() 

# My current way of reading 
row_list = [] 
for i, row in enumerate(array_table.read()): 
    row_list.append(np.array(list(row))) 

#The retreived array 
array=np.asarray(row_list).T 


# I've tried something with a recarray 
rec_array=array_table.read().view(type=np.recarray) 

# This gives me errors, or wrong results 
rec_array.view(dtype=np.float64) 
hdf.close() 

Der Fehler erhalte ich:

Traceback (most recent call last): 
    File "/home/thomas/anaconda3/lib/python3.6/site-packages/numpy/core/records.py", line 475, in __setattr__ 
    ret = object.__setattr__(self, attr, val) 
ValueError: new type not compatible with array. 

During handling of the above exception, another exception occurred: 

Traceback (most recent call last): 
    File "/home/thomas/Documents/Thesis/SO.py", line 53, in <module> 
    rec_array.view(dtype=np.float64) 
    File "/home/thomas/anaconda3/lib/python3.6/site-packages/numpy/core/records.py", line 480, in __setattr__ 
    raise exctype(value) 
ValueError: new type not compatible with array. 
Closing remaining open files:test.hdf5...done 
+0

Es könnte helfen, die Form und dtype von 'array' (von dem ersten sehen' asarray'). Ich vermute, es ist bereits ein strukturiertes Array. Oder die ähnlichen Informationen für die 'recarray' Version. – hpaulj

+0

Ist die Verwendung von Tabellen die einzige Lösung, die für Sie möglich ist? Wie greifen Sie danach auf Ihre Daten zu (nur die ganzen 2D-Arrays oder Subsets davon)? – max9111

+0

Gibt es auch nur wenige Spalten (in Ihrem Beispiel nur vier) in Ihren realen Daten? Sind Ihre Daten komprimierbar? Ist sogar eine verlustbehaftete Kompression eine Möglichkeit für Sie? https://computation.llnl.gov/projects/floating-point-compression/zfp-compression-ratio-and-quality – max9111

Antwort

2

Als schnelle und schmutzige Lösung ist es möglich, Schleifen aviod vorübergehend die Arrays auf Listen konvertieren (wenn Sie das ersparen Erinnerung). Aus irgendeinem Grund werden Aufzeichnungsarrays ohne weiteres in/aus Listen konvertiert, aber nicht zu/von herkömmlichen Arrays.

Speicher:

table.append(array.T.tolist()) 

Laden:

loaded_array = np.array(array_table.read().tolist(), dtype=np.float64).T 

Es soll ein "Numpythonic" Ansatz zwischen Plattenarrays und konventionellem Arrays zu konvertieren, aber ich bin nicht vertraut genug mit dem ehemaligen wissen wie.

+0

Das macht bereits den Code lesbarer! Ich frage mich immer noch, was der numpythonische Weg wäre. Trotzdem, danke! – Patrickens

+0

@Patrickens Ich habe gerade herausgefunden über [np.core.records.fromarrays] (https://docs.scipy.org/doc/numpy/reference/generated/numpy.core.records.fromarrays.html), aber ich don 't glaube nicht, dass es in diesem Fall einen Vorteil bringt. Intern konvertiert es das Array in eine Liste von Arrays, die weniger effizient als '.tolist()' ist und mehr Argumente benötigt. Vielleicht ist meine Herangehensweise nicht so Un-Numpythonisch :) – kazemakase

+0

In begrenzten Fällen kann 'view' oder' astype' verwendet werden, um strukturierte Arrays in numerische umzuwandeln, aber dieser 'tolist'-Vermittler ist das allgemeinste Mittel. Beachten Sie, dass für den umgekehrten Weg eine Liste von Listen in eine Liste von Tupeln umgewandelt werden muss. Eine Alternative besteht darin, Felder nach Namen zu kopieren. Da die Anzahl der Datensätze in der Regel im Vergleich zur Anzahl der Felder groß ist, verlieren Sie durch diese Iteration nicht viel. – hpaulj

1

Ich habe nicht mit tables gearbeitet, aber habe seine Dateien mit h5py angeschaut. Ich vermute, dann, dass Ihr array oder recarray ist eine strukturierte Anordnung mit dtype wie:

In [131]: dt=np.dtype('f4,u2,u2,f4') 
In [132]: np.array(arr.tolist(), float) 
Out[132]: 
array([[ 1., 1., 1., 1.], 
     [ 1., 1., 1., 1.], 
     [ 1., 1., 1., 1.]]) 
In [133]: arr 
Out[133]: 
array([(1., 1, 1, 1.), (1., 1, 1, 1.), (1., 1, 1, 1.)], 
     dtype=[('f0', '<f4'), ('f1', '<u2'), ('f2', '<u2'), ('f3', '<f4')]) 

Mit @kazemakase'stolist Ansatz (die ich in anderen Beiträgen empfohlen haben):

In [134]: np.array(arr.tolist(), float) 
Out[134]: 
array([[ 1., 1., 1., 1.], 
     [ 1., 1., 1., 1.], 
     [ 1., 1., 1., 1.]]) 

astype erhält der Form alle falsch

In [135]: arr.astype(np.float32) 
Out[135]: array([ 1., 1., 1.], dtype=float32) 

view funktioniert, wenn Komponente dtype s sind einheitlich, zum Beispiel mit den 2 Float-Feldern

In [136]: arr[['f0','f3']].copy().view(np.float32) 
Out[136]: array([ 1., 1., 1., 1., 1., 1.], dtype=float32) 

Aber es erfordert eine Umformung. view verwendet die Datenpuffer-Bytes, nur Neuinterpretation.

Viele recfunctions Funktionen verwenden eine feldweise Kopie. Hier wäre das Äquivalent

In [138]: res = np.empty((3,4),'float32') 
In [139]: for i in range(4): 
    ...:  res[:,i] = arr[arr.dtype.names[i]] 
    ...:  
In [140]: res 
Out[140]: 
array([[ 1., 1., 1., 1.], 
     [ 1., 1., 1., 1.], 
     [ 1., 1., 1., 1.]], dtype=float32) 

Wenn die Anzahl der Felder im Vergleich zur Anzahl der Datensätze klein ist, ist diese Iteration nicht teuer.


def foo(arr): 
    res = np.empty((arr.shape[0],4), np.float32) 
    for i in range(4): 
     res[:,i] = arr[arr.dtype.names[i]] 
    return res 

Mit einem großen 4-Feld-Array, das durch Feld Kopie ist deutlich schneller:

In [143]: arr = np.ones(10000, dtype=dt) 
In [149]: timeit x1 = foo(arr) 
10000 loops, best of 3: 73.5 µs per loop 
In [150]: timeit x2 = np.array(arr.tolist(), np.float32) 
100 loops, best of 3: 11.9 ms per loop