从pandas列中删除非ASCII字符

38

我已经尝试了一段时间解决这个问题。我正在尝试从DB_user列中删除非ASCII字符并用空格替换它们。但是我一直收到一些错误信息。下面是我的数据框的样子:

+-----------------------------------------------------------
|      DB_user                            source   count  |                                             
+-----------------------------------------------------------
| ???/"Ò|Z?)?]??C %??J                      A        10   |                                       
| ?D$ZGU   ;@D??_???T(?)                    B         3   |                                       
| ?Q`H??M'?Y??KTK$?ً???ЩJL4??*?_??        C         2   |                                        
+-----------------------------------------------------------

我使用了在SO上研究该问题时发现的以下函数:

def filter_func(string):
   for i in range(0,len(string)):


      if (ord(string[i])< 32 or ord(string[i])>126
           break

      return ''

And then using the apply function:

df['DB_user'] = df.apply(filter_func,axis=1)

我一直收到这个错误信息:
'ord()期望一个字符,但发现长度为66的字符串',u'出现在索引2处'
然而,我认为通过在filter_func函数中使用循环,我正在通过将字符输入到'ord'中来处理此问题。因此,一旦遇到非ASCII字符,它就应该被替换为空格。
有人可以帮帮我吗?
谢谢!
8个回答

43

你可以尝试这个:

df.DB_user.replace({r'[^\x00-\x7F]+':''}, regex=True, inplace=True)

6
好的答案,这也适用于整个DataFrame。 - elPastor
4
这个任务和问题中展示的任务略有不同 - 它接受所有ASCII字符,而问题中的示例代码通过从字符32开始而不是0来拒绝不可打印字符。字符\x00可以替换为一个空格,使这个答案在行为上与被接受的答案相匹配。 - Brandon Rhodes

37

一种常见的技巧是使用 errors="ignore" 标志执行 ASCII 编码,然后将其解码为 ASCII:

df['DB_user'].str.encode('ascii', 'ignore').str.decode('ascii')

从python3.x及以上版本开始,这是我的推荐解决方案。


最小代码示例

s = pd.Series(['Déjà vu', 'Ò|zz', ';test 123'])
s

0      Déjà vu
1         Ò|zz
2    ;test 123
dtype: object


s.str.encode('ascii', 'ignore').str.decode('ascii')

0        Dj vu
1          |zz
2    ;test 123
dtype: object

附言:这也可以扩展到需要过滤掉不属于任何字符编码方案(不仅仅是ASCII)的字符的情况。


13

您的代码失败了,因为您没有对每个字符应用它,而是按单词应用它,并且 ord 错误会因为它只接受单个字符而出现,您需要:

  df['DB_user'] = df["DB_user"].apply(lambda x: ''.join([" " if ord(i) < 32 or ord(i) > 126 else i for i in x]))

您还可以使用链式比较简化连接操作:

   ''.join([i if 32 < ord(i) < 126 else " " for i in x])

你也可以使用 string.printable 来过滤字符:
from string import printable
st = set(printable)
df["DB_user"] = df["DB_user"].apply(lambda x: ''.join([" " if  i not in  st else i for i in x]))

最快的方法是使用翻译功能:

from string import maketrans

del_chars =  " ".join(chr(i) for i in range(32) + range(127, 256))
trans = maketrans(t, " "*len(del_chars))

df['DB_user'] = df["DB_user"].apply(lambda s: s.translate(trans))

有趣的是,这比以下内容更快:

  df['DB_user'] = df["DB_user"].str.translate(trans)

@red_devil,没问题,有很多不同的方法可以做到这一点,但知道你哪里出错了很重要。 - Padraic Cunningham
值得注意的是,上述方法不适用于Python 3(例如,您不能执行range(..) + range(..))。 - Floran Gmehlin
1
第三个和第四个方法中未定义t - Dimgold
值得注意的是,maketrans 现在是 str 内置类型上的一个方法。 - Idr

3
一些回答在这里是不正确的。简单验证:

最初的回答

s = pd.Series([chr(x) for x in range(256)])
s.loc[0]
>> '\x00'
s.replace({r'[^\x00-\x7F]+':''}, regex=True).loc[0]
>> '\x00'  # FAIL
s.str.encode('ascii', 'ignore').str.decode('ascii').loc[0]
>> '\x00'  # FAIL
s.apply(lambda x: ''.join([i if 32 < ord(i) < 126 else " " for i in x])).loc[0]
>> ' '  # Success!
import string
s.apply(lambda x: ''.join([" " if  i not in string.printable else i for i in x])).loc[0]
>> ' '  # Looks good, but...
s.apply(lambda x: ''.join([" " if  i not in string.printable else i for i in x])).loc[11]
>> '\x0b'  # FAIL
del_chars =  " ".join([chr(i) for i in list(range(32)) + list(range(127, 256))])
trans = str.maketrans(del_chars, " " * len(del_chars))
s.apply(lambda x: x.translate(trans)).loc[11]
>> ' '  # Success!

结论:只有被接受的回答中(来自Padraic Cunningham)的选项能够可靠地工作。他的第二个答案中存在一些奇怪的Python错误和拼写错误,在此进行了更正,但除此之外它应该是最快的。
最初的回答。

0
这是我使用的一行代码:
df = df.replace(to_replace="/[^ -~]+/g", value="", regex=True)
使用正则表达式,它全局删除不在' '(空格)和~范围内的字符。

0
from string import printable

def printable_mapper(x): 
    return ''.join([_ if _ in printable else " " for _ in x])

df.DB_user = df.DB_user.map(printable_mapper)

0

这个对我很有用。由于该系列具有一些NaN值,因此它仅对字符串执行操作:

from string import printable

import pandas as pd

df["text_data"] = df["text_data"].str.split().str.join(' ')

df["text_data"] = df["text_data"].apply(lambda string_var: ''.join(filter(lambda y: y in printable, string_var)) if isinstance(string_var, str) else string_var)

-1
这对我有用:
import re
def replace_foreign_characters(s):
    return re.sub(r'[^\x00-\x7f]',r'', s)

df['column_name'] = df['column_name'].apply(lambda x: replace_foreign_characters(x))

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接