Pandas 内存优化:`astype(‘category’)`, 整数类型降级

Pandas 内存优化:让你的 DataFrame 苗条起来,跑得更快!🚀

各位亲爱的朋友们,欢迎来到今天的 Pandas 优化小课堂!我是你们的“内存瘦身”教练,今天我们要聊的是如何让你的 DataFrame 变得像模特一样苗条,跑得像猎豹一样飞快!

在数据分析的世界里,Pandas DataFrame 是我们最亲密的伙伴。它能处理各种数据,让我们洞察背后的规律。但是,就像我们人类一样,DataFrame 也会变胖!当数据量越来越大,我们的 DataFrame 也会变得臃肿不堪,占据大量的内存,让程序运行速度慢如蜗牛。🐌

别担心!今天我就要教你两招“瘦身秘诀”:astype('category') 和整数类型降级。掌握了这两招,你的 DataFrame 就能焕发新生,跑得更快,内存占用更少!

秘诀一:astype('category') – 化腐朽为神奇的魔法棒 🪄

什么是分类数据?

在深入 astype('category') 之前,我们需要先了解一下什么是分类数据。想象一下,你正在处理一份关于顾客购买行为的数据。其中有一列是“顾客性别”,它的取值只有两个:男和女。

这种取值有限且重复的数据,就是典型的分类数据。其他的例子还包括:

  • 国家(中国、美国、英国…)
  • 商品类别(服装、食品、家居…)
  • 学历(小学、中学、大学…)
  • 颜色(红、绿、蓝…)

关键在于,分类数据的值域是有限的,并且会出现大量的重复值。

astype('category') 的魔力

Pandas 默认会将这些分类数据存储为 object 类型(通常是字符串)。这意味着,即使你有一百万个“男”的字符串,Pandas 也会为每一个字符串都分配一块内存空间。这简直就是对内存的浪费啊!

astype('category') 的作用就是将这些字符串类型的数据转换成“类别”类型。它会将所有不同的值(比如“男”和“女”)提取出来,构建一个“类别字典”,然后用整数来表示每个类别。

举个例子:

原始数据:

0    男
1    女
2    男
3    男
4    女
...
999999  男

转换后:

0    0  # 0 代表 "男"
1    1  # 1 代表 "女"
2    0
3    0
4    1
...
999999  0

看到了吗?原来的字符串被替换成了整数,大大减少了内存占用!就像把沉重的行李箱换成了轻便的背包,你的 DataFrame 瞬间变得轻盈起来。

实战演练

让我们用一个简单的例子来演示一下:

import pandas as pd
import numpy as np

# 创建一个包含重复字符串的 DataFrame
data = {'性别': ['男', '女', '男', '男', '女'] * 200000}
df = pd.DataFrame(data)

# 观察 DataFrame 的内存占用
print("原始 DataFrame 的内存占用:", df.memory_usage(deep=True).sum() / 1024**2, "MB")

# 将 '性别' 列转换为 category 类型
df['性别'] = df['性别'].astype('category')

# 再次观察 DataFrame 的内存占用
print("转换后 DataFrame 的内存占用:", df.memory_usage(deep=True).sum() / 1024**2, "MB")

运行这段代码,你会发现,将 性别 列转换为 category 类型后,DataFrame 的内存占用大大减少!这就像给你的 DataFrame 做了一次“抽脂手术”,效果立竿见影!

适用场景和注意事项

astype('category') 非常适合以下场景:

  • 包含大量重复值的字符串列:比如国家、省份、城市、商品类别等。
  • 取值范围有限的列:比如性别、学历、等级等。

但是,也需要注意以下几点:

  • 如果列的取值非常多,且几乎没有重复值,那么使用 astype('category') 可能不会带来太大的收益,甚至可能适得其反。 因为构建类别字典本身也会消耗一定的内存。
  • 转换为 category 类型后,一些字符串操作可能会变得不太方便。 你需要先将 category 类型转换回字符串类型才能进行相应的操作。

总而言之,astype('category') 是一把双刃剑,需要根据实际情况谨慎使用。

秘诀二:整数类型降级 – 能省则省的抠门大法 💰

整数类型:越大越好吗?

在 Pandas 中,整数类型有很多种,比如 int8int16int32int64 等。它们的区别在于可以表示的整数范围不同。int64 可以表示的整数范围最大,但同时也占据最多的内存空间。

很多时候,我们并不需要这么大的整数范围。比如,如果你的数据只包含 0 到 100 之间的整数,那么使用 int8 就足够了。使用 int64 简直就是“高射炮打蚊子”,浪费资源!

