Spyder调试/正常模式下Pandas read_csv分隔符异常问题
导入存储测量数据的CSV文件时,逻辑为根据测量日期、测量设备匹配对应文件名,将同一设备对应的文件按时间顺序逐次拼接,实现代码如下:
import numpy as np import pandas as pd import os sonicpath = *PathtoFiles* def import_sonic(): Dates = np.array([1020, 1021, 1022, 1023, 1024, 1025, 1026, 1027, 1028, 1029, 1030, 1031, 1101, 1102])+20210000 Sensors = ['UwGilHs40', 'UwGilHs135', 'UwmetuS20', 'UwmetuS20', 'UwMetuS60', 'UwMetuS100', 'UwMetuS188', 'UwThi3DH80', 'UwThi3DH140'] Ext = 'csv' dic_sonics = {} for Sensor in Sensors: Filenames = [] dic_sonics[Sensor] = pd.DataFrame([]) for Date in Dates: Filenames.append('.'.join(['_'.join([str(Date), Sensor]), Ext])) for Filename in Filenames: with open(os.path.join(sonicpath, Filename)) as f: lines = f.readlines() count = 0 for line in lines: if 'Timestamp' not in line: count = count+1 else: skip = count-1 break dic_sonics[Sensor] = dic_sonics[Sensor].append( pd.read_csv(os.path.join(sonicpath, Filename), skiprows = skip, header=1, sep = ';', decimal='.', ), ignore_index = True) timestamps = dic_sonics[Sensor].loc[:, 'Timestamp'] # 时间取整到秒,转换为Unix时间戳 for its in timestamps.index: dic_sonics[Sensor].loc[its, 'Timestamp'] = int((timestamps.loc[ its].round('S') - pd.Timestamp("1970-01-01")).total_seconds()) return(dic_sonics)
已知当前实现存在可优化空间,但运行时出现如下异常:
代码中pd.read_csv已明确指定*sep=';'*作为字段分隔符,使用Spyder的「Run File(正常运行文件)」选项执行时结果完全符合预期,可正确识别全部DataFrame列,正常输出列索引如下:
Index(['Timestamp', 'Counts', 'x_mean', 'x_min', 'x_max', 'x_sigma', 'y_mean', 'y_min', 'y_max',
'y_sigma', 'z_mean', 'z_min', 'z_max', 'z_sigma', 'T_mean', 'T_min', 'T_max', 'T_sigma',
'vel_mean', 'vel_min', 'vel_max', 'vel_sigma', 'dir_mean', 'dir_min', 'dir_max',
'dir_sigma', 'IncX_mean', 'IncX_min', 'IncX_max', 'IncY_mean', 'IncY_min', 'IncY_max'],
dtype='object')
但使用「Debug File(调试文件)」模式执行时,Pandas异常将字符e识别为分隔符,列被错误拆分,同时因找不到Timestamp列抛出日期解析错误,错误输出的列索引如下:
Index(['Tim', 'stamp;Counts;x_m', 'an;x_min;x_max;x_sigma;y_m',
'an;y_min;y_max;y_sigma;z_m', 'an;z_min;z_max;z_sigma;T_m',
'an;T_min;T_max;T_sigma;v', 'l_m', 'an;v', 'l_min;v', 'l_max;v',
'l_sigma;dir_m', 'an;dir_min;dir_max;dir_sigma'],
dtype='object')
补充测试结果:调试模式下若在导入逻辑前设置断点,通过控制台手动执行该行read_csv代码则可正常运行。
当前运行环境信息:Spyder 5.3.0、Python 3.7.8、pandas 1.2.3、numpy 1.21.3。
这是Spyder 5.3.0调试器的已知兼容bug,和pandas默认C解析引擎的内存读写逻辑冲突导致:
- 调试模式下Spyder会自动注入变量探查逻辑,后台遍历当前栈帧内的所有变量读取值,用于在变量面板展示实时值,这个过程会误覆写pandas C引擎初始化阶段的sep参数内存值。你看到的按
e拆分不是随机问题,就是原本传入的*sep=';'*被覆写成了相邻内存位的字符e(刚好是字段名Timestamp里的e,和错误拆分的列名特征完全匹配)。 - 正常运行模式不会注入变量探查逻辑,sep参数不会被篡改,因此运行正常;断点后手动在控制台执行read_csv时,代码执行的上下文不在调试器自动遍历的栈帧范围内,不会触发参数覆写,因此也能正常运行。
按落地成本从低到高选择即可:
- 修改
pd.read_csv参数,强制指定Python解析引擎:给读取逻辑新增参数engine='python',直接绕开C引擎的内存覆写问题,不需要升级任何依赖,对现有逻辑影响最小,修改后调试、正常运行均可正常解析。修改后的读取代码片段如下:pd.read_csv(os.path.join(sonicpath, Filename), skiprows = skip, header=1, sep = ';', decimal='.', engine='python' ) - 升级Spyder到5.3.3及以上版本,官方在后续补丁中修复了调试器变量探查误写第三方库内存的问题,升级后无需修改代码即可正常运行。
- 关闭调试器自动变量探查功能:在Spyder设置的调试器选项页,取消勾选「调试时自动加载变量值」,调试器就不会后台遍历栈帧变量,自然不会触发参数覆写,缺点是调试时无法直接在变量面板查看实时值,排查问题效率会降低。
- 升级pandas到1.3.0及以上版本,新版本调整了
read_csv参数的内存存储逻辑,不会被外部内存读写操作篡改参数值,但要注意pandas大版本升级可能带来其他API兼容问题,现有代码需要做适配验证。
内容的提问来源于stack exchange,提问作者bürkí

