cómo hacer que la operación de “poner” pandas HDFStore sea más rápida

Estoy tratando de construir un conjunto de herramientas ETL con pandas, hdf5.

Mi plan era

  1. extrayendo una tabla de mysql a un DataFrame;
  2. poner este DataFrame en un HDFStore;

Pero cuando estaba haciendo el paso 2, descubrí que poner un dataframe en un archivo * .h5 cuesta demasiado tiempo.

  • el tamaño de la tabla en el servidor mysql de origen: 498MB
    • 52 columnas
    • 924,624 registros
  • el tamaño del archivo * .h5 después de colocar el dataframe dentro: 513MB
    • la operación ‘put’ cuesta 849.345677137 segundos

Mis preguntas son:
¿Este tiempo cuesta normal?
¿Hay alguna manera de hacerlo más rápido?


Actualización 1

gracias jeff

  • Mis códigos son bastante simples:

    extract_store = HDFStore (‘extract_store.h5’)
    extract_store [‘df_staff’] = df_staff

  • y cuando probaba ‘ptdump -av file.h5’, recibí un error, pero aún podía cargar el objeto de dataframe desde este archivo h5:

tables.exceptions.HDF5ExtError: seguimiento de error HDF5

Archivo “../../../src/H5F.c”, línea 1512, en H5Fopen
no se pudo abrir el archivo Archivo “../../../src/H5F.c”, línea 1307, en H5F_open
no se puede leer el archivo del superbloque “../../../src/H5Fsuper.c”, línea 305, en H5F_super_read
incapaz de encontrar la firma del archivo Archivo “../../../src/H5Fsuper.c”, línea 153, en H5F_locate_signature
incapaz de encontrar una firma de archivo válida

Final de la traza de error de HDF5

No se puede abrir / crear el archivo ‘extract_store.h5’

  • algunos otros infos:
    • Versión de pandas: ‘0.10.0’
    • os: servidor ubuntu 10.04 x86_64
    • cpu: 8 * CPU Intel (R) Xeon (R) X5670 a 2.93GHz
    • MemTotal: 51634016 kB

Actualizaré los pandas a 0.10.1-dev e intentaré nuevamente.


