2014-04-28 6 views
0

Lorsque j'utilise des pandas pour convertir des fichiers CSV en fichiers hdf5, le fichier résultant est extrêmement volumineux. Par exemple, un fichier csv de test (23 colonnes, 1,3 million de lignes) de 170 Mo donne un fichier hdf5 de 2 Go. Cependant, si les pandas sont contournés et que le fichier hdf5 est directement écrit (en utilisant pytables), il ne fait que 20Mb. Dans le code suivant (qui est utilisé pour effectuer la conversion en pandas géants) les valeurs des colonnes d'objet dans la trame de données sont converties explicitement à des objets de chaîne (pour éviter le décapage):Conversion d'un fichier CSV en HDF5 en utilisant des pandas

# Open the csv file as pandas data frame 
data = pd.read_csv(csvfilepath, sep=delimiter, low_memory=False) 

# Write the resulting data frame to the hdf5 file 
data.to_hdf(hdf5_file_path, table_name, format='table', complevel=9, 
      complib='lzo') 

C'est le fichier hdf5 inspecté (en utilisant vitables):

ce qui semble étrange pour moi est que les valeurs sont représentées comme (python) liste par type de données (values_block0: int, values_block1: flotteur et values_block2: string) au lieu de 1 colonne spécifique pour chaque colonne dans le fichier csv. Je me demande si cela provoque la taille du fichier et quel sera l'impact sur les temps de requête? Étant donné qu'il faut convertir environ 1 To, j'aimerais savoir ce que l'on peut faire pour réduire la taille du fichier hdf5 résultant?

P.S. Je suis au courant de ce question mais indique que la taille du fichier hdf5 est due au format HDF5 lui-même, ce qui ne peut pas être la cause dans ce cas puisque le fichier hdf5 résultant du contournement des pandas est beaucoup plus petit.

P.P.S. L'utilisation de data.iloc au lieu de data.loc comme suggéré par joris ne fait aucune différence. J'ai supprimé la 'conversion' cela ne fait aucune différence. Les informations sur la lecture dataframe comme demandé par Jeff:

<class 'pandas.core.frame.DataFrame'> 
Int64Index: 1303331 entries, 0 to 1303330 
Columns: 23 entries, _PlanId to ACTIVITY_Gratis 
dtypes: float64(1), int64(5), object(17) 
+0

Il y a certainement quelque chose qui ne va pas dans votre conversion, car vous ne devriez pas avoir de telles listes dans votre dataframe. Premièrement, les 'str_cols' sont des emplacements entiers. Dans ce cas, vous devriez utiliser 'data.iloc [: str_cols] =' au lieu de 'data.loc [..]'. Est-ce que cela le résout déjà? – joris

+0

show '' df.info() '' juste après avoir lu le fichier csv; vous n'avez pas besoin de faire la «conversion» que vous faites. – Jeff

+0

voir ces questions: http: // stackoverflow.com/questions/20428355/appending-column-to-frame-of-hdf-file-in-pandas/20428786 # 20428786, et docs: http://pandas.pydata.org/pandas-docs/stable/io.html – Jeff

Répondre

2

Here's une comparaison informelle de fois/tailles pour diverses méthodes IO

En utilisant 0.13.1 64 bits linux

Setup

In [3]: N = 1000000 

In [4]: df = DataFrame(dict([ ("int{0}".format(i),np.random.randint(0,10,size=N)) for i in range(5) ])) 

In [5]: df['float'] = np.random.randn(N) 

In [6]: from random import randrange 

In [8]: for i in range(10): 
    ...:  df["object_1_{0}".format(i)] = ['%08x'%randrange(16**8) for _ in range(N)] 
    ...:  

In [9]: for i in range(7): 
    ...:  df["object_2_{0}".format(i)] = ['%15x'%randrange(16**15) for _ in range(N)] 
    ...:  

In [11]: df.info() 
<class 'pandas.core.frame.DataFrame'> 
Int64Index: 1000000 entries, 0 to 999999 
Data columns (total 23 columns): 
int0   1000000 non-null int64 
int1   1000000 non-null int64 
int2   1000000 non-null int64 
int3   1000000 non-null int64 
int4   1000000 non-null int64 
float   1000000 non-null float64 
object_1_0 1000000 non-null object 
object_1_1 1000000 non-null object 
object_1_2 1000000 non-null object 
object_1_3 1000000 non-null object 
object_1_4 1000000 non-null object 
object_1_5 1000000 non-null object 
object_1_6 1000000 non-null object 
object_1_7 1000000 non-null object 
object_1_8 1000000 non-null object 
object_1_9 1000000 non-null object 
object_2_0 1000000 non-null object 
object_2_1 1000000 non-null object 
object_2_2 1000000 non-null object 
object_2_3 1000000 non-null object 
object_2_4 1000000 non-null object 
object_2_5 1000000 non-null object 
object_2_6 1000000 non-null object 
dtypes: float64(1), int64(5), object(17) 

