- 我有一个大型MultiIndex系列的字典,其中两个索引级别都是日期时间值。其中一个抽象的简短例子是:
idx_level_0 = pd.date_range('2020-01-01', '2020-04-01', freq = 'M')
idx_level_1 = pd.date_range('2020-04-01', '2020-07-01', freq = 'M')
idx_dates = pd.MultiIndex.from_product([idx_level_0, idx_level_1], names = ['Event_Date', 'Observation_Date'])
ser_info_dated = pd.Series(range(len(idx_level_0) * len(idx_level_1)), index = idx_dates, name = 'Some_Values') / 33
- 我需要保存所有数据,所以我选择将每个系列分别导入到通用的HDF5文件中,并将字典键作为hdf键。当我按原样保存时,我的文件卷大约为4 Gb,所以我正在努力使其更薄。此外,我还需要处理整个索引中的所有序列数据,所以我需要一些全局识别方法。我的想法是从所有系列的两个级别共同收集日期(大约有11000个唯一日期(,并将其替换为唯一的数字标识符,以便有机会恢复所有系列的原始索引。但只有当我可以将数值转换为int16类型时,它才有意义。所以我尝试了这样一个序列(在这里我将其简化为单个序列(:
list_levels_dates = sorted(list(set(idx_level_0) | set(idx_level_1)))
dict_to_numbers = dict(zip(list_levels_dates, range(len(list_levels_dates))))
df_info_numbered = ser_info_dated.reset_index().replace({'Event_Date': dict_to_numbers, 'Observation_Date': dict_to_numbers})
df_info_downcasted = df_info_numbered.copy()
df_info_downcasted[['Event_Date', 'Observation_Date']] = df_info_downcasted[['Event_Date', 'Observation_Date']].astype('int16')
它看起来很成功:
print('df_info_downcasted column types:n', df_info_downcasted.dtypes)
显示了这样一个结果:
df_info_downcasted column types:
Event_Date int16
Observation_Date int16
Some_Values float64
- 但当我将列移回索引级别时,它再次变为int64:
ser_info_downcasted = df_info_downcasted.set_index(['Event_Date', 'Observation_Date']).squeeze()
print('ser_info_downcasted index level 0 type: ', ser_info_downcasted.index.levels[0].dtype)
print('ser_info_downcasted index level 1 type: ', ser_info_downcasted.index.levels[1].dtype)
ser_info_downcasted index level 0 type: int64
ser_info_downcasted index level 1 type: int64
- 我尝试了其他操作,但也失败了:
ser_info_astyped = ser_info_downcasted.copy()
ser_info_astyped.index = ser_info_astyped.index.set_levels(ser_info_astyped.index.levels[0].astype('int16'), level = 0)
ser_info_astyped.index = ser_info_astyped.index.set_levels(ser_info_astyped.index.levels[1].astype('int16'), level = 1)
print('ser_info_astyped index level 0 type: ', ser_info_astyped.index.levels[0].dtype)
print('ser_info_astyped index level 1 type: ', ser_info_astyped.index.levels[1].dtype)
ser_info_astyped index level 0 type: int64
ser_info_astyped index level 1 type: int64
- 所以我非常需要一个如何将整数类型显式转换为较短类型的建议,或者如何缩短系列体积的替代建议。我还试图将所有系列附加到一个巨大的系列中,但这会引发内存错误
TL;DR:Pandas将索引转换为64字节值,最小化文件的最佳机会是使用HDF序列化。
Pandas似乎不支持int16
数据类型作为索引。
Int64Index
是大熊猫的一个基本指标。这是一个实现有序、可切片集的不可变数组。
源
pandas.Index.astype
进一步强化了这一点。
请注意,任何有符号整数dtype都被视为
'int64'
,任何无符号整数dtype都被处理为'uint64'
,无论大小如何。
源
因此,本质上,当设置为索引时,我们的int16值被强制转换为int64。我以前没有使用过HDF5,但我试着看看可以做些什么来最小化文件大小。
查看内存分配
>>> print(ser_info_dated)
... Event_Date Observation_Date
... 2020-01-31 2020-04-30 0.000000
... 2020-05-31 0.030303
... 2020-06-30 0.060606
... 2020-02-29 2020-04-30 0.090909
... 2020-05-31 0.121212
... 2020-06-30 0.151515
... 2020-03-31 2020-04-30 0.181818
... 2020-05-31 0.212121
... 2020-06-30 0.242424
>>> print(ser_info_dated.memory_usage(index=True, deep=True))
... 478 # memory usage in bytes
与
>>> print(df_info_downcasted)
... Event_Date Observation_Date Some_Values
... 0 0 3 0.000000
... 1 0 4 0.030303
... 2 0 5 0.060606
... 3 1 3 0.090909
... 4 1 4 0.121212
... 5 1 5 0.151515
... 6 2 3 0.181818
... 7 2 4 0.212121
... 8 2 5 0.242424
>>> print(df_info_downcasted.memory_usage(index=True, deep=True))
... Index 128
... Event_Date 18
... Observation_Date 18
... Some_Values 72
... dtype: int64
>>> print(df_info_downcasted.info())
... <class 'pandas.core.frame.DataFrame'>
... RangeIndex: 9 entries, 0 to 8
... Data columns (total 3 columns):
... # Column Non-Null Count Dtype
... --- ------ -------------- -----
... 0 Event_Date 9 non-null int16
... 1 Observation_Date 9 non-null int16
... 2 Some_Values 9 non-null float64
... dtypes: float64(1), int16(2)
... memory usage: 236.0 bytes
我们可以看到,大部分内存都用在了索引中。当保存为HDF5时,这似乎并不重要。(出于测试目的,我将范围增加为12H
(
>>> ser_info_dated.to_hdf("ser.h5", "ser")
>>> print(f"{os.path.getsize('ser.h5')/1000} kb")
... 412.628 kb
与
>>> down_set_hdf5 = df_info_downcasted.to_hdf("down.h5", "down")
>>> print(f"{os.path.getsize('down.h5')/1000} kb")
... 413.204 kb
我不知道如何保存HDF5文件,但在Pandas中有一个压缩参数complevel
,它可能很有用。
>>> ser_hdf5 = ser_info_dated.to_hdf("ser.h5", "ser", complevel=9)
>>> print(f"{os.path.getsize('ser.h5')/1000} kb")
... 155.452 kb