Pandas: 同时转换多个列会损坏数据,单独转换却不会?

0 投票
2 回答
58 浏览
提问于 2025-04-12 16:37

现在感觉自己有点傻,但我就是找不到一个好的办法来解决下面的问题。

我有一个数据框(dataframe),里面有三个不同的“部分”。

  1. 第一部分是13列的序列号数据,每一列都是一个单独的ASCII码。这些列的名字在列表[serialname_list]里。
  2. 第二部分是每一行都有的“核心数据”列。
  3. 第三部分是只有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__ 版本不同。

撰写回答