Actualización 2

  • Había actualizado los pandas a ‘0.10.1.dev-6e2b6ea’
  • pero el tiempo no se redujo, esta vez cuesta 884.15 s.
  • La salida de ‘ptdump -av file.h5’ es:
     / (RootGroup) ''  
       /._v_attrs (AttributeSet), 4 atributos:  
        [CLASE: = 'GRUPO',  
         PYTABLES_FORMAT_VERSION: = '2.0',  
         TITLE: = '',  
         VERSIÓN: = '1.0']  
     / df_bugs (Grupo) ''  
       /df_bugs._v_attrs (AttributeSet), 12 atributos:  
        [CLASE: = 'GRUPO',  
         TITLE: = '',  
         VERSIÓN: = '1.0',  
         axis0_variety: = 'regular',  
         axis1_variety: = 'regular',  
         block0_items_variety: = 'regular',  
         block1_items_variety: = 'regular',  
         block2_items_variety: = 'regular',  
         nblocks: = 3,  
         ndim: = 2,  
         pandas_type: = 'frame',  
         pandas_version: = '0.10.1']  
     / df_bugs / axis0 (Array (52,)) ''  
       atom: = StringAtom (itemize = 19, shape = (), dflt = '')  
       maindim: = 0  
       sabor: = 'numpy'  
       byteorder: = 'irrelevante'  
       chunkshape: = ninguno  
       /df_bugs/axis0._v_attrs (AttributeSet), 7 atributos:  
        [CLASE: = 'ARRAY',  
         SABOR: = 'numpy',  
         TITLE: = '',  
         VERSIÓN: = '2.3',  
         kind: = 'string',  
         nombre: = ninguno,  
         transpuesto: = Verdadero]  
     / df_bugs / axis1 (Array (924624,)) ''  
       átomo: = Int64Atom (forma = (), dflt = 0)  
       maindim: = 0  
       sabor: = 'numpy'  
       byteorder: = 'little'  
       chunkshape: = ninguno  
       /df_bugs/axis1._v_attrs (AttributeSet), 7 atributos:  
        [CLASE: = 'ARRAY',  
         SABOR: = 'numpy',  
         TITLE: = '',  
         VERSIÓN: = '2.3',  
         kind: = 'integer',  
         nombre: = ninguno,  
         transpuesto: = Verdadero]  
     / df_bugs / block0_items (Array (5,)) ''  
       atom: = StringAtom (itemize = 12, shape = (), dflt = '')  
       maindim: = 0   
       sabor: = 'numpy'  
       byteorder: = 'irrelevante'  
       chunkshape: = ninguno  
       /df_bugs/block0_items._v_attrs (AttributeSet), 7 atributos:  
        [CLASE: = 'ARRAY',  
         SABOR: = 'numpy',  
         TITLE: = '',  
         VERSIÓN: = '2.3',  
         kind: = 'string',  
         nombre: = ninguno,  
         transpuesto: = Verdadero]  
     / df_bugs / block0_values ​​(Array (924624, 5)) ''  
       átomo: = Float64Atom (forma = (), dflt = 0.0)  
       maindim: = 0  
       sabor: = 'numpy'  
       byteorder: = 'little'  
       chunkshape: = ninguno  
       /df_bugs/block0_values._v_attrs (AttributeSet), 5 atributos:  
        [CLASE: = 'ARRAY',  
         SABOR: = 'numpy',  
         TITLE: = '',  
         VERSIÓN: = '2.3',  
         transpuesto: = Verdadero]  
     / df_bugs / block1_items (Array (19,)) ''  
       atom: = StringAtom (itemize = 19, shape = (), dflt = '')  
       maindim: = 0  
       sabor: = 'numpy'  
       byteorder: = 'irrelevante'  
       chunkshape: = ninguno  
       /df_bugs/block1_items._v_attrs (AttributeSet), 7 atributos:  
        [CLASE: = 'ARRAY',  
         SABOR: = 'numpy',  
         TITLE: = '',  
         VERSIÓN: = '2.3',  
         kind: = 'string',  
         nombre: = ninguno,  
         transpuesto: = Verdadero]  
     / df_bugs / block1_values ​​(Array (924624, 19)) ''  
       átomo: = Int64Atom (forma = (), dflt = 0)  
       maindim: = 0  
       sabor: = 'numpy'  
       byteorder: = 'little'  
       chunkshape: = ninguno  
       /df_bugs/block1_values._v_attrs (AttributeSet), 5 atributos:  
        [CLASE: = 'ARRAY',  
         SABOR: = 'numpy',  
         TITLE: = '',   
         VERSIÓN: = '2.3',  
         transpuesto: = Verdadero]  
     / df_bugs / block2_items (Array (28,)) ''  
       atom: = StringAtom (itemize = 18, shape = (), dflt = '')  
       maindim: = 0  
       sabor: = 'numpy'  
       byteorder: = 'irrelevante'  
       chunkshape: = ninguno  
       /df_bugs/block2_items._v_attrs (AttributeSet), 7 atributos:  
        [CLASE: = 'ARRAY',  
         SABOR: = 'numpy',  
         TITLE: = '',  
         VERSIÓN: = '2.3',
         kind: = 'string',  
         nombre: = ninguno,  
         transpuesto: = Verdadero]  
     / df_bugs / block2_values ​​(VLArray (1,)) ''  
       atom = ObjectAtom ()  
       byteorder = 'irrelevante'  
       nows = 1  
       sabor = 'numpy'  
       /df_bugs/block2_values._v_attrs (AttributeSet), 5 atributos:  
        [CLASE: = 'VLARRAY',  
         PSEUDOATOM: = 'objeto',  
         TITLE: = '',   
         VERSIÓN: = '1.3',  
         transpuesto: = Verdadero]  
  • y probé su código a continuación (poner el dataframe en hdfstore con la ‘tabla’ de parámetros es Verdadero), pero en su lugar se produjo un error, parecía que el tipo de fecha y hora de python no era compatible:

