ฉันจะลบส่วนที่ไม่ต้องการออกจากสตริงในคอลัมน์ได้อย่างไร
6 ปีหลังจากโพสต์คำถามเดิมตอนนี้หมีแพนด้ามีฟังก์ชันสตริง "vectorised" จำนวนมากที่สามารถดำเนินการจัดการสตริงเหล่านี้ได้อย่างรวบรัด
คำตอบนี้จะสำรวจฟังก์ชันสตริงเหล่านี้แนะนำทางเลือกอื่นที่เร็วกว่าและเข้าสู่การเปรียบเทียบการกำหนดเวลาในตอนท้าย
ระบุสตริงย่อย / รูปแบบที่จะจับคู่และสตริงย่อยที่จะแทนที่ด้วย
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
โซลูชันที่ใช้ตัวช่วยที่กระชับและอ่านได้ข้างต้นคุณสามารถหยุดได้ที่นี่ อย่างไรก็ตามหากคุณสนใจทางเลือกที่รวดเร็วและมีประสิทธิภาพมากขึ้นโปรดอ่านต่อไป
การเพิ่มประสิทธิภาพ: รายการความเข้าใจ
ในบางสถานการณ์ควรใช้ความเข้าใจในรายการมากกว่าฟังก์ชันสตริงแพนด้า สาเหตุเนื่องมาจากฟังก์ชันสตริงนั้นยากที่จะทำให้เป็นเวกเตอร์ (ตามความหมายที่แท้จริงของคำ) ดังนั้นฟังก์ชันสตริงและนิพจน์ทั่วไปส่วนใหญ่จึงเป็นเพียงตัวตัดรอบลูปที่มีค่าโสหุ้ยมากกว่า
การเขียนของฉันฟอร์ลูปในแพนด้าแย่จริงหรือ? ควรดูแลเมื่อใด ลงรายละเอียดมากขึ้น
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']])