使用pd.read_sql_query读取MS SQL数据后,字节串调用.decode()返回NaN的原因及转换为常规字符串的方案
这个问题我之前也碰到过,很典型——你的id列其实不是真正的字节串,而是字符串形式的字节串表示,这就是str.decode()返回NaN的原因,下面给你拆解一下并解决:
问题根源分析
你提供的重建DataFrame字典里,id列的每个值是类似"b'DE91EBA3-313D-463C-B948-00010AA26136'"的字符串,而不是实际的bytes对象。当你调用df['id'].str.decode('utf-8')时,pandas的str访问器会尝试对每个字符串元素调用decode方法,但普通Python字符串根本没有这个方法,所以pandas只能返回NaN。
解决方案
方法1:直接字符串切片(最快速高效)
因为你的id格式非常固定——都是b'UUID'这种形式,直接去掉开头的b'和结尾的'就能拿到目标字符串:
df['id'] = df['id'].str[2:-1]
运行后你的id列就会变成正常的UUID字符串,类型保持为object(如果需要可以额外调用astype(str)确保类型统一)。
方法2:用ast.literal_eval转换(更通用)
如果你的字节串表示格式可能有变化,或者想严格按照"字符串→字节对象→字符串"的流程转换,可以用ast.literal_eval(它比原生eval安全,只会解析合法的Python字面量):
import ast df['id'] = df['id'].apply(lambda x: ast.literal_eval(x).decode('utf-8'))
这个方法会先把字符串形式的"b'xxx'"转换成真正的bytes对象,再调用decode('utf-8')转成常规字符串,结果和方法1一致,适合更复杂的字节串表示场景。
额外提示:从源头避免问题
这种情况通常是数据库驱动在返回数据时,自动把字节对象转换成了它的字符串表示。如果想从查询阶段就解决,可以尝试调整pypyodbc的连接参数,或者在pd.read_sql_query中指定dtype参数:
df = pd.read_sql_query(query, cnxn, dtype={'id': str})
不过这个效果取决于你的MS SQL驱动配置,所以上面的两种DataFrame处理方法更稳妥可靠。
内容的提问来源于stack exchange,提问作者sander

