我试图了解如何设置稀疏熊猫矩阵以最大限度地减少内存使用并保留所有值的精度。我在 pandas Sparse documentation 中没有找到答案。下面是一个例子来说明我的问题:
为什么
Sparse(int32)
数据框占用与 Sparse(float32)
数据框一样多的内存?如果是这种情况,指定 Sparse(int)
dtype 有什么好处吗?
pandas 如何决定使用哪种特定的
Sparse(int)
dtype,例如int8
还是int32
?鉴于下面的示例(请参阅数据帧 sdf_int32
和 sdf_high_int32
),似乎总是选择 Sparse(int32)
,无论 Sparse(int8)
是否可能更节省内存,或者 Sparse(int32)
是否可能截断某些值。
为每列指定
Sparse(intNN)
或 Sparse(floatNN)
dtype 是避免截断并实现最小内存使用的唯一方法吗?
import numpy as np
import pandas as pd
# Generate binary dense matrix with low density
df = pd.DataFrame()
for col in ['col1', 'col2', 'col3']:
df[col] = np.where(np.random.random_sample(100_000_000) > 0.98, 1, 0)
df.name = 'Dense'
# Replace one column by values too high for int32 dtype
df_high = df.copy()
df_high['col1'] = df_high['col1'] * 100_000_000_000
# Convert df to sparse of various dtypes
sdf_float32 = df.astype(pd.SparseDtype('float32', 0))
sdf_float32.name = 'Sparse, float32'
sdf_int8 = df.astype(pd.SparseDtype('int8', 0))
sdf_int8.name = 'Sparse, int8'
sdf_int32 = df.astype(pd.SparseDtype('int', 0))
sdf_int32.name = 'Sparse, int32'
sdf_int64 = df.astype(pd.SparseDtype('int64', 0))
sdf_int64.name = 'Sparse, int64'
# Convert df_high to Sparse(int)
sdf_high_int32 = df_high.astype(pd.SparseDtype('int', 0))
sdf_high_int32.dtypes
sdf_high_int32['col1'].value_counts()
sdf_high_int32.name = 'Sparse, int32 highval'
# Print info for all dataframes
print(f" {df.name} Dataframe; Memory size: {df.memory_usage(deep=True).sum() / 1024 ** 2:.1f} MB, {df['col1'].dtype}")
for data in [sdf_float32, sdf_int8, sdf_int32, sdf_high_int32, sdf_int64]:
print(f" {data.name} Dataframe; Memory size: {data.memory_usage(deep=True).sum() / 1024**2:.1f} MB,"
f"Density {data.sparse.density:.5%}, {data['col1'].dtype}")
"""
Dense Dataframe; Memory size: 1144.4 MB, int32
Sparse, float32 Dataframe; Memory size: 45.8 MB,Density 1.99980%, Sparse[float32, 0]
Sparse, int8 Dataframe; Memory size: 28.6 MB,Density 1.99980%, Sparse[int8, 0]
Sparse, int32 Dataframe; Memory size: 45.8 MB,Density 1.99980%, Sparse[int32, 0]
Sparse, int32 highval Dataframe; Memory size: 45.8 MB,Density 1.99980%, Sparse[int32, 0]
Sparse, int64 Dataframe; Memory size: 68.7 MB,Density 1.99980%, Sparse[int64, 0]
"""
# Show truncated values for sdf_high_int32
print(f"Values for sdf_high_int32, col1: \n {sdf_high_int32['col1'].value_counts()}")
"""
Values for sdf_high_int32, col1:
col1
0 98001473
1215752192 1998527
Name: count, dtype: int64
"""
您的问题中有两个问题,第一个是关于稀疏矩阵的。这是 pandas 文档:
pandas 提供了有效存储稀疏数据的数据结构。这些并不一定是典型的“大部分为 0”的稀疏现象。相反,您可以将这些对象视为“压缩”,其中匹配特定值(NaN/缺失值,尽管可以选择任何值,包括 0)的任何数据都会被忽略。压缩值实际上并未存储在数组中。
这意味着只有选择不存储的值(在您的情况下是
0
)不会被存储。其他值存储为您选择的数据类型。 float32
和int32
都使用32位来表示一个值,因此它们消耗相同的内存。区别在于它们可以以什么精度存储什么值。对于 int64
与 float64
来说也是如此。
由于您只存储了 0 和 1,因此在您的情况下,您也可以选择
int8
来存储 df
。
现在,回答你的
int
问题。您的平台似乎将 int
解释为 int32
。在我的平台上 int
相当于 int64
。 Numpy 负责这个,这里有一些来自 numpy 文档:
numpy.int [...] numpy.int64 或 numpy.int32
...
对于 np.int ,直接替换为 np.int_ 或 int 也很好,并且不会改变行为,但精度将继续取决于计算机和操作系统。如果您想更明确并查看当前的使用情况,您有以下选择:[...]
因为在您的情况下选择了
int32
,所以您会看到 0
(显然)和 1215752192
的值。后者被 100_000_000_000
存储在 int32
中,即存在溢出,它被存储为 100_000_000_000 % (2**32)
(在 python 中运行),这给出了 1215752192
。
顺便说一句,这是我的 python 解释器的相关部分:
>>> df_high['col1'] = df_high['col1'] * 100_000_000_000
>>> sdf_int32 = df.astype(pd.SparseDtype('int', 0))
>>> sdf_int32.dtypes
col1 Sparse[int64, 0]
col2 Sparse[int64, 0]
col3 Sparse[int64, 0]
dtype: object
>>> sdf_high_int32 = df_high.astype(pd.SparseDtype('int', 0))
>>> sdf_high_int32.dtypes
col1 Sparse[int64, 0]
col2 Sparse[int64, 0]
col3 Sparse[int64, 0]
dtype: object
>>> 100_000_000_000 % (2**32)
1215752192