ลบค่า None ออกจากรายการโดยไม่ลบค่า 0


244

นี่คือแหล่งที่มาของฉันที่ฉันเริ่มต้นด้วย

รายการของฉัน

L = [0, 23, 234, 89, None, 0, 35, 9]

เมื่อฉันเรียกใช้สิ่งนี้:

L = filter(None, L)

ฉันได้รับผลลัพธ์นี้

[23, 234, 89, 35, 9]

แต่นี่ไม่ใช่สิ่งที่ฉันต้องการสิ่งที่ฉันต้องการจริงๆคือ:

[0, 23, 234, 89, 0, 35, 9]

เพราะฉันคำนวณเปอร์เซ็นต์ไทล์ของข้อมูลและ 0 สร้างความแตกต่างอย่างมาก

วิธีการลบค่าไม่มีออกจากรายการโดยไม่ลบค่า 0 ออก

คำตอบ:


354
>>> L = [0, 23, 234, 89, None, 0, 35, 9]
>>> [x for x in L if x is not None]
[0, 23, 234, 89, 0, 35, 9]

เพื่อความสนุกนี่คือวิธีที่คุณสามารถปรับตัวให้เข้าfilterกับการทำสิ่งนี้ได้โดยไม่ต้องใช้lambda(ฉันจะไม่แนะนำรหัสนี้ - เป็นเพียงเพื่อวัตถุประสงค์ทางวิทยาศาสตร์)

>>> from operator import is_not
>>> from functools import partial
>>> L = [0, 23, 234, 89, None, 0, 35, 9]
>>> filter(partial(is_not, None), L)
[0, 23, 234, 89, 0, 35, 9]

23
filterรุ่นที่หรูหราน้อยกว่า: filter(lambda x: x is not None, L)- คุณสามารถกำจัดการlambdaใช้งานpartialและoperator.is_notฉันคิดว่า แต่อาจไม่คุ้มค่าเนื่องจาก list-comp สะอาดกว่ามาก
mgilson

3
@ mgilson โอ้ว้าวฉันไม่รู้ด้วยซ้ำว่าis_notมีอยู่จริง! ฉันคิดว่ามันเป็นอย่างis_นั้นฉันจะเพิ่มมันเพื่อความสนุก
jamylak

@jamylak - ใช่ จริงๆแล้วมันรบกวนจิตใจฉันที่is_notมีอยู่และnot_inไม่มีอยู่จริง จริง ๆ แล้วฉันคิดว่ามันnot_inควรจะกลายเป็นวิธีการทางเวทมนตร์__not_contains__... ดูคำถามที่ฉันถามในขณะที่กลับมาและความคิดเห็นที่ฉันทำกับผู้ตอบ ... และยังไม่รู้สึกเหมือนมันได้รับการแก้ไข
mgilson

@ mgilson ฉันคิดว่าภายใต้สมมติฐานเดียวกันนั้นฉันแค่คิดว่ามันไม่มีอยู่จริง ฉันเดาว่าคุณสามารถใช้filterfalseหรือบางอย่างขึ้นอยู่กับกรณีการใช้งาน
jamylak

@jamylak - ใช่ ปัญหาหลักของฉันที่x > yไม่ได้หมายความถึงnot x <= yในหลามเพราะคุณสามารถทำอะไรใน__lt__และ__le__ดังนั้นจึงควรจะx not in yบ่งบอกถึงnot x in y(โดยเฉพาะตั้งแต่not inมี bytecode ของตัวเอง?)
mgilson

136

FWIW, Python 3 ทำให้ปัญหานี้ง่ายขึ้น:

>>> L = [0, 23, 234, 89, None, 0, 35, 9]
>>> list(filter(None.__ne__, L))
[0, 23, 234, 89, 0, 35, 9]

ใน Python 2 คุณจะใช้ list comprehension แทน:

>>> [x for x in L if x is not None]
[0, 23, 234, 89, 0, 35, 9]

+1 คุณแนะนำให้ใช้__ne__แบบนั้นซึ่งตรงข้ามกับpartialและne?
jamylak

1
@jamylak ใช่เร็วกว่าเขียนได้ง่ายขึ้นและชัดเจนขึ้นอีกนิด
Raymond Hettinger

พิจารณาใช้operatorโมดูล
rightfold

12
คือ__ne__อะไร
DrMcCleod

11
@DrMcCleod นิพจน์x != yเรียกภายในx.__ne__(y)โดยที่neหมายถึง "ไม่เท่ากัน" ดังนั้นNone.__ne__เป็นวิธีการที่ถูกผูกไว้ที่ส่งกลับTrueเมื่อเรียกว่ามีค่าอื่น ๆ กว่าไม่มี ยกตัวอย่างเช่นbm = None.__ne__เรียกว่ามีbm(10)ผลตอบแทนNotImplementedซึ่งเป็นมูลค่าที่แท้จริงและbm(None)ผลตอบแทนที่เป็นเท็จ
Raymond Hettinger

