如何利用pandas处理大数据

2023年1月16日10:26:58

翻译自这篇文章

当我们需要处理大数据时,如果不对数据做任何处理,可能会带来内存占用过大和运行过慢的风险。

当然对于处理大数据集,类似spark之类的专业处理工具是大家的首选,但是pandas优秀的特性和简单明了的语法能极大提升数据分析的效率,因此我需要考虑如何对数据优化,使得我们能在pandas上完成更大数据量的数据分析工作。

在用pandas进行数据分析时,减少内存占用简单来说就是选择合适的数据类型。

我们用棒球比赛日志数据来作为我们的例子。

import pandas as pd
gl = pd.read_csv("game_logs.csv")#读数据

gl.info(memory_usage = 'deep')#数据集的一些信息和内存占用

我们先了解下pandas内部储存Dataframe的机制。
pandas将列聚合成一个个的blocks,block内都是相同类型的数据。blocks不会存储列名,只会储存dataframe列中的实际的值。内部的BlockManager class会将row和column的indexes与它们的值一一映射起来。当我们选择,编辑,删除值的时候,dataframe类会和BlockManager类交互,将我们的请求转换成相应的函数和方法调用。

pandas中,有一个叫pandas.core.internals模块,这里有许多的class,每个数据类型都对应了一个特殊的class。例如Pandas用ObjectBlock类来表示包含了存储string类型数据的列的block。对于包含存储数值数据比如说整型和浮点型数据的block,pandas将他们合并在一起,并用Numpy ndarray来存储。

由于每个数据类型在pandas中的都是分别存储的。我们可以看下每种数据类型平均需要用多少的内存空间。

for dtype in ['float', 'int', 'object]:
    selected_dtype = gl.select_dtypes(include = [dtypes])
    mean_usage_b = selected.dtype.memory_usage(deep = True)
    mean_usage_mb = mean_usage_b / 1024 ** 2
    print("Average memory usage for {} columns: {:03.2f} MB".format(dtype, mean_usage_mb))

正如我们之前提到的,pandas用Numpy ndarrays来表示数值型数据,并且将他们存放在连续的内存单元中。这样的存储模型能消耗更小的空间资源,并且能使我们的访问更加迅速。

许多的pandas中的类有许多的子类。例如float类型有flloat16,float32和float64这些子类。使用的内存依次上升。

我们可以用numy.iinfo类来观察每个子类的最大值和最小值。例如

import numpy as np
int_types = ['uint8', 'int8', 'int16']
for it in int_types:
    print(np.iinfo(it))

这里uint代表了无符号整型,int代表了带符号整型。两种子类的存储空间都是相同的。

我们可以再看下数据类型转换后的内存变化情况。

def mem_usage(pandas_obj):
    if isinstance(pandas_obj, pd.DataFrame):
        usage_b = pandas_obj.memory_usage(deep = True).sum()
    else:
        usage_b = pandas_obj.memory_usage(deep = True)
    usage_mb = usage_b / 1024 ** 2
    return "{:03.2f} MB".format(usage_mb)

gl_int = gl.select_dtypes(include = ['int'])
converted_int = gl_int.apply(pd.to_numeric, downcast = 'unsigned')

print(mem_usage(go_int))
print(mem_usage(converted_int))

compare_ints = pd.concat([gl_int.dtypes, converted_int.dtypes], axis = 1)
compare_ints.columnns = ['before', 'after']
compare_ints.apply(pd.Series.value_counts)

我们对float类型的数据也做同样的处理。最后看下整个数据集内存减少了多少。

optimized_gl = gl.copy()

optimized_gl[converted_int.columns] = converted_int
optimized_gl[converted_float.columns] = converted_float

print(mem_usage(gl))
print(mem_usage(optimized_gl))

我们发现,仅仅对数值型数据做类型变换不能大幅度的减少整个数据集的内存占用情况。因此我们需要对最耗内存的,也就是object类型的数据进行优化。

pandas中的object类型的数据表示数据是Python string类型的,部分原因是因为在Numpy中缺少对缺失string值的支持机制,因为Python语言的原因,它没有一个好的对值的内存管理的机制。

这样的限制导致了strings被存储在非连续的内存单元当中,每一个元素实际上是一个包含了值的实际内存地址的指针。

from sys import getsizeof

s1 = "working out"
s2 = "memory usage for"
s3 = "strings in python is fun!"
s4 = "strings in python is fun!" 

for s in [s1, s2, s3, s4]:
    print(getsizeof(s))

我们用categgory类型来对object类型的数据做优化。

dow = gl_ob.day_of_week
print(dow.head())

dow_cat = dow.astype("category")
print(dow_cat.head())

如果运行,我们将会看到这样的操作的确节省了非常多的内存。

  • 作者:inf_zh
  • 原文链接:https://blog.csdn.net/qq_33840784/article/details/80164018
    更新时间:2023年1月16日10:26:58 ,共 2603 字。