Pandas:基于特定值选择性连接列
我刚开始接触Pandas,遇到了一个问题,就是如何正确地把几列合并在一起。这似乎涉及到我研究过的所有其他问题。
我的数据有多个字段,前13个字段都是和序列号有关的(字段名是“sn1”到“sn13”)。这些字段之后的数据也很重要,但和我现在的问题无关。前11个字段包含了实际的序列号数据,剩下的字段是以后要解决的其他问题。这些字段名存储在一个列表中,叫做serialname_list
。
序列号的格式是:XXX-XXXX-XXX
唯一需要注意的是,有些序列号是:XXXA-XXX-XXX
这个“A”总是在“SN4”这个位置(也就是第4个字符)。每个字符最开始都是以ASCII码的形式存储在各自的字段里。我想写一个函数,接收这个数据框,先把ASCII码转换成字符,然后根据是否有“A”来正确格式化序列号,最后把格式化后的序列号存储在“Serial”字段中,并删除所有旧的字段。
我写的代码已经接近了,但似乎运行这两个函数时,会把之前的数据覆盖成“NAN”,这是因为序列号格式不一样。我不知道我哪里做错了,因为我以为我已经正确选择了那些==A
或!=A
的行。
另外,从我的研究来看,我觉得逐个遍历每一行的元素是不对的,因为应该用向量化的方式来处理。我的数据似乎是可以这样处理的(行要么包含“A”,要么不包含“A”,可以随意处理),但在实际操作中,我对如何正确使用Loc、Where、Apply和Query感到困惑。
def convertserial(df):
def long_serial(x):
tempstring =''
for i in range(len(serialname_list)-2):
tempstring = tempstring + x['sn' + str(i+1)]
if i == 3 or i == 7: #Follow pattern with adding dashes
tempstring = tempstring + '-'
return tempstring
def normal_serial(x):
tempstring =''
for i in range(len(serialname_list)-2):
tempstring = tempstring + x['sn' + str(i+1)]
if i == 2 or i == 6: #Follow alternate pattern with adding dashes
tempstring = tempstring + '-'
return tempstring
df = df[serialname_list].apply(lambda x: x.map(chr)) #Convert ASCII codes to characters
df['serial'] = df[df['sn4']=='A'].apply(long_serial,axis=1) #If A is present, longer than normal SN
df['serial'] = df[df['sn4']!='A'].apply(normal_serial,axis=1) #If A is not present, normal
return df
1 个回答
1
def convertserial(df):
df[serialname_list] = df[serialname_list].apply(lambda x: x.map(chr))
mask = df['sn4'] == 'A'
df['serial'] = df.apply(
lambda x: f"{x['sn1']}{x['sn2']}{x['sn3']}-{'A-' if x['sn4'] == 'A' else ''}{x['sn5']}{x['sn6']}{x['sn7']}-{x['sn8']}{x['sn9']}{x['sn10']}",
axis=1
)
df.drop(columns=serialname_list[:-1], inplace=True)
return df
这个看起来是有效的,已经在一个样本上测试过了。它解决了几个问题:
- 你遇到的NaN(不是一个数字)问题,可能是因为同一列被写了两次。一次是在检查A的时候,另一次是在检查不是A的时候。这里因为没有条件检查,第二个语句的结果覆盖了第一个。
- 我们把样本模板写死了,所以不需要根据代码来做任何修改。
- 修复这个问题也有助于解决数据丢失的情况,比如在我的运行中,我发现生成结果时最后两个字符被跳过了。
Output: sn1 sn2 sn3 sn4 sn5 sn6 sn7 sn8 sn9 sn10 0 74 79 76 66 87 74 72 65 86 80 1 81 66 89 65 86 67 89 81 80 65 2 82 81 78 66 90 82 79 80 71 80 3 65 68 80 66 82 78 88 90 80 77 4 86 73 70 66 74 68 83 65 84 86 5 79 83 78 66 84 77 65 71 73 76 6 89 84 76 66 83 78 87 74 85 69 7 73 68 71 66 76 68 82 68 65 70 8 77 67 87 66 68 79 70 65 73 65 9 76 82 81 65 86 66 65 71 70 79 sn10 serial 0 P JOL-WJH-AVP 1 A QBY-A-VCY-QPA 2 P RQN-ZRO-PGP 3 M ADP-RNX-ZPM 4 V VIF-JDS-ATV 5 L OSN-TMA-GIL 6 E YTL-SNW-JUE 7 F IDG-LDR-DAF 8 A MCW-DOF-AIA 9 O LRQ-A-VBA-GFO
关于数据生成
data = {
'sn1': np.random.randint(65, 91, size=10),
'sn2': np.random.randint(65, 91, size=10),
'sn3': np.random.randint(65, 91, size=10),
'sn4': [65 if np.random.rand() < 0.5 else np.random.randint(65, 91) for _ in range(10)],
'sn5': np.random.randint(65, 91, size=10),
'sn6': np.random.randint(65, 91, size=10),
'sn7': np.random.randint(65, 91, size=10),
'sn8': np.random.randint(65, 91, size=10),
'sn9': np.random.randint(65, 91, size=10),
'sn10': np.random.randint(65, 91, size=10),
}
df = pd.DataFrame(data)
serialname_list = ['sn1', 'sn2', 'sn3', 'sn4', 'sn5', 'sn6', 'sn7', 'sn8', 'sn9', 'sn10']