通常我们在Django framework里去取DB数据做处理时,会用values()这个function,然后直接转换成dataframe。假设需要取整个table的数据,简单粗暴的写法如下:
querySet = models.xxx.objects.all().using("db_name").values()
df = pd.DataFrame(list(querySet))
这是一种最方便的写法,但却是最耗memory的。那么我们如何改进这一操作?
首先我们可以做的是去掉不需要的column,只选择有用的column。如果目标table有很多column,而你只需要其中一两个的话,这一缩减可以节省很多的资源消耗。
querySet = models.xxx.objects.all().using("db_name").values("column_1", "column_2")
df = pd.DataFrame(list(querySet))
其次,我们还想要做进一步的优化,可以先观察一下,代码中 querySet 到底是什么样的数据,打印出来的结果如下:
<QuerySet [{'column_1':'value_10', 'column_2':'value_20'}, {'column_1':'value_11', 'column_2':'value_21'}, {'column_1':'value_12', 'column_2':'value_22'} ...]>
这是一种极度冗余的数据信息,table里的每一行被转行成key,value的字典格式,而key是永远不变的,浪费了大量的存储空间。那么我们现在要做的是如何滤掉key的信息,只提取value。Django提供了另外一个function叫values_list(),它可以达到这一效果。
querySet = models.xxx.objects.all().using("db_name").values_list("column_1", "column_2")
print(querySet)
>>>
<QuerySet [('value_10', 'value_20'), ('value_11', 'value_21'), ('value_12', 'value_22')...]>
用了values_list(), table里的每一行对应成了一个tuple输出,没有了column的信息,这正是我们想要的。但是这样的数据转成dataframe需要显式声明column的信息。最终代码如下:
querySet = models.xxx.objects.all().using("db_name").values_list("column_1", "column_2")
df = pd.DataFrame(list(querySet), columns=["column_1", "column_2"])
这个优化,曾经用在一个千万数据集的项目中,peak mem直接下降了50%。当然如果你的每个column name很长,而数据很短的话,可能节省更多的memory。