การแทนที่ค่าว่าง (white space) ด้วย NaN ใน pandas


150

ฉันต้องการค้นหาค่าทั้งหมดใน Pandas dataframe ที่มีช่องว่าง (จำนวนเท่าใดก็ได้) และแทนที่ค่าเหล่านั้นด้วย NaNs

ความคิดใด ๆ ที่สามารถปรับปรุงได้?

โดยทั่วไปฉันต้องการที่จะเปิดนี้:

                   A    B    C
2000-01-01 -0.532681  foo    0
2000-01-02  1.490752  bar    1
2000-01-03 -1.387326  foo    2
2000-01-04  0.814772  baz     
2000-01-05 -0.222552         4
2000-01-06 -1.176781  qux     

เป็นนี้

                   A     B     C
2000-01-01 -0.532681   foo     0
2000-01-02  1.490752   bar     1
2000-01-03 -1.387326   foo     2
2000-01-04  0.814772   baz   NaN
2000-01-05 -0.222552   NaN     4
2000-01-06 -1.176781   qux   NaN

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

for i in df.columns:
    df[i][df[i].apply(lambda i: True if re.search('^\s*$', str(i)) else False)]=None

มันสามารถปรับให้เหมาะสมเพียงเล็กน้อยโดยวนซ้ำผ่านเขตข้อมูลที่อาจมีสตริงว่าง:

if df[i].dtype == np.dtype('object')

แต่นั่นไม่ใช่การปรับปรุงมากนัก

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


2
สิ่งที่คุณต้องการจริงๆคือสามารถใช้replaceกับ regex ... (บางทีนี่อาจจะถูกร้องขอเป็นคุณสมบัติ)
Andy Hayden

3
ฉันทำปัญหา GitHub สำหรับคุณลักษณะนี้: github.com/pydata/pandas/issues/2285 จะขอบคุณสำหรับ PRs! :)
Chang She

สำหรับผู้ที่ต้องการเปลี่ยนอักขระว่างเปล่าให้หายไปให้ดูวิธีแก้ปัญหาง่ายๆด้านล่างนี้
Ted Petrou

คำตอบ:


198

ฉันคิดว่าdf.replace()ทำงานได้ดีเนื่องจากpandas 0.13 :

df = pd.DataFrame([
    [-0.532681, 'foo', 0],
    [1.490752, 'bar', 1],
    [-1.387326, 'foo', 2],
    [0.814772, 'baz', ' '],     
    [-0.222552, '   ', 4],
    [-1.176781,  'qux', '  '],         
], columns='A B C'.split(), index=pd.date_range('2000-01-01','2000-01-06'))

# replace field that's entirely space (or empty) with NaN
print(df.replace(r'^\s*$', np.nan, regex=True))

ผลิต:

                   A    B   C
2000-01-01 -0.532681  foo   0
2000-01-02  1.490752  bar   1
2000-01-03 -1.387326  foo   2
2000-01-04  0.814772  baz NaN
2000-01-05 -0.222552  NaN   4
2000-01-06 -1.176781  qux NaN

ดังที่Temakชี้ให้เห็นใช้df.replace(r'^\s+$', np.nan, regex=True)ในกรณีที่ข้อมูลที่ถูกต้องของคุณมีช่องว่างสีขาว


1
regex เป็นธงบูลีน บางทีคุณอาจหมายถึงpd.Series(["1", "#", "9", " .", None]).replace(r"( +\.)|#", "X", regex=True).valuesที่ให้['1', 'X', '9', 'X', None]
patricksurry

2
2 ปีต่อมาฉันได้เปลี่ยนคำตอบที่ได้รับการยอมรับในขณะนี้ที่แพนด้าสนับสนุน ขอบคุณ!
Chris Clark

35
หมายเหตุ : หากคุณไม่ต้องการให้องค์ประกอบที่มีช่องว่างตรงกลางถูกแทนที่ด้วยการใช้ NaNdf.replace(r'^\s+$', np.nan, regex=True)
Temak

7
ฉันพยายามใช้สิ่งนี้ แต่พบว่า r '^ \ s * $' ควรเป็นนิพจน์ที่จะใช้ โดยไม่ต้อง ^ และ $ มันจะจับคู่สตริงใด ๆ ที่มีช่องว่างสองช่องติดกัน เปลี่ยนด้วย + เป็น * เพื่อรวมสตริงว่าง "" ในรายการสิ่งที่ต้องแปลงเป็น NaN
Master Yogurt

