2015-06-11 3 views
7

Hier ist ein Beispiel-Datenrahmen:Save Pandas Datenrahmen mit h5py für interoperabilty mit anderen hdf5 Lesern

import pandas as pd 

NaN = float('nan') 
ID = [1, 2, 3, 4, 5, 6, 7] 
A = [NaN, NaN, NaN, 0.1, 0.1, 0.1, 0.1] 
B = [0.2, NaN, 0.2, 0.2, 0.2, NaN, NaN] 
C = [NaN, 0.5, 0.5, NaN, 0.5, 0.5, NaN] 
columns = {'A':A, 'B':B, 'C':C} 
df = pd.DataFrame(columns, index=ID) 
df.index.name = 'ID' 
print(df) 

     A B C 
ID    
1 NaN 0.2 NaN 
2 NaN NaN 0.5 
3 NaN 0.2 0.5 
4 0.1 0.2 NaN 
5 0.1 0.2 0.5 
6 0.1 NaN 0.5 
7 0.1 NaN NaN 

Ich weiß, dass Pandas die pytables Basis HDFStore hat, die eine einfache Möglichkeit, ein Datum effizient zu serialisiert/deserialisiert Rahmen. Aber diese Datensätze sind nicht sehr einfach direkt mit einem Reader h5py oder Matlab zu laden. Wie kann ich einen Datenrahmen mit h5py speichern, so dass ich ihn einfach mit einem anderen hdf5 reader laden kann?

Antwort

6

The Pandas HDFStore Format ist Standard HDF5, mit nur einer Konvention für die Interpretation der Metadaten. Docs sind here

In [54]: df.to_hdf('test.h5','df',mode='w',format='table',data_columns=True) 

In [55]: h = h5py.File('test.h5') 

In [56]: h['df']['table'] 
Out[56]: <HDF5 dataset "table": shape (7,), type "|V32"> 

In [64]: h['df']['table'][:] 
Out[64]: 
array([(1, nan, 0.2, nan), (2, nan, nan, 0.5), (3, nan, 0.2, 0.5), 
     (4, 0.1, 0.2, nan), (5, 0.1, 0.2, 0.5), (6, 0.1, nan, 0.5), 
     (7, 0.1, nan, nan)], 
     dtype=[('index', '<i8'), ('A', '<f8'), ('B', '<f8'), ('C', '<f8')]) 


In [57]: h['df']['table'].attrs.items() 
Out[57]: 
[(u'CLASS', 'TABLE'), 
(u'VERSION', '2.7'), 
(u'TITLE', ''), 
(u'FIELD_0_NAME', 'index'), 
(u'FIELD_1_NAME', 'A'), 
(u'FIELD_2_NAME', 'B'), 
(u'FIELD_3_NAME', 'C'), 
(u'FIELD_0_FILL', 0), 
(u'FIELD_1_FILL', 0.0), 
(u'FIELD_2_FILL', 0.0), 
(u'FIELD_3_FILL', 0.0), 
(u'index_kind', 'integer'), 
(u'A_kind', "(lp1\nS'A'\na."), 
(u'A_meta', 'N.'), 
(u'A_dtype', 'float64'), 
(u'B_kind', "(lp1\nS'B'\na."), 
(u'B_meta', 'N.'), 
(u'B_dtype', 'float64'), 
(u'C_kind', "(lp1\nS'C'\na."), 
(u'C_meta', 'N.'), 
(u'C_dtype', 'float64'), 
(u'NROWS', 7)] 

In [58]: h.close() 

Die Daten in jedem HDF5 reader vollständig lesbar ist. Einige der Metadaten sind gebeizt, daher ist Vorsicht geboten.

+1

Ich hatte die Standardargumente des 'festen' Formats verwendet, ohne data_columns zu setzen, das eine viel andere und abstrakter aussehende hdf5 Datei hat, als wenn format = 'table', data_columns = True verwendet wird. Was die Pandas-Dokumentation zur externen Kompatibilität mit HDFStore betrifft, lese ich sie erneut und würde Ihre Antwort nie von dieser Beschreibung aus sehen. Vielen Dank für Ihre klare und sehr hilfreiche Antwort! – Phil

+0

Wenn Sie diesem Doku-Beispiel noch weitere Kommentare hinzufügen möchten, wäre das eine tolle Sache! Bitte senden Sie eine Pull-Anfrage – Jeff

+0

Ich möchte zum Doc-Beispiel hinzufügen, aber ich habe Probleme. Eine Folgefrage finden Sie unter http://StackOverflow.com/questions/30807270. – Phil

7

Hier ist mein Ansatz zur Lösung dieses Problems. Ich hoffe, dass jemand anderes eine bessere Lösung hat oder mein Ansatz anderen hilft.

Definieren Sie zunächst die Funktion, um aus einem Pandas DataFrame ein numpy Struktur-Array (kein Record-Array) zu machen.

import numpy as np 
def df_to_sarray(df): 
    """ 
    Convert a pandas DataFrame object to a numpy structured array. 
    This is functionally equivalent to but more efficient than 
    np.array(df.to_array()) 

    :param df: the data frame to convert 
    :return: a numpy structured array representation of df 
    """ 

    v = df.values 
    cols = df.columns 
    types = [(cols[i].encode(), df[k].dtype.type) for (i, k) in enumerate(cols)] 
    dtype = np.dtype(types) 
    z = np.zeros(v.shape[0], dtype) 
    for (i, k) in enumerate(z.dtype.names): 
     z[k] = v[:, i] 
    return z 