Excepción: no se puede encontrar el tipo de átomo correcto -> [dtype-> objeto] objeto de tipo ‘datetime.datetime’ no tiene len ()


Actualización 3

gracias jeff Perdón por el retraso.

  • mesas. versión : ‘2.4.0’.
  • sí, los 884 segundos son solo los costos de operación de venta sin la operación de extracción de mysql
  • una fila de dataframe (df.ix [0]):
 bug_id 1
 asignado a 185
 bug_file_loc Ninguno
 bug_severity crítico
 bug_status cerrado
 creation_ts 1998-05-06 21:27:00
 delta_ts 2012-05-09 14:41:41
 short_desc Dos cursores.
 host_op_sys desconocido
 guest_op_sys Desconocido
 prioridad P3
 rep_platform IA32
 reportero 56
 product_id 7
 category_id 983
 componente_id 12925
 resolución fija
 target_milestone ws1
 qa_contact 412
 status_whiteboard                         
 votos 0
 palabras clave SR
 Lastdiffed 2012-05-09 14:41:41
 siempre confirmado 1
 reporter_accessible 1
 cclist_accessible 1
 estimado_tiempo 0.00
 restante_tiempo 0.00
 fecha límite ninguno
 alias Ninguno
 found_in_product_id 0
 found_in_version_id 0
 found_in_phase_id 0
 cf_type Defecto
 cf_reported_by Development
 cf_attempted NaN
 cf_failed NaN
 cf_public_summary                         
 cf_doc_impact 0
 cf_security 0
 cf_build NaN
 cf_branch                                 
 cf_change NaN
 cf_test_id NaN
 cf_regression desconocido
 cf_reviewer 0
 cf_on_hold 0
 cf_public_severity ---
 cf_i18n_impact 0
 cf_eta ninguno
 cf_bug_source ---
 cf_viss Ninguno
 Nombre: 0, longitud: 52
  • la imagen del dataframe (solo escribe ‘df’ en el cuaderno de ipython):

 Índice Int64: 924624 entradas, 0 a 924623
 Columnas de datos:
 bug_id 924624 valores no nulos
 asignado a 924624 valores no nulos
 bug_file_loc 427318 valores no nulos
 bug_severity 924624 valores no nulos
 bug_status 924624 valores no nulos
 creation_ts 924624 valores no nulos
 delta_ts 924624 valores no nulos
 short_desc 924624 valores no nulos
 host_op_sys 924624 valores no nulos
 guest_op_sys 924624 valores no nulos
 Prioridad 924624 valores no nulos
 rep_platform 924624 valores no nulos
 Reportero 924624 valores no nulos
 product_id 924624 valores no nulos
 category_id 924624 valores no nulos
 component_id 924624 valores no nulos
 Resolución 924624 valores no nulos.
 target_milestone 924624 valores no nulos
 qa_contact 924624 valores no nulos
 status_whiteboard 924624 valores no nulos
 vota 924624 valores no nulos
 palabras clave 924624 valores no nulos
 Lastdiffed 924509 valores no nulos
 siempre confirmado 924624 valores no nulos
 reporter_accessible 924624 valores no nulos
 cclist_accessible 924624 valores no nulos
 estimado_tiempo 924624 valores no nulos
 Tiempo restante_2442424 valores no nulos
 fecha límite 0 valores no nulos
 alias 0 valores no nulos
 found_in_product_id 924624 valores no nulos
 found_in_version_id 924624 valores no nulos
 found_in_phase_id 924624 valores no nulos
 cf_type 924624 valores no nulos
 cf_reported_by 924624 valores no nulos
 cf_attempted 89622 valores no nulos
 cf_failed 89587 valores no nulos
 cf_public_summary 510799 valores no nulos
 cf_doc_impact 924624 valores no nulos
 cf_security 924624 valores no nulos
 cf_build 327460 valores no nulos
 cf_branch 614929 valores no nulos
 cf_change 300612 valores no nulos
 cf_test_id 12610 valores no nulos
 cf_regression 924624 valores no nulos
 cf_reviewer 924624 valores no nulos
 cf_on_hold 924624 valores no nulos
 cf_public_severity 924624 valores no nulos
 cf_i18n_impact 924624 valores no nulos
 cf_eta 3910 valores no nulos
 cf_bug_source 924624 valores no nulos
 cf_viss 725 valores no nulos
 dtypes: float64 (5), int64 (19), objeto (28)
  • después de ‘convert_objects ()’:
 dtypes: datetime64 [ns] (2), float64 (5), int64 (19), objeto (26)
  • y poner el dataframe convertido en costos de hdfstore : 749.50 s 🙂
    • Parece que reducir la cantidad de tipos de ‘objeto’ es la clave para disminuir los costos de tiempo.
  • y poner el dataframe convertido en hdfstore con la ‘tabla’ de parámetros es verdadero, todavía devuelve ese error
 /usr/local/lib/python2.6/dist-packages/pandas-0.10.1.dev_6e2b6ea-py2.6-linux-x86_64.egg/pandas/io/pytables.pyc en create_axes (self, axes, obj, validate , nan_rep, data_columns, min_itemsize, ** kwargs)
    2203 de subida
    2204 excepto (Excepción), detalle:
 -> 2205 boost Excepción ("no se puede encontrar el tipo de átomo correcto -> [dtype ->% s]% s"% (b.dtype.name, str (detalle)))
    2206 j + = 1
    2207 
 Excepción: no se puede encontrar el tipo de átomo correcto -> [dtype-> objeto] objeto de tipo 'datetime.datetime' no tiene len ()
  • Estoy tratando de poner el dataframe sin columnas de fecha y hora