1
ฉันกำลังลองวิธีแก้ปัญหาของคุณในรหัสของฉัน แต่มันไม่มีผล ฉันกำลังลอง "พลังงาน [" การจัดหาพลังงาน "] แทนที่ (to_replace =" ... ", ค่า = np.NaN)" ต้องการเปลี่ยนสตริง "... " เป็นค่า NaN แต่ไม่ทำอะไรเลยและส่งคืนดาต้าเฟรมเดียวกัน
Archan Joshi

50

หากคุณต้องการแทนที่สตริงว่างและระเบียนด้วยช่องว่างเท่านั้นคำตอบที่ถูกต้องคือ :

df = df.replace(r'^\s*$', np.nan, regex=True)

คำตอบที่ได้รับการยอมรับ

df.replace(r'\s+', np.nan, regex=True)

อย่าแทนที่สตริงว่าง! คุณสามารถลองด้วยตัวคุณเองด้วยตัวอย่างที่ได้รับการปรับปรุงเล็กน้อย:

df = pd.DataFrame([
    [-0.532681, 'foo', 0],
    [1.490752, 'bar', 1],
    [-1.387326, 'fo o', 2],
    [0.814772, 'baz', ' '],     
    [-0.222552, '   ', 4],
    [-1.176781,  'qux', ''],         
], columns='A B C'.split(), index=pd.date_range('2000-01-01','2000-01-06'))

โปรดทราบว่า 'fo o' จะไม่ถูกแทนที่ด้วยน่านแม้ว่าจะมีช่องว่าง หมายเหตุเพิ่มเติมว่าง่าย:

df.replace(r'', np.NaN)

ใช้งานไม่ได้ - ลองใช้ดู


33

เกี่ยวกับ:

d = d.applymap(lambda x: np.nan if isinstance(x, basestring) and x.isspace() else x)

applymapฟังก์ชั่นใช้ฟังก์ชั่นไปยังเซลล์ของ dataframe ทุก


เป็นการปรับปรุงที่ดีมาก! ฉันควรจะคิดถึงเรื่องนี้ในการหวนกลับมา แต่ก็หยุดคิดทำบูลีนแทนด้วยเหตุผลบางอย่าง คำถามหนึ่งข้อ - มีความได้เปรียบในการตรวจสอบ basestring เทียบกับ str (x) .isspace () หรือไม่
Chris Clark

1
@ChrisClark: อย่างใดอย่างหนึ่งได้ดีแม้ว่าฉันจะคาดเดาว่าisinstanceจะเร็วขึ้นเล็กน้อย
BrenBarn

13
การอ้างอิงถึง "basestring" ในรหัสด้านบนจะไม่ทำงานใน Python 3 .... ในกรณีนั้นลองใช้ "str" ​​แทน
Spike Williams

4
''โปรดทราบว่าการแก้ปัญหานี้ไม่ได้แทนที่สตริงที่ว่างเปล่า ในการพิจารณาสตริงว่างให้ใช้:d = d.applymap(lambda x: np.nan if isinstance(x, basestring) and (not x or x.isspace()) else x)
tuomastik

18

ฉันจะทำสิ่งนี้:

df = df.apply(lambda x: x.str.strip()).replace('', np.nan)

หรือ

df = df.apply(lambda x: x.str.strip() if isinstance(x, str) else x).replace('', np.nan)

คุณสามารถตัด STR ทั้งหมดแล้วเปลี่ยน STR np.nanว่างเปล่ากับ


แลมบ์ดา x: x.str.strip () ควรเป็นแลมบ์ดา x: x.strip ()? ข้อเสนอแนะเล็ก ๆ น้อย ๆ : เพิ่ม .astype (str) อยู่ข้างหน้านี่จะแก้ปัญหาข้อมูลอื่น ๆ สำหรับฉัน สิ่งนี้ใช้ได้กับฉัน: df = df.apply ['column']. astype (str) .apply (lambda x: x.strip ()). แทนที่ ('', np.nan)
Wouter

โค้ดบรรทัดที่สองจัดการทั้งคอลัมน์ int / float และคอลัมน์ชนิดสตริง ดี Tks!
Kate Stohr


5

