Pandas: 同时转换多个列会损坏数据,单独转换却不会?
现在感觉自己有点傻,但我就是找不到一个好的办法来解决下面的问题。
我有一个数据框(dataframe),里面有三个不同的“部分”。
- 第一部分是13列的序列号数据,每一列都是一个单独的ASCII码。这些列的名字在列表
[serialname_list]
里。 - 第二部分是每一行都有的“核心数据”列。
- 第三部分是只有80%的行才有的“附加数据”列。
这个数据框大概长这样:
sn1 sn2 ... sn13 data1 data2 add1 add2
0 74 79 76 66 5.0 5.0 NaN NaN
1 81 66 89 65 1.0 1.0 NaN NaN
2 82 81 78 66 2.0 2.0 79 80
我正在写一个函数,把这13个ASCII码转换成实际的ASCII字符,然后把它们合并成一个字段,并删除这些单独的列。有些“序列号”字段的数据坏掉了,里面有明显错误的浮点值,对于这些情况,我就直接删除任何包含非标准数据的行。
在转换ASCII码时,我遇到了问题。之前我用的是:
df[serialname_list] = df[serialname_list].apply(lambda x: x.map(chr))
但是,我注意到当这行代码运行时,数据框删除了所有的“附加数据”列,以及任何“附加数据”是NaN的行。
尝试对[serialname_list]
中定义的字段使用不同的聚合函数,总是会出现这个问题(比如使用apply(map)、replace等)。不过,直接引用这13列并应用转换函数是没问题的。
我到底漏掉了什么?有没有什么正确的方法可以做到这一点,而不需要逐个列出每个字段名?
def convertserial(df):
def chr_convert(x):
y = chr(x)
return y
msk = df[serialname_list].lt(1).any(axis=1) #Mask where any sn column is less than 1, real data not ASCII
idx_to_drop = df.index[msk] #Create indicies based on mask
df = df.drop(idx_to_drop) #drop based on the index
df = df.reset_index(drop=True)
df[serialname_list]=df[serialname_list].astype(int)
df['sn1'] = df['sn1'].apply(chr_convert)
df['sn2'] = df['sn2'].apply(chr_convert)
df['sn3'] = df['sn3'].apply(chr_convert)
df['sn4'] = df['sn4'].apply(chr_convert)
df['sn5'] = df['sn5'].apply(chr_convert)
df['sn6'] = df['sn6'].apply(chr_convert)
df['sn7'] = df['sn7'].apply(chr_convert)
df['sn8'] = df['sn8'].apply(chr_convert)
df['sn9'] = df['sn9'].apply(chr_convert)
df['sn10'] = df['sn10'].apply(chr_convert)
df['sn11'] = df['sn11'].apply(chr_convert)
df['sn12'] = df['sn12'].apply(chr_convert)
df['sn13'] = df['sn13'].apply(chr_convert)
#df[serialname_list] = df[serialname_list].apply(chr_convert)
df.insert(0, "serial", df.apply(
lambda x: f"{x['sn1']}{x['sn2']}{x['sn3']}{x['sn4']+'-'+x['sn5']+x['sn6']+x['sn7']+x['sn8']+'-'+x['sn9']+x['sn10']+x['sn11'] if x['sn4'] == 'A' else '-'+x['sn4']+x['sn5']+x['sn6']+x['sn7']+'-'+x['sn8']+x['sn9']+x['sn10']}",
axis=1
))
df.insert(1,"position", df.apply(
lambda x: f"{x['sn12']+x['sn13'] if x['sn4'] == 'A' else x['sn11']+x['sn12']}",
axis=1
))
df.drop(columns=serialname_list[:],inplace=True)
return df
2 个回答
1
这里有一个使用 .map 的示例代码,数据比较简单:
import pandas as pd
df = pd.DataFrame({'x1': [65, 66, 67],
'x2': [68, 69, 70],
'x3': [71, 72, 73],
'y': [10, 11, 12]
})
x_list = ['x1', 'x2', 'x3']
df[x_list] = df[x_list].map(chr)
print(df)
运行后得到:
x1 x2 x3 y
0 A D G 10
1 B E H 11
2 C F I 12
接下来是把这些字符连接起来:
df['x'] = pd.Series(map(''.join, df[x_list].values.astype(str).tolist()))
结果是:
x1 x2 x3 y x
0 A D G 10 ADG
1 B E H 11 BEH
2 C F I 12 CFI
2
你可以使用 .apply(…, axis=1)
来对 pandas
中的字符串进行操作,这种操作需要用到一些只有在 Python 里才能实现的功能,比如在多个列之间进行格式化。
import pandas as pd
df = pd.DataFrame({
'sn1': [65, 66, 67],
'sn2': [68, 69, 70],
'sn3': [71, 72, 73],
'sn4': [74, 75, 76],
'sn5': [77, 78, 79],
'y': [10, 11, 12]
})
df['serial'] = (
df.filter(like='sn')
.applymap(chr) # .applymap for pandas < 2.1.0 OR .map for pandas >= 2.1.0
# format across multiple columns
.apply('({sn1})({sn2})-{sn3}:{sn4}-{sn5}'.format_map, axis=1)
)
print(df)
# sn1 sn2 sn3 sn4 sn5 y serial
# 0 65 68 71 74 77 10 (A)(D)-G:J-M
# 1 66 69 72 75 78 11 (B)(E)-H:K-N
# 2 67 70 73 76 79 12 (C)(F)-I:L-O
DataFrame.apply(…, axis=0)
(注意 axis=0
是默认设置)会把每一列的数据传递给指定的函数。然后,这些列的数据会被合并成一个新的 DataFrame
。
import pandas as pd
print(pd.__version__) # 1.5.3
df = pd.DataFrame({
'sn1': [65, 66, 67],
'sn2': [68, 69, 70],
'sn3': [71, 72, 73],
'sn4': [74, 75, 76],
'sn5': [77, 78, 79],
'y': [10, 11, 12]
})
snlist = ['sn1', 'sn2', 'sn3']
df[snlist] = df[snlist].apply(lambda s: s.map(chr))
print(df)
# sn1 sn2 sn3 sn4 sn5 y
# 0 A D G 74 77 10
# 1 B E H 75 78 11
# 2 C F I 76 79 12
如果上述方法对你不起作用,可能是因为我们使用的 pandas.__version__
版本不同。