ลบส่วนที่ไม่ต้องการออกจากสตริงในคอลัมน์


129

ฉันกำลังมองหาวิธีที่มีประสิทธิภาพในการลบส่วนที่ไม่ต้องการออกจากสตริงในคอลัมน์ DataFrame

ข้อมูลดูเหมือนว่า:

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

ฉันต้องการตัดข้อมูลเหล่านี้เพื่อ:

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

ฉันพยายาม.str.lstrip('+-')และ. str.rstrip('aAbBcC')แต่มีข้อผิดพลาด:

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

คำแนะนำใด ๆ จะได้รับการชื่นชมอย่างมาก!

คำตอบ:


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

ขอบคุณ! ที่ได้ผล ฉันยังคงคิดเกี่ยวกับแผนที่ () ไม่แน่ใจว่าจะใช้หรือไม่ใช้เมื่อใด ...
Yannan Wang

ฉันยินดีที่เห็นว่าวิธีนี้ใช้ได้กับฟังก์ชันแทนที่ด้วย
BKay

@eumiro คุณจะใช้ผลลัพธ์นี้อย่างไรหากทำซ้ำในแต่ละคอลัมน์
medev21

ฉันสามารถใช้ฟังก์ชันนี้เพื่อแทนที่ตัวเลขเช่นหมายเลข 12 ได้หรือไม่? ถ้าฉันทำ x.lstrip ('12 ') มันจะเอา 1 และ 2 ออกทั้งหมด
Dave

78

ฉันจะลบส่วนที่ไม่ต้องการออกจากสตริงในคอลัมน์ได้อย่างไร

6 ปีหลังจากโพสต์คำถามเดิมตอนนี้หมีแพนด้ามีฟังก์ชันสตริง "vectorised" จำนวนมากที่สามารถดำเนินการจัดการสตริงเหล่านี้ได้อย่างรวบรัด

คำตอบนี้จะสำรวจฟังก์ชันสตริงเหล่านี้แนะนำทางเลือกอื่นที่เร็วกว่าและเข้าสู่การเปรียบเทียบการกำหนดเวลาในตอนท้าย


.str.replace

ระบุสตริงย่อย / รูปแบบที่จะจับคู่และสตริงย่อยที่จะแทนที่ด้วย

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

.str.extract

มีประโยชน์สำหรับการแยกสตริงย่อยที่คุณต้องการเก็บไว้

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จะส่งคืนซีรี่ส์พร้อมรายการที่จับได้จากกลุ่มการจับแรก


.str.split และ .str.get

การแยกทำงานโดยสมมติว่าสตริงทั้งหมดของคุณเป็นไปตามโครงสร้างที่สอดคล้องกันนี้

# 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 โซลูชันที่ใช้ตัวช่วยที่กระชับและอ่านได้ข้างต้นคุณสามารถหยุดได้ที่นี่ อย่างไรก็ตามหากคุณสนใจทางเลือกที่รวดเร็วและมีประสิทธิภาพมากขึ้นโปรดอ่านต่อไป


การเพิ่มประสิทธิภาพ: รายการความเข้าใจ

ในบางสถานการณ์ควรใช้ความเข้าใจในรายการมากกว่าฟังก์ชันสตริงแพนด้า สาเหตุเนื่องมาจากฟังก์ชันสตริงนั้นยากที่จะทำให้เป็นเวกเตอร์ (ตามความหมายที่แท้จริงของคำ) ดังนั้นฟังก์ชันสตริงและนิพจน์ทั่วไปส่วนใหญ่จึงเป็นเพียงตัวตัดรอบลูปที่มีค่าโสหุ้ยมากกว่า

การเขียนของฉันฟอร์ลูปในแพนด้าแย่จริงหรือ? ควรดูแลเมื่อใด ลงรายละเอียดมากขึ้น

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 ฯลฯ


การเปรียบเทียบประสิทธิภาพ

ใส่คำอธิบายภาพที่นี่

กราฟที่สร้างโดยใช้เพอร์พล็อรายการรหัสเต็มสำหรับการอ้างอิงของคุณ ฟังก์ชันที่เกี่ยวข้องแสดงอยู่ด้านล่าง

การเปรียบเทียบเหล่านี้บางส่วนไม่ยุติธรรมเนื่องจากใช้ประโยชน์จากโครงสร้างของข้อมูลของ OP แต่ใช้ประโยชน์จากสิ่งที่คุณต้องการ สิ่งหนึ่งที่ควรทราบก็คือฟังก์ชันการทำความเข้าใจรายการทั้งหมดจะเร็วกว่าหรือเทียบได้กว่าตัวแปรแพนด้าที่เทียบเท่ากัน

ฟังก์ชั่น

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

วิธีแก้ปัญหาใด ๆ เพื่อหลีกเลี่ยงการตั้งค่าพร้อมสำเนาคำเตือน:Try using .loc[row_indexer,col_indexer] = value instead
PV8

@ PV8 ไม่แน่ใจเกี่ยวกับรหัสของคุณ แต่ลองดูที่: stackoverflow.com/questions/20625582/…
cs95