17

การใช้ความเข้าใจในรายการสามารถทำได้ดังนี้:

l = [i for i in my_list if i is not None]

ค่าของ l คือ:

[0, 23, 234, 89, 0, 35, 9]

วิธีแก้ปัญหานี้พบได้ในคำตอบยอดนิยมหรือฉันขาดอะไรไป?
Qaswed

16

สำหรับ Python 2.7 (ดูคำตอบของ Raymond สำหรับ Python 3 ที่เทียบเท่า):

ต้องการทราบว่าบางสิ่ง "ไม่ใช่ไม่ใช่" เป็นเรื่องปกติใน python (และภาษา OO อื่น ๆ ), ใน Common.py ของฉัน (ซึ่งฉันนำเข้าไปยังแต่ละโมดูลด้วย "จาก Common import *"), ฉันรวมบรรทัดเหล่านี้:

def exists(it):
    return (it is not None)

จากนั้นเมื่อต้องการลบองค์ประกอบไม่มีออกจากรายการเพียงทำ:

filter(exists, L)

ฉันพบว่ามันอ่านง่ายกว่าความเข้าใจในรายการที่เกี่ยวข้อง (ซึ่ง Raymond แสดงเป็นเวอร์ชัน Python 2 ของเขา)


ฉันต้องการโซลูชัน Raymonds สำหรับ Python 3 และจากนั้นรายการความเข้าใจสำหรับ Python 2 แต่ถ้าฉันต้องไปเส้นทางนี้ฉันจะดีpartial(is_not, None)กว่าโซลูชันนี้ ฉันเชื่อว่าสิ่งนี้จะช้าลง (แม้ว่าจะไม่สำคัญเกินไป) แต่ด้วยการนำเข้าโมดูลหลามสองสามไม่จำเป็นต้องมีฟังก์ชั่นที่กำหนดเองในกรณีนี้
jamylak

12

คำตอบ @jamylak เป็นสิ่งที่ดีมาก แต่ถ้าคุณไม่ต้องการที่จะนำเข้าคู่ของโมดูลเพียงเพื่อทำงานที่ง่ายนี้เขียนของคุณเองlambdaในสถานที่:

>>> L = [0, 23, 234, 89, None, 0, 35, 9]
>>> filter(lambda v: v is not None, L)
[0, 23, 234, 89, 0, 35, 9]

เห็นได้ชัดว่าคุณไม่ได้อ่านโซลูชันของฉันอย่างถูกต้องซึ่งเป็น[x for x in L if x is not None]รหัสอื่นเป็นเพียงการเพิ่มฉันระบุไว้อย่างชัดเจนฉันจะไม่แนะนำ
jamylak

1
@jamylak - ฉันอ่านแล้ว แต่คุณยังไม่ได้รวมโซลูชันนี้ - ยังไม่แน่ใจว่าทำไมคุณแก้ไขคำตอบของผู้คนเมื่อ 4-5 ปีก่อน
AT

5

Iteration vs Spaceการใช้งานอาจเป็นปัญหา ในสถานการณ์ต่างๆการทำโปรไฟล์อาจแสดงว่า "เร็วกว่า" และ / หรือ "ใช้หน่วยความจำน้อย" แบบเข้มข้น

# first
>>> L = [0, 23, 234, 89, None, 0, 35, 9, ...]
>>> [x for x in L if x is not None]
[0, 23, 234, 89, 0, 35, 9, ...]

# second
>>> L = [0, 23, 234, 89, None, 0, 35, 9]
>>> for i in range(L.count(None)): L.remove(None)
[0, 23, 234, 89, 0, 35, 9, ...]

แรกวิธีการ (ในขณะที่ยังมีข้อเสนอแนะโดย@jamylak , @Raymond Hettingerและ@Dipto ) สร้างรายชื่อที่ซ้ำกันในหน่วยความจำซึ่งอาจจะเป็นค่าใช้จ่ายสำหรับรายการใหญ่ที่มีไม่กี่Noneรายการ

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

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

การเลือกวิธีการอย่างใดอย่างหนึ่งอาจไม่สำคัญในสถานการณ์ทั่วไป มันกลายเป็นความพึงพอใจของสัญกรณ์มากกว่า ในความเป็นจริงในสถานการณ์ที่ผิดปกติเหล่านั้นnumpyหรือcythonอาจเป็นทางเลือกที่คุ้มค่าแทนการพยายามเพิ่มประสิทธิภาพ Prom ของ micromanage


ไม่ใช่แฟนของเรื่องนี้ข้อดีทั้งหมดที่คุณอ้างสิทธิ์ด้วยโซลูชันนี้คือรายการอาจมีขนาดใหญ่มากจนการสร้างรายการที่ซ้ำกันในหน่วยความจำอาจมีค่าใช้จ่ายสูง วิธีแก้ปัญหาของคุณจะมีค่าใช้จ่ายสูงขึ้นเนื่องจากคุณกำลังสแกนรายการทั้งหมดL.count(None)และคุณกำลังเรียก.remove(None)หลายครั้งซึ่งทำให้สิ่งนี้O(N^2)สถานการณ์ที่คุณพยายามแก้ไขไม่ควรได้รับการจัดการด้วยวิธีนี้ข้อมูลควรได้รับการปรับโครงสร้างใหม่ ลงในฐานข้อมูลหรือไฟล์แทนหากหน่วยความจำนั้นเข้มข้น
jamylak

