Pandas为何变更DataFrame数据类型?如何保留原类型?
Pandas修改列值后int列变float的问题解析
问题背景
初始DataFrame数据类型:
id int64 symbol int64 period int64 start_at int64 open float64 high float64 low float64 close float64 volume int64 turnover float64 cross_seq int64 last_apply_1min_timestamp int64 updated_at object dtype: object
使用循环修改float类型列的值(GetOHIC返回均为float64):
for i in range(len(raw_sql_data_period_1)): temp_start_at_timestamp = raw_sql_data_period_1['start_at'].iloc[i] temp_data = raw_data[(raw_data['timestamp'] >= temp_start_at_timestamp*1e3) & (raw_data['timestamp'] <= (temp_start_at_timestamp+60)*1e3)] if len(temp_data) == 0 : continue open_price, high, low, close_price = GetOHIC(temp_data) raw_sql_data_period_1['open'].iloc[i] = open_price raw_sql_data_period_1['high'].iloc[i] = high raw_sql_data_period_1['low'].iloc[i] = low raw_sql_data_period_1['close'].iloc[i] = close_price
但循环结束后,所有int64列均变为float64:
id float64 symbol float64 period float64 start_at float64 open float64 high float64 low float64 close float64 volume float64 turnover float64 cross_seq float64 last_apply_1min_timestamp float64 updated_at object dtype: object
Pandas变更数据类型的场景及原因
- 链式赋值触发隐式类型提升:你用的
df['col'].iloc[i] = ...属于链式索引操作,Pandas无法保证这是在操作原DataFrame的视图还是临时副本。当这种操作触发数据重新分配时,为了避免数据丢失,Pandas会把所有数值列统一转换为更宽泛的类型(float能兼容int,所以int列被强制提升为float)。 - 底层numpy数组的类型约束:Pandas的数值列基于numpy数组存储,结构化数组在修改时如果出现内存布局变化,会自动提升类型以容纳所有可能的数据——哪怕你只修改了float列,也可能牵连其他int列。
- 视图/副本的模糊性:链式索引的写法会让Pandas的索引机制混乱,修改操作可能意外作用到临时副本上,导致原DataFrame的元数据(包括类型)被错误更新。
保留原有数据类型的解决方法
1. 用.loc替代链式赋值(最直接)
把循环里的赋值语句改成直接用.loc定位行和列,这是Pandas推荐的修改单值方式,不会创建临时对象,能稳定保留原类型:
raw_sql_data_period_1.loc[i, 'open'] = open_price raw_sql_data_period_1.loc[i, 'high'] = high raw_sql_data_period_1.loc[i, 'low'] = low raw_sql_data_period_1.loc[i, 'close'] = close_price
2. 事后强制转换int列(补救方案)
如果已经出现类型变更,可以手动把int列转回去:
int_columns = ['id', 'symbol', 'period', 'start_at', 'volume', 'cross_seq', 'last_apply_1min_timestamp'] raw_sql_data_period_1[int_columns] = raw_sql_data_period_1[int_columns].astype('int64')
3. 用批量操作替代循环(更高效)
循环逐行修改性能极低,推荐用Pandas的矢量化分组操作来计算OHLC,再合并到原DataFrame,从根源避免类型问题:
# 将raw_data的timestamp转换为分钟级start_at(和原DataFrame对齐) raw_data['start_at'] = (raw_data['timestamp'] // 1000).astype('int64') # 分组计算每个时间窗口的OHLC ohlc_result = raw_data.groupby('start_at').agg( open=('price', 'first'), high=('price', 'max'), low=('price', 'min'), close=('price', 'last') ).reset_index() # 合并到原DataFrame,空值保留原列数据 raw_sql_data_period_1 = raw_sql_data_period_1.merge(ohlc_result, on='start_at', how='left', suffixes=('', '_new')) raw_sql_data_period_1['open'] = raw_sql_data_period_1['open_new'].fillna(raw_sql_data_period_1['open']) raw_sql_data_period_1['high'] = raw_sql_data_period_1['high_new'].fillna(raw_sql_data_period_1['high']) raw_sql_data_period_1['low'] = raw_sql_data_period_1['low_new'].fillna(raw_sql_data_period_1['low']) raw_sql_data_period_1['close'] = raw_sql_data_period_1['close_new'].fillna(raw_sql_data_period_1['close']) # 清理临时列 raw_sql_data_period_1 = raw_sql_data_period_1.drop(['open_new', 'high_new', 'low_new', 'close_new'], axis=1)
内容的提问来源于stack exchange,提问作者Molin Liu
相关产品推荐
相关产品推荐