types: float64(1), int64(5), object(17) 

Enregistrement avec diverses méthodes

In [12]: df.to_hdf('test_fixed.h5','data',format='fixed') 

In [13]: df.to_hdf('test_table_no_dc.h5','data',format='table') 

In [14]: df.to_hdf('test_table_dc.h5','data',format='table',data_columns=True) 

In [15]: df.to_hdf('test_fixed_compressed.h5','data',format='fixed',complib='blosc',complevel=9) 
!ls -ltr *.h5 

In [16]: !ls -ltr *.h5 
-rw-rw-r-- 1 jreback users 361093304 Apr 28 09:20 test_fixed.h5 
-rw-rw-r-- 1 jreback users 311475690 Apr 28 09:21 test_table_no_dc.h5 
-rw-rw-r-- 1 jreback users 351316525 Apr 28 09:22 test_table_dc.h5 
-rw-rw-r-- 1 jreback users 317467870 Apr 28 2014 test_fixed_compressed.h5 

La taille sur le disque va être fonction de la taille de la chaîne sélectionnée pour chaque colonne; Si vous utilisez NO data_columns, c'est la taille la plus longue pour CHAQUE chaîne. Donc, écrire avec data_columns peut potentiellement avoir la taille ici (équilibré par le fait que vous avez plus de colonnes donc cela prend plus d'espace par colonne). Vous prob voulez spécifier min_item_size pour contrôler voir here

Voici un exemple du disque sur la structure:

In [8]: DataFrame(dict(A = ['foo','bar','bah'], B = [1,2,3], C = [1.0,2.0,3.0], D=[4.0,5.0,6.0])).to_hdf('test.h5','data',mode='w',format='table') 

In [9]: !ptdump -avd test.h5 
/(RootGroup) '' 
    /._v_attrs (AttributeSet), 4 attributes: 
    [CLASS := 'GROUP', 
    PYTABLES_FORMAT_VERSION := '2.1', 
    TITLE := '', 
    VERSION := '1.0'] 
/data (Group) '' 
    /data._v_attrs (AttributeSet), 14 attributes: 
    [CLASS := 'GROUP', 
    TITLE := '', 
    VERSION := '1.0', 
    data_columns := [], 
    encoding := None, 
    index_cols := [(0, 'index')], 
    info := {1: {'type': 'Index', 'names': [None]}, 'index': {}}, 
    levels := 1, 
    nan_rep := 'nan', 
    non_index_axes := [(1, ['A', 'B', 'C', 'D'])], 
    pandas_type := 'frame_table', 
    pandas_version := '0.10.1', 
    table_type := 'appendable_frame', 
    values_cols := ['values_block_0', 'values_block_1', 'values_block_2']] 
/data/table (Table(3,)) '' 
    description := { 
    "index": Int64Col(shape=(), dflt=0, pos=0), 
    "values_block_0": Float64Col(shape=(2,), dflt=0.0, pos=1), 
    "values_block_1": Int64Col(shape=(1,), dflt=0, pos=2), 
    "values_block_2": StringCol(itemsize=3, shape=(1,), dflt='', pos=3)} 
    byteorder := 'little' 
    chunkshape := (1872,) 
    autoindex := True 
    colindexes := { 
    "index": Index(6, medium, shuffle, zlib(1)).is_csi=False} 
    /data/table._v_attrs (AttributeSet), 19 attributes: 
    [CLASS := 'TABLE', 
    FIELD_0_FILL := 0, 
    FIELD_0_NAME := 'index', 
    FIELD_1_FILL := 0.0, 
    FIELD_1_NAME := 'values_block_0', 
    FIELD_2_FILL := 0, 
    FIELD_2_NAME := 'values_block_1', 
    FIELD_3_FILL := '', 
    FIELD_3_NAME := 'values_block_2', 
    NROWS := 3, 
    TITLE := '', 
    VERSION := '2.7', 
    index_kind := 'integer', 
    values_block_0_dtype := 'float64', 
    values_block_0_kind := ['C', 'D'], 
    values_block_1_dtype := 'int64', 
    values_block_1_kind := ['B'], 
    values_block_2_dtype := 'string24', 
    values_block_2_kind := ['A']] 
    Data dump: 
[0] (0, [1.0, 4.0], [1], ['foo']) 
[1] (1, [2.0, 5.0], [2], ['bar']) 
[2] (2, [3.0, 6.0], [3], ['bah']) 

Dtypes se regroupent en blocs (si vous avez data_columns ils sont alors séparés). Ceux-ci sont simplement imprimés de cette façon; ils sont stockés comme tableau.

Questions connexes