Actualización 4

  • Hay 4 columnas en mysql cuyo tipo es datetime:
    • creacion_ts
    • delta_ts
    • lastimado
    • fecha tope

Después de llamar a los convert_objects ():

  • Creacion_ts:
 Marca de tiempo: 1998-05-06 21:27:00
  • delta_ts:
 Marca de tiempo: 2012-05-09 14:41:41
  • lastimado
 datetime.datetime (2012, 5, 9, 14, 41, 41)
  • la fecha límite siempre es Ninguna, no importa antes o después de llamar a ‘convert_objects’
 Ninguna
  • poner el dataframe sin la columna ‘lastdiff’ cuesta 691.75 s
  • al colocar el dataframe sin la columna ‘lastdiff’ y configurar param ‘table’ igual a True, recibí un nuevo error:
 /usr/local/lib/python2.6/dist-packages/pandas-0.10.1.dev_6e2b6ea-py2.6-linux-x86_64.egg/pandas/io/pytables.pyc en create_axes (self, axes, obj, validate , nan_rep, data_columns, min_itemsize, ** kwargs)
    2203 de subida
    2204 excepto (Excepción), detalle:
 -> 2205 boost Excepción ("no se puede encontrar el tipo de átomo correcto -> [dtype ->% s]% s"% (b.dtype.name, str (detalle)))
    2206 j + = 1
    2207 

 Excepción: no se puede encontrar el tipo de átomo correcto -> [dtype-> objeto] objeto de tipo 'Decimal' no tiene len ()
  • el tipo de columnas ‘hora_estimada’, ‘hora restante’, ‘cf_viss’ es ‘decimal’ en mysql

Actualización 5

  • Transformé estas columnas de tipo ‘decimal’ en tipo ‘flotante’, mediante el siguiente código:
 no_diffed_converted_df_bugs.estimated_time = no_diffed_converted_df_bugs.estimated_time.map (float)
  • Y ahora, el tiempo cuesta 372.84 s.
  • pero la versión de ‘tabla’ aún generaba un error:
 /usr/local/lib/python2.6/dist-packages/pandas-0.10.1.dev_6e2b6ea-py2.6-linux-x86_64.egg/pandas/io/pytables.pyc en create_axes (self, axes, obj, validate , nan_rep, data_columns, min_itemsize, ** kwargs)
    2203 de subida
    2204 excepto (Excepción), detalle:
 -> 2205 boost Excepción ("no se puede encontrar el tipo de átomo correcto -> [dtype ->% s]% s"% (b.dtype.name, str (detalle)))
    2206 j + = 1
    2207 

 Excepción: no se puede encontrar el tipo de átomo correcto -> [dtype-> objeto] objeto de tipo 'datetime.date' no tiene len ()

