Python 3.x 加快将数十亿行写入HDF5的速度

Python 3.x 加快将数十亿行写入HDF5的速度,python-3.x,pandas,hdf5,pytables,Python 3.x,Pandas,Hdf5,Pytables,这是我在问题中试图讨论的情景的延续。请阅读问题,了解有关以下内容的更多详细信息 由于上述相关问题因主题过于宽泛而被关闭,我没有机会从处理数百GB数据经验丰富的人那里收集想法。我在这方面没有任何经验,我在不断学习。很明显,我在某个地方犯了一些错误,因为我的方法花费的时间太长了 数据如我在上面的链接问题中所述。我决定为每个传感器创建一个节点(组)(以传感器ID作为节点名,在根目录下)来存储我拥有的260k传感器中的每个传感器生成的数据。该文件最终将有260k个节点,每个节点将有几GB的数据存储在其下

这是我在问题中试图讨论的情景的延续。请阅读问题,了解有关以下内容的更多详细信息

由于上述相关问题因主题过于宽泛而被关闭,我没有机会从处理数百GB数据经验丰富的人那里收集想法。我在这方面没有任何经验,我在不断学习。很明显,我在某个地方犯了一些错误,因为我的方法花费的时间太长了

数据如我在上面的链接问题中所述。我决定为每个传感器创建一个节点(组)(以传感器ID作为节点名,在根目录下)来存储我拥有的260k传感器中的每个传感器生成的数据。该文件最终将有260k个节点,每个节点将有几GB的数据存储在其下的一个表中。执行所有重型起重作业的代码如下:

with pd.HDFStore(hdf_path, mode='w') as hdf_store:
    for file in files:
        # Read CSV files in Pandas
        fp = os.path.normpath(os.path.join(path, str(file).zfill(2)) + '.csv')
        df = pd.read_csv(fp, names=data_col_names, skiprows=1, header=None,
                         chunksize=chunk_size, dtype=data_dtype)

        for chunk in df:
            # Manipulate date & epoch to get it in human readable form
            chunk['DATE'] = pd.to_datetime(chunk['DATE'], format='%m%d%Y', box=False)
            chunk['EPOCH'] = pd.to_timedelta(chunk['EPOCH']*5, unit='m')
            chunk['DATETIME'] = chunk['DATE'] + chunk['EPOCH']

            #Group on Sensor to store in HDF5 file
            grouped = chunk.groupby('Sensor')
            for group, data in grouped:
                data.index = data['DATETIME']
                hdf_store.append(group, data.loc[:,['R1', 'R2', 'R3']])

    # Adding sensor information as metadata to nodes
    for sens in sensors:
        try:
            hdf_store.get_storer(sens).attrs.metadata = sens_dict[sens]
            hdf_store.get_storer(sens).attrs['TITLE'] = sens
        except AttributeError:
            pass
如果我注释掉行
hdf_store.append(group,data.loc[:,['R1','R2','R3']])
,df:中块的
下的位大约需要40-45秒来完成迭代处理。(我正在读取的块大小是1M行。)但是代码中包含了行(即如果分组块正在写入HDF文件),那么代码每次迭代大约需要10-12分钟。我对执行时间的增加完全感到困惑。我不知道是什么导致了这种情况

请给我一些建议来解决这个问题。请注意,我无法承受如此长的执行时间。我需要以这种方式处理大约220 GB的数据。稍后,我需要查询该数据,一次查询一个节点,以便进一步分析。我花了4天多的时间研究这个话题,但我仍然像刚开始时一样感到困惑

#########编辑1### 包含包含1M行的块的
df.info()

<class 'pandas.core.frame.DataFrame'>
Int64Index: 1000000 entries, 0 to 999999
Data columns (total 7 columns):
SENSOR      1000000 non-null object
DATE        1000000 non-null datetime64[ns]
EPOCH       1000000 non-null timedelta64[ns]
R1          1000000 non-null float32
R2          773900 non-null float32
R3          483270 non-null float32
DATETIME    1000000 non-null datetime64[ns]
dtypes: datetime64[ns](2), float32(3), object(1), timedelta64[ns](1)
memory usage: 49.6+ MB

您不断地对所写的行执行索引。写入所有的行,然后创建索引,效率要高得多

请参阅有关创建索引的文档

在追加操作上,通过
index=False
;这将关闭索引

In [38]: N = 1000000

