Як видалити небажані частини з рядків у стовпці?
Через 6 років після розміщення оригінального запитання в пандах тепер є велика кількість "векторизованих" рядкових функцій, які можуть лаконічно виконувати ці операції з маніпуляції з рядками.
Ця відповідь вивчить деякі з цих рядкових функцій, запропонує більш швидкі варіанти та перейде до порівняння таймінгів наприкінці.
Вкажіть підрядку / шаблон, що відповідає, та підрядку, на яку слід замінити.
pd.__version__
# '0.24.1'
df
time result
1 09:00 +52A
2 10:00 +62B
3 11:00 +44a
4 12:00 +30b
5 13:00 -110a
df['result'] = df['result'].str.replace(r'\D', '')
df
time result
1 09:00 52
2 10:00 62
3 11:00 44
4 12:00 30
5 13:00 110
Якщо вам потрібен результат, перетворений на ціле число, ви можете використовувати Series.astype
,
df['result'] = df['result'].str.replace(r'\D', '').astype(int)
df.dtypes
time object
result int64
dtype: object
Якщо ви не хочете змінювати df
на місці, скористайтеся DataFrame.assign
:
df2 = df.assign(result=df['result'].str.replace(r'\D', ''))
df
# Unchanged
Корисно для вилучення підрядів, які ви хочете зберегти.
df['result'] = df['result'].str.extract(r'(\d+)', expand=False)
df
time result
1 09:00 52
2 10:00 62
3 11:00 44
4 12:00 30
5 13:00 110
З extract
, необхідно вказати принаймні одну групу захоплення. expand=False
поверне Серію із захопленими предметами з першої групи захоплення.
Розбиття творів, якщо всі ваші рядки дотримуються цієї послідовної структури.
# df['result'] = df['result'].str.split(r'\D').str[1]
df['result'] = df['result'].str.split(r'\D').str.get(1)
df
time result
1 09:00 52
2 10:00 62
3 11:00 44
4 12:00 30
5 13:00 110
Не рекомендую, якщо ви шукаєте загальне рішення.
Якщо вас влаштовують лаконічні та читані str
рішення, що базуються на аксесуарах вище, ви можете зупинитися тут. Однак якщо вас цікавлять швидші, ефективніші альтернативи, продовжуйте читати.
Оптимізація: розуміння списку
За деяких обставин розуміння списків слід надавати перевагу функціям рядків pandas. Причина полягає в тому, що рядкові функції за своєю суттю важко векторизувати (у справжньому розумінні цього слова), тому більшість функцій рядків і регулярних виразів є лише обгортками навколо циклів з більшою накладними витратами.
Моє записування: Чи справді циклі у пандах погані? Коли я повинен піклуватися? , детальніше.
str.replace
Опція може бути переписана з використаннямre.sub
import re
# Pre-compile your regex pattern for more performance.
p = re.compile(r'\D')
df['result'] = [p.sub('', x) for x in df['result']]
df
time result
1 09:00 52
2 10:00 62
3 11:00 44
4 12:00 30
5 13:00 110
str.extract
Приклад може бути переписаний з використанням списку розуміння з re.search
,
p = re.compile(r'\d+')
df['result'] = [p.search(x)[0] for x in df['result']]
df
time result
1 09:00 52
2 10:00 62
3 11:00 44
4 12:00 30
5 13:00 110
Якщо NaNs або не збіг є можливістю, вам потрібно буде переписати вище, щоб включити деяку перевірку помилок. Я роблю це за допомогою функції.
def try_extract(pattern, string):
try:
m = pattern.search(string)
return m.group(0)
except (TypeError, ValueError, AttributeError):
return np.nan
p = re.compile(r'\d+')
df['result'] = [try_extract(p, x) for x in df['result']]
df
time result
1 09:00 52
2 10:00 62
3 11:00 44
4 12:00 30
5 13:00 110
Ми також можемо переписати відповіді @ eumiro та @ MonkeyButter, використовуючи розуміння списку:
df['result'] = [x.lstrip('+-').rstrip('aAbBcC') for x in df['result']]
І,
df['result'] = [x[1:-1] for x in df['result']]
Діють ті самі правила поводження з NaN та ін.
Порівняння продуктивності
Графіки, згенеровані за допомогою perfplot . Повний список коду, для довідки. Відповідні функції перераховані нижче.
Деякі з цих порівнянь є несправедливими, оскільки вони користуються структурою даних ОП, але беруть з цього те, що ви хочете. Варто зазначити, що кожна функція розуміння списку є або швидшою, або порівнянною, ніж її еквівалентний варіант панди.
Функції
def eumiro(df):
return df.assign(
result=df['result'].map(lambda x: x.lstrip('+-').rstrip('aAbBcC')))
def coder375(df):
return df.assign(
result=df['result'].replace(r'\D', r'', regex=True))
def monkeybutter(df):
return df.assign(result=df['result'].map(lambda x: x[1:-1]))
def wes(df):
return df.assign(result=df['result'].str.lstrip('+-').str.rstrip('aAbBcC'))
def cs1(df):
return df.assign(result=df['result'].str.replace(r'\D', ''))
def cs2_ted(df):
# `str.extract` based solution, similar to @Ted Petrou's. so timing together.
return df.assign(result=df['result'].str.extract(r'(\d+)', expand=False))
def cs1_listcomp(df):
return df.assign(result=[p1.sub('', x) for x in df['result']])
def cs2_listcomp(df):
return df.assign(result=[p2.search(x)[0] for x in df['result']])
def cs_eumiro_listcomp(df):
return df.assign(
result=[x.lstrip('+-').rstrip('aAbBcC') for x in df['result']])
def cs_mb_listcomp(df):
return df.assign(result=[x[1:-1] for x in df['result']])