Estoy bastante convencido de que su problema está relacionado con la asignación de tipos de los tipos reales en DataFrames y con la forma en que PyTables los almacena.

  • Tipos simples (floats / ints / bools) que tienen una representación fija, estos se asignan a tipos c fijos
  • Los tiempos de los datos se manejan si se pueden convertir correctamente (por ejemplo, tienen un tipo de ‘datetime64 [ns]’, especialmente los tiempos de los datos.date NO se manejan (NaN es una historia diferente y dependiendo del uso puede hacer que todo el tipo de columna sea mal manejado)
  • Las cadenas se asignan (en los objetos Storer al tipo de objeto, la tabla las asigna a los tipos de cadena)
  • Unicode no son manejados
  • todos los otros tipos se manejan como Objeto en Almacenes o se lanza una Excepción para Tablas

Lo que esto significa es que si está realizando una asignación a un Storer (una representación fija), todos los tipos no asignables se convertirán en Objeto, vea esto. PyTables escabina estas columnas . Vea la referencia a continuación para ObjectAtom

http://pytables.github.com/usersguide/libref/declarative_classes.html#the-atom-class-and-its-descendants

La tabla boostá en un tipo no válido (debería proporcionar un mejor mensaje de error aquí). Creo que también proporcionaré una advertencia si intenta almacenar un tipo que se asigna a ObjectAtom (por motivos de rendimiento).

Para forzar algunos tipos prueba algunos de estos:

import pandas as pd # convert None to nan (its currently Object) # converts to float64 (or type of other objs) x = pd.Series([None]) x = x.where(pd.notnull(x)).convert_objects() # convert datetime like with embeded nans to datetime64[ns] df['foo'] = pd.Series(df['foo'].values, dtype = 'M8[ns]') 

Aquí hay una muestra en Linux de 64 bits (el archivo es 1M de filas, aproximadamente 1 GB de tamaño en el disco)

 In [1]: import numpy as np In [2]: import pandas as pd In [3]: pd.__version__ Out[3]: '0.10.1.dev' In [3]: import tables In [4]: tables.__version__ Out[4]: '2.3.1' In [4]: df = pd.DataFrame(np.random.randn(1000 * 1000, 100), index=range(int( ...: 1000 * 1000)), columns=['E%03d' % i for i in xrange(100)]) In [5]: for x in range(20): ...: df['String%03d' % x] = 'string%03d' % x In [6]: df Out[6]:  Int64Index: 1000000 entries, 0 to 999999 Columns: 120 entries, E000 to String019 dtypes: float64(100), object(20) # storer put (cannot query) In [9]: def test_put(): ...: store = pd.HDFStore('test_put.h5','w') ...: store['df'] = df ...: store.close() In [10]: %timeit test_put() 1 loops, best of 3: 7.65 s per loop # table put (can query) In [7]: def test_put(): ....: store = pd.HDFStore('test_put.h5','w') ....: store.put('df',df,table=True) ....: store.close() In [8]: %timeit test_put() 1 loops, best of 3: 21.4 s per loop 

¿Cómo hacer esto más rápido?

  1. use ‘io.sql.read_frame’ para cargar datos desde una base de datos SQL a un dataframe. Porque el ‘read_frame’ se ocupará de las columnas cuyo tipo es ‘decimal’ al convertirlas en float.
  2. Rellena los datos que faltan para cada columna.
  3. llamar a la función ‘DataFrame.convert_objects’ antes de poner la operación
  4. Si tiene columnas de tipo cadena en el marco de fecha, use ‘tabla’ en lugar de ‘almacenador’

store.put (‘key’, df, table = True)

Después de realizar estos trabajos, el rendimiento de la operación de colocación tiene una gran mejora con el mismo conjunto de datos:

 CPU times: user 42.07 s, sys: 28.17 s, total: 70.24 s Wall time: 98.97 s 

Perfil de registros de la segunda prueba:

 95984 llamadas de función (95958 llamadas primitivas) en 68.688 segundos de CPU

    Ordenado por: tiempo interno

    ncalls tottime percall cumtime percall filename: lineno (función)
       445 16.757 0.038 16.757 0.038 {numpy.core.multiarray.array}
        19 16.250 0.855 16.250 0.855 {método '_append_records' de 'tables.tableExtension.Table' objetos}
        16 7.958 0.497 7.958 0.497 {método 'astype' de los objetos 'numpy.ndarray'}
        19 6.533 0.344 6.533 0.344 {pandas.lib.create_hdf_rows_2d}
         4 6.284 1.571 6.388 1.597 {método '_fillCol' de 'tables.tableExtension.Row' objetos}
        20 2.640 0.132 2.641 0.132 {pandas.lib.maybe_convert_objects}
         1 1.785 1.785 1.785 1.785 {pandas.lib.isnullobj}
         7 1.619 0.231 1.619 0.231 {método 'aplanar' de objetos 'numpy.ndarray'}
        11 1.059 0.096 1.059 0.096 {pandas.lib.infer_dtype}
         1 0.997 0.997 41.952 41.952 pytables.py:2468(write_data)
        19 0.985 0.052 40.590 2.136 pytables.py:2504(write_data_chunk)
         1 0.827 0.827 60.617 60.617 pytables.py:2433 (escribir)
      1504 0.592 0.000 0.592 0.000 {método '_g_readSlice' de 'tables.hdf5Extension.Array' objetos}
         4 0.534 0.133 13.676 3.419 pytables.py:1038(set_atom)
         1 0.528 0.528 0.528 0.528 {pandas.lib.max_len_string_array}
         4 0.441 0.110 0.571 0.143 internals.py:1409(_stack_arrays)
        35 0.358 0.010 0.358 0.010 {método 'copia' de los objetos 'numpy.ndarray'}
         1 0.276 0.276 3.135 3.135 internals.py:208(fillna)
         5 0.263 0.053 2.054 0.411 common.py:128(_isnull_ndarraylike)
        48 0.253 0.005 0.253 0.005 {método '_append' de 'tables.hdf5Extension.Array' objetos}
         4 0.240 0.060 1.500 0.375 internals.py:1400(_simple_blockify)
         1 0.234 0.234 12.145 12.145 pytables.py:1066(set_atom_string)
        28 0.225 0.008 0.225 0.008 {método '_createCArray' de 'tables.hdf5Extension.Array' objetos}
        36 0.218 0.006 0.218 0.006 {método '_g_writeSlice' de 'tables.hdf5Extension.Array' objetos}
      6110 0.155 0.000 0.155 0.000 {numpy.core.multiarray.empty}
         4 0.097 0.024 0.097 0.024 {método 'todos' de los objetos 'numpy.ndarray'}
         6 0.084 0.014 0.084 0.014 {tables.indexesExtension.keysort}
        18 0.084 0.005 0.084 0.005 {método '_g_close' of 'tables.hdf5Extension.Leaf' objects}
     11816 0.064 0.000 0.108 0.000 file.py:1036(_getNode)
        19 0.053 0.003 0.053 0.003 {método '_g_flush' de 'tables.hdf5Extension.Leaf' objetos}
      1528 0.045 0.000 0.098 0.000 array.py:342(_interpret_indexing)
     11709 0.040 0.000 0.042 0.000 file.py:248(__getitem__)
         2 0.027 0.013 0.383 0.192 index.py:1099(get_neworder)
         1 0.018 0.018 0.018 0.018 {numpy.core.multiarray.putmask}
         4 0.013 0.003 0.017 0.004 index.py:607(final_idx32)