Loại bỏ các phần không mong muốn khỏi chuỗi trong một cột


129

Tôi đang tìm kiếm một cách hiệu quả để loại bỏ các phần không mong muốn khỏi các chuỗi trong cột DataFrame.

Dữ liệu trông như:

    time    result
1    09:00   +52A
2    10:00   +62B
3    11:00   +44a
4    12:00   +30b
5    13:00   -110a

Tôi cần phải cắt những dữ liệu này để:

    time    result
1    09:00   52
2    10:00   62
3    11:00   44
4    12:00   30
5    13:00   110

Tôi đã thử .str.lstrip('+-')và. str.rstrip('aAbBcC'), nhưng có một lỗi:

TypeError: wrapper() takes exactly 1 argument (2 given)

Bât cư thông tin được cung câp nao cung được la sự suât hiện tuyệt vơi!

Câu trả lời:


167
data['result'] = data['result'].map(lambda x: x.lstrip('+-').rstrip('aAbBcC'))

cám ơn! mà làm việc Tôi vẫn đang bận tâm về bản đồ (), không biết khi nào nên sử dụng hay không sử dụng nó ...
Yannan Wang

Tôi hài lòng khi thấy rằng phương pháp này cũng hoạt động với chức năng thay thế.
BKay

@eumiro làm thế nào để bạn áp dụng kết quả này nếu lặp lại từng cột?
medev21

Tôi có thể sử dụng chức năng này để thay thế một số như số 12 không? Nếu tôi làm x.lstrip ('12 '), nó sẽ mất tất cả 1 và 2 giây.
Dave

76

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.


.str.replace

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 dftại chỗ, hãy sử dụng DataFrame.assign:

df2 = df.assign(result=df['result'].str.replace(r'\D', ''))
df
# Unchanged

.str.extract

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=Falsesẽ trả về Sê-ri với các vật phẩm bị bắt từ nhóm chụp đầu tiên.


.str.split.str.get

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.replacetù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.extractví 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

nhập mô tả hình ảnh ở đây

Đồ 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']])

bất kỳ cách giải quyết nào để tránh việc thiết lập nội dung:Try using .loc[row_indexer,col_indexer] = value instead
PV8

@ PV8 không chắc chắn về mã của bạn, nhưng hãy kiểm tra điều này: stackoverflow.com/questions/20625582/ Lời
cs95

Đối với bất kỳ ai mới biết đến REGEX như tôi, \ D cũng giống như [^ \ d] (bất cứ thứ gì không phải là chữ số) từ đây . Vì vậy, về cơ bản chúng ta thay thế tất cả các chữ số không có trong chuỗi bằng không có gì.
Rishi Latchmepersad

56

Tôi sẽ sử dụng chức năng thay thế gấu trúc, rất đơn giản và mạnh mẽ vì bạn có thể sử dụng regex. Bên dưới tôi đang sử dụng regex \ D để xóa bất kỳ ký tự không có chữ số nào nhưng rõ ràng bạn có thể khá sáng tạo với regex.

data['result'].replace(regex=True,inplace=True,to_replace=r'\D',value=r'')

Tôi đã thử điều này, và nó không hoạt động. Tôi tự hỏi nếu nó chỉ hoạt động khi bạn muốn thay thế toàn bộ một chuỗi thay vì chỉ thay thế một phần chuỗi con.
bgenchel

@bgenchel - Tôi đã sử dụng phương pháp này để thay thế một phần của chuỗi trong pd.Series : df.loc[:, 'column_a'].replace(regex=True, to_replace="my_prefix", value="new_prefix"). Điều này sẽ chuyển đổi một chuỗi như "my_prefixaaa" thành "new_prefixaaa".
jakub

r làm gì trong to numplace = r '\ D'?
Luca Guarro

@LucaGuarro từ các tài liệu python: "Tiền tố r, làm cho nghĩa đen là một chuỗi ký tự thô, là cần thiết trong ví dụ này bởi vì các chuỗi thoát trong một chuỗi ký tự chuỗi nấu chín bình thường mà Python không nhận ra, trái ngược với các biểu thức thông thường, bây giờ dẫn đến Khấu haoWarning và cuối cùng sẽ trở thành SyntaxError. "
Coder375

35

Trong trường hợp cụ thể khi bạn biết số lượng vị trí bạn muốn xóa khỏi cột khung dữ liệu, bạn có thể sử dụng lập chỉ mục chuỗi bên trong hàm lambda để loại bỏ các phần đó:

Nhân vật cuối cùng:

data['result'] = data['result'].map(lambda x: str(x)[:-1])

Hai nhân vật đầu tiên:

data['result'] = data['result'].map(lambda x: str(x)[2:])

Tôi cần cắt tọa độ địa lý thành 8 ký tự (bao gồm (.), (-)) và trong trường hợp nếu chúng nhỏ hơn 8, tôi cần chèn '0' cuối cùng để tạo tất cả 8 tọa độ. Cách đơn giản hơn để làm như vậy là gì?
Sitz Blogz

Tôi không hiểu đầy đủ vấn đề của bạn nhưng bạn có thể cần thay đổi chức năng lambda thành một cái gì đó như "{0: .8f}". Format (x)
prl900

Cảm ơn bạn rất nhiều vì đã trả lời. Nói một cách đơn giản, tôi có khung dữ liệu với tọa độ địa lý - vĩ độ và kinh độ là hai cột. Độ dài của các ký tự là hơn 8 ký tự và tôi chỉ giữ 8 ký tự bắt đầu từ đầu tiên bao gồm (-) và (.).
Sitz Blogz

18

Có một lỗi ở đây: hiện không thể chuyển đối số đến str.lstripstr.rstrip:

http://github.com/pydata/pandas/issues/2411

EDIT: 2012-12-07 hiện tại nó hoạt động trên nhánh dev:

In [8]: df['result'].str.lstrip('+-').str.rstrip('aAbBcC')
Out[8]: 
1     52
2     62
3     44
4     30
5    110
Name: result

11

Một phương pháp rất đơn giản sẽ là sử dụng extractphương thức để chọn tất cả các chữ số. Đơn giản chỉ cần cung cấp cho nó biểu thức chính quy '\d+'trích ra bất kỳ số chữ số nào.

df['result'] = df.result.str.extract(r'(\d+)', expand=True).astype(int)
df

    time  result
1  09:00      52
2  10:00      62
3  11:00      44
4  12:00      30
5  13:00     110

7

Tôi thường sử dụng khả năng hiểu danh sách cho các loại nhiệm vụ này vì chúng thường nhanh hơn.

Có thể có sự khác biệt lớn về hiệu suất giữa các phương thức khác nhau để thực hiện những việc như thế này (nghĩa là sửa đổi mọi thành phần của chuỗi trong DataFrame). Thường thì việc hiểu danh sách có thể nhanh nhất - xem cuộc đua mã dưới đây cho nhiệm vụ này:

import pandas as pd
#Map
data = pd.DataFrame({'time':['09:00','10:00','11:00','12:00','13:00'], 'result':['+52A','+62B','+44a','+30b','-110a']})
%timeit data['result'] = data['result'].map(lambda x: x.lstrip('+-').rstrip('aAbBcC'))
10000 loops, best of 3: 187 µs per loop
#List comprehension
data = pd.DataFrame({'time':['09:00','10:00','11:00','12:00','13:00'], 'result':['+52A','+62B','+44a','+30b','-110a']})
%timeit data['result'] = [x.lstrip('+-').rstrip('aAbBcC') for x in data['result']]
10000 loops, best of 3: 117 µs per loop
#.str
data = pd.DataFrame({'time':['09:00','10:00','11:00','12:00','13:00'], 'result':['+52A','+62B','+44a','+30b','-110a']})
%timeit data['result'] = data['result'].str.lstrip('+-').str.rstrip('aAbBcC')
1000 loops, best of 3: 336 µs per loop

4

Giả sử DF của bạn cũng có các ký tự phụ ở giữa các số. Mục cuối cùng.

  result   time
0   +52A  09:00
1   +62B  10:00
2   +44a  11:00
3   +30b  12:00
4  -110a  13:00
5   3+b0  14:00

Bạn có thể thử str.replace để xóa các ký tự không chỉ từ đầu và cuối mà còn ở giữa.

DF['result'] = DF['result'].str.replace('\+|a|b|\-|A|B', '')

Đầu ra:

  result   time
0     52  09:00
1     62  10:00
2     44  11:00
3     30  12:00
4    110  13:00
5     30  14:00

0

Hãy thử điều này bằng cách sử dụng biểu thức thông thường:

import re
data['result'] = data['result'].map(lambda x: re.sub('[-+A-Za-z]',x)
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.