หากคุณกำลังส่งออกข้อมูลจากไฟล์ CSV อาจเป็นเรื่องง่ายเช่นนี้:

df = pd.read_csv(file_csv, na_values=' ')

สิ่งนี้จะสร้าง data frame รวมถึงแทนที่ค่าว่างเป็น Na


2
option..using อีกskipinitialspace=Trueยังเอาช่องว่างใด ๆ หลังจากที่คั่นซึ่งจะทำให้เกิดความยาวของพื้นที่สีขาวใด ๆ nanสตริงที่ว่างเปล่าเพื่อจะอ่านเป็น อย่างไรก็ตามหากคุณต้องการรักษาช่องว่างเริ่มต้นด้วยเหตุผลใดก็ตามตัวเลือกนี้ไม่ใช่ตัวเลือกที่ดี
Rajshekar Reddy

1
@Rajshekar เรดดี้คุณช่วยกรุณาใส่นี่เป็นคำตอบที่ไหนสักแห่งนี่ยอดเยี่ยมมาก!
User2321

2

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

df.mask(df == ' ')

1

ทั้งหมดนี้อยู่ใกล้กับคำตอบที่ถูกต้อง แต่ฉันจะไม่พูดอะไรแก้ปัญหาในขณะที่คนอื่น ๆ อ่านโค้ดของคุณได้ ฉันจะบอกว่าคำตอบคือการรวมกันของคำตอบของ BrenBarn และความคิดเห็นของ tuomasttik ด้านล่างคำตอบนั้น คำตอบของ BrenBarn ใช้isspacebuiltin แต่ไม่สนับสนุนการลบสตริงว่างเปล่าตามที่ OP ร้องขอและฉันมักจะอ้างว่าเป็นกรณีการใช้งานมาตรฐานของการแทนที่สตริงด้วย null

ฉันเขียนมันด้วย.applyเพื่อให้คุณสามารถเรียกมันบนหรือpd.Seriespd.DataFrame


Python 3:

ในการแทนที่สตริงว่างหรือสตริงของช่องว่างทั้งหมด:

df = df.apply(lambda x: np.nan if isinstance(x, str) and (x.isspace() or not x) else x)

ในการแทนที่สตริงของช่องว่างทั้งหมด:

df = df.apply(lambda x: np.nan if isinstance(x, str) and x.isspace() else x)

ที่จะใช้ในหลาม 2 คุณจะต้องแทนที่ด้วยstrbasestring

Python 2:

ในการแทนที่สตริงว่างหรือสตริงของช่องว่างทั้งหมด:

df = df.apply(lambda x: np.nan if isinstance(x, basestring) and (x.isspace() or not x) else x)

ในการแทนที่สตริงของช่องว่างทั้งหมด:

df = df.apply(lambda x: np.nan if isinstance(x, basestring) and x.isspace() else x)

1

สิ่งนี้ใช้ได้สำหรับฉัน เมื่อฉันนำเข้าไฟล์ csv ของฉันฉันเพิ่ม na_values ​​= '' ช่องว่างไม่รวมอยู่ในค่า NaN เริ่มต้น

df = pd.read_csv (filepath, na_values ​​= '')


0

คุณยังสามารถใช้ตัวกรองเพื่อทำ

df = PD.DataFrame([
    [-0.532681, 'foo', 0],
    [1.490752, 'bar', 1],
    [-1.387326, 'foo', 2],
    [0.814772, 'baz', ' '],     
    [-0.222552, '   ', 4],
    [-1.176781,  'qux', '  '])
    df[df=='']='nan'
    df=df.astype(float)

ทุกบรรทัดของรหัสนี้ (ไม่รวมข้อมูล) เป็นความผิดพลาด
Julius

0
print(df.isnull().sum()) # check numbers of null value in each column

modifiedDf=df.fillna("NaN") # Replace empty/null values with "NaN"

# modifiedDf = fd.dropna() # Remove rows with empty values

print(modifiedDf.isnull().sum()) # check numbers of null value in each column

0

นี่ไม่ใช่วิธีการแก้ปัญหาที่สง่างาม แต่สิ่งที่ดูเหมือนว่าจะใช้ได้คือประหยัดไปยัง XLSX แล้วนำเข้ากลับมา โซลูชันอื่น ๆ ในหน้านี้ไม่ได้ผลสำหรับฉันไม่แน่ใจว่าทำไม

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