Verwenden reset_index, um einen neuen Datenrahmen zu bilden, der den Index als Teil seiner Daten enthält. Konvertieren Sie diesen Datenrahmen in ein Struktur-Array.

sa = df_to_sarray(df.reset_index()) 
sa 

array([(1L, nan, 0.2, nan), (2L, nan, nan, 0.5), (3L, nan, 0.2, 0.5), 
     (4L, 0.1, 0.2, nan), (5L, 0.1, 0.2, 0.5), (6L, 0.1, nan, 0.5), 
     (7L, 0.1, nan, nan)], 
     dtype=[('ID', '<i8'), ('A', '<f8'), ('B', '<f8'), ('C', '<f8')]) 

speichern, die strukturierte Anordnung zu einer hdf5 Datei.

import h5py 
with h5py.File('mydata.h5', 'w') as hf: 
      hf['df'] = sa 

laden h5-Datensatz

with h5py.File('mydata.h5') as hf: 
      sa2 = hf['df'][:] 

die ID-Spalte extrahieren und aus SA2 löschen

ID = sa2['ID'] 
sa2 = nprec.drop_fields(sa2, 'ID') 

Make-Datenrahmen mit dem Index ID unter Verwendung SA2

df2 = pd.DataFrame(sa2, index=ID) 
df2.index.name = 'ID' 

print(df2) 

     A B C 
ID    
1 NaN 0.2 NaN 
2 NaN NaN 0.5 
3 NaN 0.2 0.5 
4 0.1 0.2 NaN 
5 0.1 0.2 0.5 
6 0.1 NaN 0.5 
7 0.1 NaN NaN 
+1

Ziemlich nette Antwort und genau das, was ich suchte.Wenn ich Standard-Pandas benutze, um eine hdf5-Datei zu erstellen, erstellt sie eine hdf5-Datei mit vielen Tabellen, was nicht wirklich praktisch ist. Daher bevorzuge ich es mit 'h5py', aber mit den Daten zu arbeiten Pandas. Wenn ich nur mehr als 1 Upvote zu deiner Antwort machen könnte, würde ich es tun, Danke! ;) – silgon

+0

Danke für die Ermutigung und die Verbesserung! Meine Antwort ist niedriger und kundenspezifischer als die von Jeff, deren Antwort ich akzeptierte, weil sie keinen benutzerdefinierten Code erfordert und meinem Anwendungsfall besser entspricht als dieser benutzerdefinierte Ansatz. Ich bin jedoch sehr froh, dass dieser Code für Sie hilfreich ist. – Phil

1

Im Fall ist es hilfreich für jedermann, nahm ich this post von Guillaume und Phil und änderte es ein bisschen für meine Bedürfnisse mit Hilfe von ankostis. Wir lesen den Pandas DataFrame aus einer CSV-Datei.

Hauptsächlich habe ich es für Strings angepasst, weil Sie ein Objekt in einer HDF5-Datei (glaube ich) nicht speichern können. Prüfen Sie zuerst, welche Spaltenarten numpy objects sind. Überprüfen Sie dann, welche Spalte die längste Länge hat und fixieren Sie diese Spalte als String dieser Länge. Der Rest ist dem anderen sehr ähnlich.

def df_to_sarray(df): 
    """ 
    Convert a pandas DataFrame object to a numpy structured array. 
    Also, for every column of a str type, convert it into 
    a 'bytes' str literal of length = max(len(col)). 

    :param df: the data frame to convert 
    :return: a numpy structured array representation of df 
    """ 

    def make_col_type(col_type, col): 
     try: 
      if 'numpy.object_' in str(col_type.type): 
       maxlens = col.dropna().str.len() 
       if maxlens.any(): 
        maxlen = maxlens.max().astype(int) 
        col_type = ('S%s' % maxlen, 1) 
       else: 
        col_type = 'f2' 
      return col.name, col_type 
     except: 
      print(col.name, col_type, col_type.type, type(col)) 
      raise 

    v = df.values    
    types = df.dtypes 
    numpy_struct_types = [make_col_type(types[col], df.loc[:, col]) for col in df.columns] 
    dtype = np.dtype(numpy_struct_types) 
    z = np.zeros(v.shape[0], dtype) 
    for (i, k) in enumerate(z.dtype.names): 
     # This is in case you have problems with the encoding, remove the if branch if not 
     try: 
      if dtype[i].str.startswith('|S'): 
       z[k] = df[k].str.encode('latin').astype('S') 
      else: 
       z[k] = v[:, i] 
     except: 
      print(k, v[:, i]) 
      raise 

    return z, dtype 

So würde der Workflow sein:

import h5py 
import pandas as pd 

# Read a CSV file 
# Here we assume col_dtypes is a dictionary that contains the dtypes of the columns 
df = pd.read_table('./data.csv', sep='\t', dtype=col_dtypes) 
# Transform the DataFrame into a structured numpy array and get the dtype 
sa, saType = df_to_sarray(df) 

# Open/create the HDF5 file 
f = h5py.File('test.hdf5', 'a') 
# Save the structured array 
f.create_dataset('someData', data=sa, dtype=saType) 
# Retrieve it and check it is ok when you transform it into a pandas DataFrame 
sa2 = f['someData'][:] 
df2 = pd.DataFrame(sa2) 
print(df2.head()) 
f.close() 

Auch auf diese Weise Sie es sehen können aus HDFView selbst wenn gzip Kompression zum Beispiel verwendet wird.

+0

Schöne Lösung. Es funktioniert für mich auf Python3. –

Verwandte Themen