去除/修剪数据框中所有字符串的空格

157

在Python/Pandas中清洗多类型数据框的值,我想要裁剪字符串。我目前使用两个指令来完成:

import pandas as pd

df = pd.DataFrame([['  a  ', 10], ['  c  ', 5]])

df.replace('^\s+', '', regex=True, inplace=True) #front
df.replace('\s+$', '', regex=True, inplace=True) #end

df.values

这很慢,我应该如何提升速度?


6
df.replace(r'\s*(.*?)\s*', r'\1', regex=True) - MaxU - stand with Ukraine
1
这是最好的答案,我只是登录来点赞@MaxU的回答。 - Linkon
@MaxU的回答是最简单的。谢谢。 - moys
我为下面的答案添加了基准测试。请注意,这里评论中的那个非常慢。请适当使用。 - Jamie Marshall
10个回答

274
你可以使用 DataFrame.select_dtypes 选择 string 列,然后使用 apply 函数应用 str.strip 函数。
注意:值不能是像字典或列表这样的类型,因为它们的 dtypesobject
df_obj = df.select_dtypes(['object'])
print (df_obj)
0    a  
1    c  

df[df_obj.columns] = df_obj.apply(lambda x: x.str.strip())
print (df)

   0   1
0  a  10
1  c   5

但如果只有几列,请使用str.strip函数:

df[0] = df[0].str.strip()

3
在这种情况下,应该忽略SettingWithCopyWarning警告,如https://dev59.com/8WIj5IYBdhLWcg3wHhlX?answertab=oldest#tab-top所述。 - Hrvoje
2
如果您有像N/A这样的字符串,当执行df_obj.apply时,您需要添加参数na_action="ignore",否则pandas将把这些值转换为空字符串。 - Justin Furuness

141

赚钱之路

这是一个精简版的示例,使用直接的lambda表达式结合applymap方法来调用strip函数,仅在值为字符串类型时进行处理:

df.applymap(lambda x: x.strip() if isinstance(x, str) else x)

完整示例

以下是一个更完整的示例:

import pandas as pd


def trim_all_columns(df):
    """
    Trim whitespace from ends of each value across all series in dataframe
    """
    trim_strings = lambda x: x.strip() if isinstance(x, str) else x
    return df.applymap(trim_strings)


# simple example of trimming whitespace from data elements
df = pd.DataFrame([['  a  ', 10], ['  c  ', 5]])
df = trim_all_columns(df)
print(df)


>>>
   0   1
0  a  10
1  c   5

工作示例

以下是由trinket托管的工作示例: https://trinket.io/python3/e6ab7fb4ab


1
嗨@DaleKube ... 我只是在新机器上尝试了一下,以作为一个健全性检查,我得到与答案中发布的相同的结果。您能否确认您是使用Python2还是Python3?我现在只使用Python3,但也许这可能是因素之一。如果是这样,如果您能够确认,我将在我的回答中注明。谢谢! - Jonathan B.
1
我删除了我的评论。我在我的代码中发现了一个错误,并且我可以确认它现在像魅力一样工作。顺便说一下,我正在使用Python 3。对此给您带来的不便表示抱歉。 - Dale Kube
1
你应该使用 type(x) == str,而不是 type(x) is str - fjsj
1
@fjsj 感谢提醒。我已经更新了示例,采用了PEP8指南中更倾向于使用isinstance(x, str)的方式。 - Jonathan B.
很好的解决方案!如果我从CSV加载df,则不会修剪列名。 - csf
对于未来的读者:最后的 else 部分是针对整个部分而不是其相邻的 if - zionpi

15

您可以尝试:

df[0] = df[0].str.strip()

更具体地说,适用于所有字符串列

non_numeric_columns = list(set(df.columns)-set(df._get_numeric_data().columns))
df[non_numeric_columns] = df[non_numeric_columns].apply(lambda x : str(x).strip())

如果您的代码有NaN值,它将失败。 - Tushar Seth

13

如果您真的想使用正则表达式,那么