整数类型降级的奥义

整数类型降级就是指,将占用内存空间较大的整数类型转换为占用内存空间较小的整数类型。比如,将 int64 转换为 int32int16,甚至 int8

这样做的好处是显而易见的:减少内存占用,提高程序运行速度。

实战演练

Pandas 并没有提供直接的整数类型降级函数。我们需要自己编写一些代码来实现。这里提供一个通用的整数类型降级函数:

def reduce_mem_usage(df):
    """ 迭代 dataframe 的每一列,并尝试修改数据类型
        以减少内存的使用。
    """
    start_mem = df.memory_usage().sum() / 1024**2
    print('Memory usage of dataframe is {:.2f} MB'.format(start_mem))

    for col in df.columns:
        col_type = df[col].dtype

        if col_type != 'object':
            c_min = df[col].min()
            c_max = df[col].max()
            if str(col_type)[:3] == 'int':
                if c_min > np.iinfo(np.int8).min and c_max < np.iinfo(np.int8).max:
                    df[col] = df[col].astype(np.int8)
                elif c_min > np.iinfo(np.int16).min and c_max < np.iinfo(np.int16).max:
                    df[col] = df[col].astype(np.int16)
                elif c_min > np.iinfo(np.int32).min and c_max < np.iinfo(np.int32).max:
                    df[col] = df[col].astype(np.int32)
                elif c_min > np.iinfo(np.int64).min and c_max < np.iinfo(np.int64).max:
                    df[col] = df[col].astype(np.int64)
            elif str(col_type)[:5] == 'float':
                if c_min > np.finfo(np.float16).min and c_max < np.finfo(np.float16).max:
                    df[col] = df[col].astype(np.float16)
                elif c_min > np.finfo(np.float32).min and c_max < np.finfo(np.float32).max:
                    df[col] = df[col].astype(np.float32)
                else:
                    df[col] = df[col].astype(np.float64)

    end_mem = df.memory_usage().sum() / 1024**2
    print('Memory usage after optimization is: {:.2f} MB'.format(end_mem))
    print('Decreased by {:.1f}%'.format(100 * (start_mem - end_mem) / start_mem))
    return df

这个函数会自动遍历 DataFrame 的每一列,判断其数据类型和取值范围,然后选择合适的整数类型进行转换。

让我们用一个例子来演示一下:

# 创建一个包含整数的 DataFrame
data = {'年龄': np.random.randint(0, 150, 1000000),
        '收入': np.random.randint(1000, 100000, 1000000)}
df = pd.DataFrame(data)

# 观察 DataFrame 的内存占用
print("原始 DataFrame 的内存占用:", df.memory_usage(deep=True).sum() / 1024**2, "MB")

# 进行整数类型降级
df = reduce_mem_usage(df)

# 再次观察 DataFrame 的内存占用
print("转换后 DataFrame 的内存占用:", df.memory_usage(deep=True).sum() / 1024**2, "MB")

运行这段代码,你会发现,经过整数类型降级后,DataFrame 的内存占用也大大减少了!就像给你的 DataFrame 做了一次“节食运动”,效果同样显著!

适用场景和注意事项

整数类型降级非常适合以下场景:

  • 包含整数类型的列,且取值范围较小。
  • 对内存占用要求较高的场景。

但是,也需要注意以下几点:

  • 降级后的整数类型可能无法表示原始数据中的所有值。 比如,如果你将 int64 降级为 int8,而原始数据中存在大于 127 或小于 -128 的整数,那么这些值会被截断,导致数据丢失。
  • 在进行数值计算时,如果降级后的整数类型范围不够,可能会导致溢出错误。

因此,在进行整数类型降级时,一定要仔细评估数据的取值范围,选择合适的降级方案。

总结:让你的 DataFrame 轻装上阵 🧳

今天我们学习了 Pandas 内存优化的两大法宝:astype('category') 和整数类型降级。它们就像两把锋利的刀,可以帮助你切除 DataFrame 中的冗余内存,让它变得更加苗条和高效。

  • astype('category') 适用于处理包含大量重复值的字符串列,可以将字符串转换为类别,减少内存占用。
  • 整数类型降级适用于处理包含整数类型的列,可以将占用内存空间较大的整数类型转换为占用内存空间较小的整数类型。

记住,优化是一个持续的过程。你需要不断地分析你的数据,找到潜在的优化点,并根据实际情况选择合适的优化策略。

希望今天的课程对你有所帮助!让我们一起努力,让我们的 DataFrame 跑得更快,内存占用更少!

祝大家编码愉快!🎉

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注