@jamylak True แต่ไม่ใช่ทุกสถานการณ์ในโลกแห่งความเป็นจริงหรือข้อมูลที่อนุญาตให้มีความยืดหยุ่น ตัวอย่างเช่นการปั๊มข้อมูลเชิงพื้นที่ "ดั้งเดิม" ผ่านการวิเคราะห์แบบครั้งเดียวบนระบบที่ไม่มีหน่วยความจำมาก จากนั้นยังมีเวลาการเขียนโปรแกรมและรันไทม์ที่ต้องพิจารณา ผู้คนมักหันมาใช้ Python เพราะประหยัดเวลาในการพัฒนา ด้วยคำตอบนี้ฉันกำลังให้ความสนใจกับความจริงที่ว่าหน่วยความจำอาจมีมูลค่าการพิจารณา แต่ฉันระบุในตอนท้ายว่ามันเป็นความชอบส่วนบุคคลในสัญกรณ์ ฉันยังชี้ให้เห็นว่าการรู้ข้อมูลเป็นสิ่งสำคัญ เป็นเพียงเมื่อรายชื่อทั้งหมดเป็นO(n^2) None
เควิน

จะสนใจถ้าคุณมีตัวอย่างการปฏิบัติที่คำตอบนี้เป็นทางออกที่ดีที่สุดฉันมักจะคิดว่าจะมีวิธีที่ดีกว่าในทุกกรณี ตัวอย่างเช่นnumpyจะสามารถจัดการการทำงานประเภทนี้ได้อย่างเหมาะสมที่สุด
jamylak

@jamylak เพื่อความยุติธรรมฉันใช้งานnumpyมาหลายปีแล้ว แต่มันก็เป็นทักษะที่แยกต่างหาก หากLมีการสร้างอินสแตนซ์numpy.arrayแทน Python listแล้วL = L[L != numpy.array(None)](stackoverflow.com/a/25255015/3003133) น่าจะดีกว่าอย่างใดอย่างหนึ่ง แต่ฉันไม่ทราบรายละเอียดการใช้งานสำหรับการประมวลผล vs หน่วยความจำภายใต้ อย่างน้อยก็สร้างอาร์เรย์ความยาวซ้ำของบูลีนสำหรับมาสก์ ไวยากรณ์ของการเปรียบเทียบภายในตัวดำเนินการเข้าถึง (ดัชนี) เป็นวิธีใหม่สำหรับฉัน dtype=objectการสนทนานี้ยังได้นำไปสู่ความสนใจของฉัน
เควิน

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

2
from operator import is_not
from functools import partial   

filter_null = partial(filter, partial(is_not, None))

# A test case
L = [1, None, 2, None, 3]
L = list(filter_null(L))

6
กรุณาให้ข้อมูลรายละเอียดกับ OP ไม่ใช่แค่รหัส
Laurent LAPORTE

1
ฉันทำ. คุณคิดว่ายังไง?
med_abidi

อย่างนี้ไม่ตอบคำถาม OP ลองพิจารณาคำตอบนี้แทน: stackoverflow.com/a/16096769/1513933
Laurent LAPORTE

ใช่คุณถูก. เกิดปัญหากับตัวกรองบางส่วน
med_abidi

2

หากเป็นรายการทั้งหมดคุณสามารถแก้ไขคำตอบของ Sir @ Raymond

L = [ [None], [123], [None], [151] ] no_none_val = list(filter(None.__ne__, [x[0] for x in L] ) ) สำหรับ python 2 อย่างไรก็ตาม

no_none_val = [x[0] for x in L if x[0] is not None] """ Both returns [123, 151]"""

<< list_indice [0] สำหรับตัวแปรในรายการหากตัวแปรไม่ใช่ None >>


1

สมมติว่าเป็นรายการด้านล่าง

iterator = [None, 1, 2, 0, '', None, False, {}, (), []]

นี่จะส่งคืนเฉพาะรายการที่มี bool(item) is True

print filter(lambda item: item, iterator)
# [1, 2]

สิ่งนี้เทียบเท่า

print [item for item in iterator if item]

หากต้องการกรองไม่มี:

print filter(lambda item: item is not None, iterator)
# [1, 2, 0, '', False, {}, (), []]

เทียบเท่ากับ:

print [item for item in iterator if item is not None]

เพื่อรับรายการทั้งหมดที่ประเมินเป็นเท็จ

print filter(lambda item: not item, iterator)
# Will print [None, '', 0, None, False, {}, (), []]
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.