Pandas加载数据前指定各列数据类型以优化内存占用
解决Pandas加载数据库数据时提前指定列类型以节省内存的问题
核心问题原因
你之前的代码报错是因为pd.DataFrame()构造函数的dtype参数仅支持传入单个数据类型(用于统一设置所有列的类型),不能传入字典指定每列的类型,这才触发了TypeError: object of type 'type' has no len()错误。
下面是几种可行的解决方案,按推荐优先级排序:
1. 直接用pd.read_sql_query读取(最优方案)
跳过先把数据取成tuple列表的步骤,直接用Pandas内置的read_sql_query从数据库读取数据,这个方法原生支持传入dtype字典,能在读取阶段就为每列指定类型,完全避免Pandas自动推断错误,同时减少一次内存拷贝(不需要先把数据存在tuple列表里)。
代码示例:
import pandas as pd import numpy as np sql = "select premise_id, parent_id, addr_ward FROM table;" # 定义每列的目标类型,字符串推荐用Pandas的string类型(1.0+版本支持),比object更省内存 dtype = { 'premise_id': np.int32, # 如果数值范围允许,用int32比int64省一半内存 'parent_id': np.int32, 'addr_ward': 'string' } # 直接从数据库读取并指定列类型 data_frame = pd.read_sql_query(sql, db.conn, dtype=dtype)
2. 用Numpy结构化数组中转(适合必须先获取tuple列表的场景)
如果业务上必须先通过safe_call_db_read获取tuple列表,可以先将列表转换成Numpy结构化数组(提前指定每个字段的类型),再转成DataFrame。这种方式能严格控制内存占用,避免Pandas自动将列推断为object类型。
代码示例:
import pandas as pd import numpy as np sql = "select premise_id, parent_id, addr_ward FROM table;" rows = safe_call_db_read(db.conn, sql) # 定义Numpy结构化类型,字符串可指定固定长度(根据实际业务调整) np_dtype = [ ('premise_id', np.int32), ('parent_id', np.int32), ('addr_ward', 'U100') # U表示Unicode字符串,100为最大长度 ] # 转换为结构化数组 structured_arr = np.array(rows, dtype=np_dtype) # 转成DataFrame data_frame = pd.DataFrame(structured_arr)
3. 先创建指定类型的空DataFrame再批量写入(不推荐大数据量)
如果一定要先构建空DataFrame再填充数据,需要先指定列名和对应类型,再用from_records批量写入(避免逐行append的低效):
代码示例:
import pandas as pd import numpy as np sql = "select premise_id, parent_id, addr_ward FROM table;" rows = safe_call_db_read(db.conn, sql) dtype = { 'premise_id': np.int32, 'parent_id': np.int32, 'addr_ward': 'string' } # 创建指定列和类型的空DataFrame empty_df = pd.DataFrame(columns=dtype.keys()).astype(dtype) # 批量写入数据 data_frame = empty_df.append( pd.DataFrame.from_records(rows, columns=dtype.keys(), dtype=dtype), ignore_index=True )
注意:这种方法对超大数据量的内存友好度不如前两种,因为append操作会生成新的DataFrame对象。
额外内存优化建议
- 整数类型:根据列的数值范围选择最小的类型(比如
np.int8/np.int16/np.int32),不要默认用np.int64。 - 字符串类型:优先用Pandas的
stringdtype替代object,或Numpy的固定长度字符串类型。 - 分类变量:如果某列是有限的枚举值(比如
addr_ward是固定的行政区列表),可以指定为category类型,能大幅降低内存占用。
内容的提问来源于stack exchange,提问作者Jan Janáček
相关产品推荐
相关产品推荐

