(плохой ответ)
Series.str.split
суп
df['str'] = df['str'].str.split('(').str[0].str.split('_').str[-1]
df
id str
0 1 d
1 2 d
2 3 e
3 4 b
(менее плохой ответ)
Series.str.extract
df['str'] = df['str'].str.extract(r'_([^_]+)\(', expand=False)
df
id str
0 1 d
1 2 d
2 3 e
3 4 b
Методы регулярных выражений идут со своей значительной долей накладных расходов, а str.extract
мало что делает для улучшения ситуации.
(лучший ответ)
re.search
со списком комп.
import re
p = re.compile(r'(?<=_)[^_]+(?=\()')
df['str'] = [p.search(x)[0] for x in df['str'].tolist()]
df
id str
0 1 d
1 2 d
2 3 e
3 4 b
Это должно быть быстрее, чем описанные выше методы.Я нахожу, что списочное понимание действительно быстрое по сравнению с большинством векторизованных строковых методов панд, даже если для этого используется регулярное выражение.Я предварительно скомпилировал шаблон заранее, чтобы устранить некоторые проблемы с производительностью.
(также лучший ответ)
str.split
со списком comp
df['str'] = [
x.split('(', 1)[0].split('_')[1] for x in df['str'].tolist()
]
df
id str
0 1 d
1 2 d
2 3 e
3 4 b
Это сочетает в себе лучшее из обоих миров, производительность компоновки списка и скорость чистого разделения строк в Python.Должно быть самым быстрым.
Производительность
df_test = pd.concat([df] * 10000, ignore_index=True)
%timeit df_test['str'].str.extract(r'_([^_]+)\(', expand=False)
%timeit df_test['str'].str.split('(').str[0].str.split('_').str[-1]
%timeit [p.search(x)[0] for x in df_test['str'].tolist()]
%timeit [x.split('(', 1)[0].split('_')[1] for x in df_test['str'].tolist()]
70.4 ms ± 623 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
99.6 ms ± 730 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
31 ms ± 877 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
30 ms ± 431 µs per loop (mean ± std. dev. of 7 runs, 10 loops each) # fastest but not by much