为何Pandas的pandas.unique()会忽略字符串中的特定字符?
问题分析与解决
问题现象
处理包含加密bytes字符串的数据集时,将bytes解码为字符串后调用pandas.unique(),返回的唯一值数量远低于预期,不同的相似字符串会被误判为重复;但pandas.duplicated()却能正确识别这些字符串为非重复。
示例代码的输出也验证了这个矛盾:
- 解码为字符串后调用
unique(),返回1个唯一值(实际应为2个) - 解码后重新编码回bytes再调用
unique(),返回正确的2个唯一值 - 两种情况下
duplicated()都返回0,说明能正确识别为非重复
问题原因
核心问题在于pandas对字符串类型的unique()方法,在处理包含特殊控制字符或非标准Unicode字符时,内部哈希/比较逻辑出现误判。当加密bytes被解码为latin1字符串后,字符串中包含大量不可见控制字符(如\x00、\r、\n等),这些字符在pandas的字符串哈希机制中可能被意外归一化,导致不同字符串的哈希值冲突,进而被判定为重复。
而duplicated()方法采用逐字符精确比较逻辑,不受哈希冲突影响,因此能正确识别差异。
解决方案
针对这类加密字符串数据,推荐以下几种处理方式:
1. 直接保留bytes类型处理
避免将bytes解码为字符串,直接对原始bytes列调用unique():
len_raw_bytes = len(df[0].unique()) print(f'原始bytes唯一值数量: {len_raw_bytes}.') # 输出2,正确
2. 解码后重新编码回bytes再处理
将字符串重新编码为latin1 bytes,绕开pandas字符串哈希的问题:
unique_vals = df[0].str.decode('latin1').str.encode('latin1').unique() print(len(unique_vals)) # 输出2,正确
3. 使用numpy的unique方法
借助numpy的unique()方法,其字符串比较逻辑更精确:
import numpy as np len_numpy_unique = len(np.unique(df[0].str.decode('latin1'))) print(f'numpy处理唯一值数量: {len_numpy_unique}.') # 输出2,正确
验证测试
运行修改后的测试代码:
import pandas as pd import numpy as np arr = [ b'''`\x95\x00\x95\xba\xe6M\xef\x9f\xdd\xc1\xe5\xc5\xd9\'\\e\x88O\xc4\\\xf7\xd2\xe8J8xJ\x84\x0c\xb1\x1b\xe5>Y\'\xd8\n\xf9U\xc0\xd5\xc8\xe8\xfc\xd5\xf8\xaf`1\xdc|$\x96\xdcin\x7f\x12\xa0\xbd\x03+_\x13o\xb1\x8a\x14\xc5\x0b\xb4\xa8\xfc\x12\xbd\xe3\x7fO\xa6\xf5\x89\xa6\xe2\xca\xa2\xccs\x890XP2\x87\x87\xa4M\xc1\xe8\x94\x83\xda5;4\xa0_\xff+\xdfj\xfe\xd5\xc9xo\xe2c\x95\x91N\x85\xbcG>_9\xabJ9\x9f?\xf0\xc6\xdfr\x943Qv\xf1\xcf\xd8\x92V8\x15\xdcO\x1a\xfd\xdd\xe2\x0ct\xe9z\xe8\'\xa1\xab7\xd8\xdf\xe5\x99\xa8\x19"(\r\x1f);\xae\x17''', b'''`\x95\x00\x95\xba\xe6M\xef\x9f\xdd\xc1\xe5\xc5\xd9'\\e\x88O\xc4\\\xf7\xd2\xe8J8xJ\x84\x0c\xb1\x1b\xe5>Y'\xd8\n\xf9U\xc0\xd5\xc8\xe8\xfc\xd5\xf8\xaf`1\xdc|$\x96\xdcin\x7f\x12\xa0\xbd\x03+_\x13o\xb1\x8a\x14\xc5\x0b\xb4\xa8\xfc\x12\xbd\xe3\x7fO\xa6\xf5\x89\xa6\xe2\xca\xa2\xccs\x890XP2\x87\x87\xa4M\xc1\xe8\x94\x83\xda5;4\xa0_\xff+\xdfj\xfe\xd5\xc9xo\xe2c\x95\x91N\x85\xbcG>_9\xabJ9\x9f?\xf0\xc6\xdfr\x943Qv\xf1\xcf\xd8\x92\x8a:\xd1\x8a\xeb]\x91\x89\xdb@\x931\xff\xb1}A\xb1\xc5\x98f\r\x019\x9f\x04n\xe9k#\x84d\xc8''' ] df = pd.DataFrame(data=arr) # 原始bytes直接处理 len_raw = len(df[0].unique()) # 解码后重编码处理 len_encode = len(df[0].str.decode('latin1').str.encode('latin1').unique()) # numpy处理 len_numpy = len(np.unique(df[0].str.decode('latin1'))) print(f'原始bytes唯一值数量: {len_raw}') print(f'解码后重编码唯一值数量: {len_encode}') print(f'numpy处理唯一值数量: {len_numpy}')
输出结果:
原始bytes唯一值数量: 2 解码后重编码唯一值数量: 2 numpy处理唯一值数量: 2
内容的提问来源于stack exchange,提问作者nadrods
相关产品推荐
相关产品推荐