In [39]: df = DataFrame(np.random.randn(N,3).astype(np.float32),columns=list('ABC'),index=pd.date_range('20130101',freq='ms',periods=N))

In [40]: df.info()
<class 'pandas.core.frame.DataFrame'>
DatetimeIndex: 1000000 entries, 2013-01-01 00:00:00 to 2013-01-01 00:16:39.999000
Freq: L
Data columns (total 3 columns):
A    1000000 non-null float32
B    1000000 non-null float32
C    1000000 non-null float32
dtypes: float32(3)
memory usage: 19.1 MB

In [41]: store = pd.HDFStore('test.h5',mode='w')

In [42]: def write():
   ....:     for i in range(10):
   ....:         dfi = df.copy()
   ....:         dfi.index = df.index + pd.Timedelta(minutes=i)
   ....:         store.append('df',dfi)
   ....:         

In [43]: %timeit -n 1 -r 1 write()
1 loops, best of 1: 4.26 s per loop

In [44]: store.close()

In [45]: pd.read_hdf('test.h5','df').info()
<class 'pandas.core.frame.DataFrame'>
DatetimeIndex: 10000000 entries, 2013-01-01 00:00:00 to 2013-01-01 00:25:39.999000
Data columns (total 3 columns):
A    float32
B    float32
C    float32
dtypes: float32(3)
memory usage: 190.7 MB
最后完成后,假设
store
是您的
HDFStore
,运行(在每个节点上)

store.create_table_index('node')
此操作将需要一些时间,但将只执行一次,而不是连续执行。这会产生巨大的差异,因为创建可以考虑所有数据(并且只移动一次)

您可能还希望
ptrepack
您的数据(在索引操作之前或之后),以重置
chunksize
。我不会直接指定它,而是设置
chunksize='auto'
,让它在写入所有数据后计算出最佳大小

因此,这应该是一个非常快速的操作(即使使用索引)


谢谢你,杰夫。我希望你能回答。在我更新代码以符合您的建议之前,有一个问题:现在,我正在将数据帧中的
'DATETIME'
字段转换为索引,然后再编写。数据按datatime索引是非常重要的,因为它们是带时间戳的数据。通过传递
index=False
它会忽略日期时间索引列,还是在我最终运行
存储时以任何方式对其进行更改。创建\u table\u index('node')
?还是建议我将
'datetime'
作为列包含在数据中,然后以某种方式将其转换为索引?(可能使用ptrepack…)无论如何,我都必须在创建文件后使用ptrepack来压缩文件。no index=False只是延迟创建磁盘索引本身(用于帧索引和您创建的任何数据列)。关键的一点是,它仍然是索引的,只是在您写入数据之后创建的。写入磁盘仍然需要7分钟多一点的时间。在此之后,索引将花费更长的时间。我还可以使用其他优化策略吗?存储260k节点效率很低;我认为,最好是存储在更少的节点中
In [38]: N = 1000000

In [39]: df = DataFrame(np.random.randn(N,3).astype(np.float32),columns=list('ABC'),index=pd.date_range('20130101',freq='ms',periods=N))

In [40]: df.info()
<class 'pandas.core.frame.DataFrame'>
DatetimeIndex: 1000000 entries, 2013-01-01 00:00:00 to 2013-01-01 00:16:39.999000
Freq: L
Data columns (total 3 columns):
A    1000000 non-null float32
B    1000000 non-null float32
C    1000000 non-null float32
dtypes: float32(3)
memory usage: 19.1 MB

In [41]: store = pd.HDFStore('test.h5',mode='w')

In [42]: def write():
   ....:     for i in range(10):
   ....:         dfi = df.copy()
   ....:         dfi.index = df.index + pd.Timedelta(minutes=i)
   ....:         store.append('df',dfi)
   ....:         

In [43]: %timeit -n 1 -r 1 write()
1 loops, best of 1: 4.26 s per loop

In [44]: store.close()

In [45]: pd.read_hdf('test.h5','df').info()
<class 'pandas.core.frame.DataFrame'>
DatetimeIndex: 10000000 entries, 2013-01-01 00:00:00 to 2013-01-01 00:25:39.999000
Data columns (total 3 columns):
A    float32
B    float32
C    float32
dtypes: float32(3)
memory usage: 190.7 MB
In [46]: pd.__version__
Out[46]: u'0.17.0'

In [49]: import tables

In [50]: tables.__version__
Out[50]: '3.2.2'

In [51]: np.__version__
Out[51]: '1.10.1'