Làm cách nào để loại bỏ các phần không mong muốn khỏi chuỗi trong một cột?
6 năm sau khi câu hỏi ban đầu được đăng, gấu trúc hiện có một số lượng lớn các hàm chuỗi "vectơ" có thể thực hiện ngắn gọn các hoạt động thao tác chuỗi này.
Câu trả lời này sẽ khám phá một số hàm chuỗi này, đề xuất các lựa chọn thay thế nhanh hơn và đi vào so sánh thời gian ở cuối.
Chỉ định chuỗi con / mẫu phù hợp và chuỗi con để thay thế nó.
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
Nếu bạn cần kết quả được chuyển đổi thành một số nguyên, bạn có thể sử dụng Series.astype
,
df['result'] = df['result'].str.replace(r'\D', '').astype(int)
df.dtypes
time object
result int64
dtype: object
Nếu bạn không muốn sửa đổi df
tại chỗ, hãy sử dụng DataFrame.assign
:
df2 = df.assign(result=df['result'].str.replace(r'\D', ''))
df
# Unchanged
Hữu ích để trích xuất (các) chuỗi con bạn muốn giữ.
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
Với extract
, cần phải chỉ định ít nhất một nhóm chụp. expand=False
sẽ trả về Sê-ri với các vật phẩm bị bắt từ nhóm chụp đầu tiên.
Chia tách công việc giả định tất cả các chuỗi của bạn theo cấu trúc nhất quán này.
# 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
Không khuyến nghị nếu bạn đang tìm kiếm một giải pháp chung.
Nếu bạn hài lòng với các str
giải pháp dựa trên truy cập ngắn gọn và dễ đọc ở trên, bạn có thể dừng ở đây. Tuy nhiên, nếu bạn quan tâm đến các lựa chọn thay thế nhanh hơn, hiệu quả hơn, hãy tiếp tục đọc.
Tối ưu hóa: Danh sách toàn diện
Trong một số trường hợp, việc hiểu danh sách nên được ưu tiên hơn các hàm chuỗi gấu trúc. Lý do là bởi vì các hàm chuỗi vốn đã khó để vector hóa (theo nghĩa thực sự của từ này), vì vậy hầu hết các hàm chuỗi và regex chỉ là các hàm bao quanh các vòng lặp có nhiều chi phí hơn.
Bài viết của tôi, vòng lặp for trong gấu trúc có thực sự xấu không? Khi nào tôi nên quan tâm? , đi vào chi tiết hơn.
Các str.replace
tùy chọn có thể được tái sử dụng bằng văn bảnre.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
Các str.extract
ví dụ có thể được viết lại bằng một sự hiểu biết danh sách với 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
Nếu NaN hoặc không khớp là một khả năng, bạn sẽ cần phải viết lại phần trên để bao gồm một số kiểm tra lỗi. Tôi làm điều này bằng cách sử dụng một chức năng.
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
Chúng tôi cũng có thể viết lại câu trả lời của @ eumiro và @ MonkeyButter bằng cách hiểu danh sách:
df['result'] = [x.lstrip('+-').rstrip('aAbBcC') for x in df['result']]
Và,
df['result'] = [x[1:-1] for x in df['result']]
Áp dụng quy tắc tương tự để xử lý NaN, v.v.
So sánh hiệu suất
Đồ thị được tạo bằng perfplot . Danh sách mã đầy đủ, để bạn tham khảo. Các chức năng có liên quan được liệt kê dưới đây.
Một số so sánh này không công bằng vì chúng tận dụng cấu trúc dữ liệu của OP, nhưng lấy từ đó những gì bạn muốn. Một điều cần lưu ý là mọi chức năng hiểu danh sách đều nhanh hơn hoặc có thể so sánh với biến thể gấu trúc tương đương của nó.
Chức năng
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']])