>>> df.replace('(^\s+|\s+$)', '', regex=True, inplace=True)
>>> df
   0   1
0  a  10
1  c   5

但是这样做应该会更快:

>>> df[0] = df[0].str.strip()

10
你可以使用 Series 对象的 apply 函数
>>> df = pd.DataFrame([['  a  ', 10], ['  c  ', 5]])
>>> df[0][0]
'  a  '
>>> df[0] = df[0].apply(lambda x: x.strip())
>>> df[0][0]
'a'

请注意使用strip而不是更快的regex

另一个选项 - 使用DataFrame对象的apply函数

>>> df = pd.DataFrame([['  a  ', 10], ['  c  ', 5]])
>>> df.apply(lambda x: x.apply(lambda y: y.strip() if type(y) == type('') else y), axis=0)

   0   1
0  a  10
1  c   5

2
df[0] = df[0].str.strip() - 在较大的数据框中,很可能会更快。 - MaxU - stand with Ukraine

3

仅使用strip无法移除字符串中的额外空格。解决方法是先将一个或多个空格替换为单个空格。这可以确保我们删除额外的内部空格和外部空格。

# Import packages
import re 

# First inspect the dtypes of the dataframe
df.dtypes

# First replace one or more spaces with a single space. This ensures that we remove extra inner spaces and outer spaces.
df = df.applymap(lambda x: re.sub('\s+', ' ', x) if isinstance(x, str) else x)


# Then strip leading and trailing white spaces
df = df.apply(lambda x: x.str.strip() if isinstance(x, object) else x)

3

@jezrael的答案看起来不错。但是,如果您想在最终结果集中同时获取其他(数字/整数等)列,那么您应该需要将其与原始DataFrame合并。

如果是这种情况,那么您可以使用以下方法:

df = df.apply(lambda x: x.str.strip() if x.dtype.name == 'object' else x, axis=0)

谢谢!


2

最佳答案的基准:

bm = Benchmark()
df = pd.read_excel(
    path, 
    sheet_name=advantage_sheet_name, 
    parse_dates=True
)
bm.mark('Loaded')

# @jezrael 's answer (accepted answer)
dfClean_1 = df\
    .select_dtypes(['object'])\
    .apply(lambda x: x.str.strip())
bm.mark('Clean method 1')

# @Jonathan B. answer 
dfClean_2 = df\
    .applymap(lambda x: x.strip() if isinstance(x, str) else x)
bm.mark('Clean method 2')

#@MaxU - stop genocide of UA / @Roman Pekar answer 
dfClean_3 = df\
    .replace(r'\s*(.*?)\s*', r'\1', regex=True)
bm.mark('Clean method 3')

结果

145.734375 - 145.734375 : Loaded
147.765625 - 2.03125 : Clean method 1
155.109375 - 7.34375 : Clean method 2
288.953125 - 133.84375 : Clean method 3

0

对于字符串列怎么样?

df[col] = df[col].str.replace(" ","")

永不失败


2
这不仅会剥离字符串的末尾,还会剥离字符串本身内部的所有空格。 - skjerns

-3
def trim(x):
    if x.dtype == object:
        x = x.str.split(' ').str[0]
    return(x)

df = df.apply(trim)

1
请问您能否解释一下这个函数在做什么? - CJ Dennis
例如,在我的日常工作中,我会遇到以下类似的数据: 가나다 봻 左侧是我想要的内容,右侧是垃圾。 使用 trim 函数可以从原始数据中提取我需要的内容。 - hyunwoo jeong
1
因为这个代码没有修剪字符串,它会删除第一个空格后面的所有内容。这不是问题所要求的行为,并且引入了读者可能没有预料到的副作用。 此外,这些副作用可能不会立即显现。如果您正在尝试修剪姓氏列,您可能会认为这是按预期工作的,因为大多数人没有多个姓氏并且尾随空格已被删除。然后,一个有两个姓氏的葡萄牙人加入您的网站,该代码将修剪掉他们的最后一个姓氏,只留下他们的第一个姓氏。 - scottclowe

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