删除 pandas DataFrame 列中字符串条目的结尾

Remove ends of string entries in pandas DataFrame column

我有一个 pandas 数据框,其中一列是文件列表

import pandas as pd
df = pd.read_csv('fname.csv')

df.head()

filename    A    B    C
fn1.txt   2    4    5
fn2.txt   1    2    1
fn3.txt   ....
....

我想从 filename 中的每个条目中删除文件扩展名 .txt。我该如何做到这一点?

我试过了:

df['filename'] = df['filename'].map(lambda x: str(x)[:-4])

但是当我随后使用 df.head() 查看列条目时,没有任何改变。

如何做到这一点?

您可以使用 str.rstrip 删除结尾:

df['filename'] = df['filename'].str.rstrip('.txt')

应该可以

我想你可以使用 str.replace with regex .txt$' ( $ - matches the end of the string):

import pandas as pd

df = pd.DataFrame({'A': {0: 2, 1: 1}, 
                   'C': {0: 5, 1: 1}, 
                   'B': {0: 4, 1: 2}, 
                   'filename': {0: "txt.txt", 1: "x.txt"}}, 
                columns=['filename','A','B', 'C'])

print df
  filename  A  B  C
0  txt.txt  2  4  5
1    x.txt  1  2  1

df['filename'] = df['filename'].str.replace(r'.txt$', '')
print df
  filename  A  B  C
0      txt  2  4  5
1        x  1  2  1

df['filename'] = df['filename'].map(lambda x: str(x)[:-4])
print df
  filename  A  B  C
0      txt  2  4  5
1        x  1  2  1

df['filename'] = df['filename'].str[:-4]
print df
  filename  A  B  C
0      txt  2  4  5
1        x  1  2  1

编辑:

rstrip可以去除更多的字符,如果字符串的末尾包含一些条纹字符串的字符(在本例中为.tx):

示例:

print df
  filename  A  B  C
0  txt.txt  2  4  5
1    x.txt  1  2  1

df['filename'] = df['filename'].str.rstrip('.txt')

print df
  filename  A  B  C
0           2  4  5
1           1  2  1

您可能需要:

df['filename'] = df.apply(lambda x: x['filename'][:-4], axis = 1)

使用列表理解

df['filename'] = [x[:-4] for x in df['filename']]

更新 2021 + 速度测试

从pandas 1.4开始,实现了str.removesuffix, the pandas.Series.str.removesuffix的等价物,所以可以使用

df['filename'].str.removesuffix('.txt')

速度测试

tl;dr:最快的是

dat["fname"].map(lambda x: x[:-4] if x[-4:] == ".txt" else x)

在速度测试中,我想考虑这个SO页面中收集的不同方法。我排除了 rstrip,因为它也会去除 .txt 以外的结尾,并且由于正则表达式包含条件,因此修改其他函数也是公平的,以便它们仅在它们删除最后 4 个字符时是 .txt.

测试代码为

import pandas as pd
import time

ITER = 10


def rm_re(dat: pd.DataFrame) -> pd.Series:
    """Use regular expression."""
    return dat["fname"].str.replace(r'.txt$', '', regex=True)


def rm_map(dat: pd.DataFrame) -> pd.Series:
    """Use pandas map, find occurrences and remove with []"""
    where = dat["fname"].str.endswith(".txt")
    dat.loc[where, "fname"] = dat["fname"].map(lambda x: x[:-4])
    return dat["fname"]


def rm_map2(dat: pd.DataFrame) -> pd.Series:
    """Use pandas map with lambda conditional."""
    return dat["fname"].map(lambda x: x[:-4] if x[-4:] == ".txt" else x)


def rm_apply_str_suffix(dat: pd.DataFrame) -> pd.Series:
    """Use str method suffix with pandas apply"""
    return dat["fname"].apply(str.removesuffix, args=(".txt",))


def rm_suffix(dat: pd.DataFrame) -> pd.Series:
    """Use pandas removesuffix from version 1.6"""
    return dat["fname"].str.removesuffix(".txt")


functions = [rm_map2, rm_apply_str_suffix, rm_map, rm_suffix, rm_re]
for base in range(12, 23):
    size = 2**base
    data = pd.DataFrame({"fname": ["fn"+str(i) for i in range(size)]})
    data.update(data.sample(frac=.5)["fname"]+".txt")
    for func in functions:
        diff = 0
        for _ in range(ITER):
            data_copy = data.copy()
            start = time.process_time()
            func(data_copy)
            diff += time.process_time() - start

        print(diff, end="\t")

输出绘制如下:

从图中可以看出,最慢的解决方案是正则表达式,最快的是带条件的 pandas.Series.map。在 pandas 的更高版本中,这可能会改变,我希望 pandas.Series.str.removesuffix 有所改进,因为它在矢量化方面具有更大的潜力。

Pandas 必须从 2021-11-30 开始从源安装,因为 1.4 版仅处于开发阶段。我按照 pandas dev repo 中的说明安装了它,方法是克隆项目并使用 python setup.py install.

安装

我的机器:

  • AMD Ryzen 5 2400G 配备 Radeon Vega 显卡,3.60 GHz
  • Windows 10 20H2
  • Python 3.10.0, pandas.版本 '1.4.0.dev0+1267.gaee662a7e3', numpy.版本 '1.21.4'