สำหรับทุกคนที่ใหม่ในการทั่วไป REGEX เช่นฉัน \ D เป็นเช่นเดียวกับ [^ \ d] (อะไรที่ไม่หลัก) จากที่นี่ ดังนั้นโดยพื้นฐานแล้วเราจะแทนที่ตัวเลขที่ไม่ใช่ตัวเลขทั้งหมดในสตริงโดยไม่มีอะไรเลย
Rishi Latchmepersad

56

ฉันจะใช้ฟังก์ชั่นแทนที่ของแพนด้าซึ่งเรียบง่ายและทรงพลังมากเพราะคุณสามารถใช้ regex ได้ ด้านล่างนี้ฉันใช้ regex \ D เพื่อลบอักขระที่ไม่ใช่ตัวเลขออก แต่เห็นได้ชัดว่าคุณสามารถใช้ regex ได้อย่างสร้างสรรค์

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

ฉันลองแล้วมันไม่ได้ผล ฉันสงสัยว่ามันใช้งานได้เฉพาะเมื่อคุณต้องการแทนที่สตริงทั้งหมดแทนที่จะเปลี่ยนเพียงส่วนสตริงย่อย
bgenchel

@bgenchel - ฉันใช้วิธีนี้เพื่อแทนที่ส่วนหนึ่งของสตริงใน pd ซีรีส์: df.loc[:, 'column_a'].replace(regex=True, to_replace="my_prefix", value="new_prefix"). สิ่งนี้จะแปลงสตริงเช่น "my_prefixaaa" เป็น "new_prefixaaa"
jakub

r ทำอะไรใน to_replace = r '\ D'?
Luca Guarro

@LucaGuarro จาก python docs: "คำนำหน้า r ซึ่งทำให้ลิเทอรัลเป็นลิเทอรัลสตริงดิบเป็นสิ่งที่จำเป็นในตัวอย่างนี้เนื่องจากลำดับการหลีกเลี่ยงในลิเทอรัลสตริง" สุก "ปกติที่ Python ไม่รู้จักเมื่อเทียบกับนิพจน์ทั่วไปในตอนนี้ ส่งผลให้เกิด DeprecationWarning และจะกลายเป็น SyntaxError ในที่สุด "
Coder375

35

ในกรณีเฉพาะที่คุณทราบจำนวนตำแหน่งที่คุณต้องการลบออกจากคอลัมน์ดาต้าเฟรมคุณสามารถใช้การจัดทำดัชนีสตริงภายในฟังก์ชันแลมบ์ดาเพื่อกำจัดส่วนนั้นได้:

อักขระสุดท้าย:

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

อักขระสองตัวแรก:

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

ฉันต้องการตัดพิกัดทางภูมิศาสตร์ให้เหลือ 8 ตัวอักษร (รวมถึง (.), (-)) และในกรณีที่มีค่าน้อยกว่า 8 ฉันต้องแทรก '0' ในที่สุดเพื่อสร้างพิกัดทั้งหมด 8 อักขระ วิธีที่ง่ายกว่านั้นคืออะไร?
Sitz Blogz

ฉันไม่เข้าใจปัญหาของคุณทั้งหมด แต่คุณอาจต้องเปลี่ยนฟังก์ชันแลมด้าเป็น "{0: .8f}". format (x)
prl900

ขอบคุณมากสำหรับการตอบกลับ พูดง่ายๆคือฉันมี dataframe ที่มีพิกัดทางภูมิศาสตร์ - ละติจูดและลองจิจูดเป็นสองคอลัมน์ ความยาวอักขระมากกว่า 8 อักขระและฉันเก็บไว้เพียง 8 อักขระโดยเริ่มจากตัวแรกซึ่งควรมี (-) และ (.) ด้วย
Sitz Blogz

18

มีข้อบกพร่องที่นี่: ขณะนี้ไม่สามารถส่งผ่านข้อโต้แย้งไปยังstr.lstripและstr.rstrip:

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

แก้ไข: 2012-12-07 สิ่งนี้ใช้งานได้ในสาขา 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

วิธีที่ง่ายมากคือใช้extractวิธีการเลือกตัวเลขทั้งหมด เพียงแค่ใส่นิพจน์ทั่วไป'\d+'ซึ่งแยกจำนวนหลักใด ๆ

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

ฉันมักใช้ความเข้าใจรายการสำหรับงานประเภทนี้เพราะมักจะเร็วกว่า

ประสิทธิภาพอาจมีความแตกต่างกันมากระหว่างวิธีการต่างๆในการทำสิ่งต่างๆเช่นนี้ (เช่นการปรับเปลี่ยนทุกองค์ประกอบของชุดข้อมูลภายใน DataFrame) บ่อยครั้งที่การทำความเข้าใจรายการสามารถทำได้เร็วที่สุด - ดูการแข่งขันรหัสด้านล่างสำหรับงานนี้:

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

สมมติว่า DF ของคุณมีอักขระพิเศษเหล่านั้นอยู่ระหว่างตัวเลขเช่นกันรายการสุดท้าย

  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

คุณสามารถลองใช้ str.replace เพื่อลบอักขระไม่เพียง แต่จากจุดเริ่มต้นและจุดสิ้นสุด แต่ยังรวมถึงระหว่างระหว่าง

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

เอาท์พุท:

  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

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.