Mengganti nilai kosong (spasi putih) dengan NaN dalam panda

150

Saya ingin menemukan semua nilai dalam kerangka data Pandas yang berisi spasi putih (jumlah sembarang) dan mengganti nilai-nilai itu dengan NaNs.

Adakah ide bagaimana hal ini dapat diperbaiki?

Pada dasarnya saya ingin mengubah ini:

                   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     

Ke dalam ini:

                   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

Saya sudah berhasil melakukannya dengan kode di bawah ini, tetapi manusia itu jelek. Ini bukan Pythonic dan saya yakin itu bukan penggunaan panda yang paling efisien juga. Saya loop melalui setiap kolom dan melakukan penggantian boolean terhadap topeng kolom yang dihasilkan dengan menerapkan fungsi yang melakukan pencarian regex dari setiap nilai, cocok dengan spasi putih.

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

Itu bisa sedikit dioptimalkan dengan hanya mengulangi bidang yang bisa berisi string kosong:

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

Tapi itu tidak banyak perbaikan

Dan akhirnya, kode ini menetapkan string target ke Tidak ada, yang berfungsi dengan fungsi Pandas seperti fillna(), tetapi akan lebih baik untuk kelengkapan jika saya benar-benar dapat memasukkan secara NaNlangsung, bukan None.

Chris Clark
sumber
2
Apa yang benar-benar Anda inginkan adalah dapat digunakan replacedengan regex ... (mungkin ini harus diminta sebagai fitur).
Andy Hayden
3
Saya membuat masalah github untuk fitur ini: github.com/pydata/pandas/issues/2285 . Akan berterima kasih untuk PRs! :)
Chang She
Bagi mereka yang ingin mengubah satu karakter kosong menjadi hilang, lihat solusi sederhana di bawah ini
Ted Petrou

Jawaban:

198

Saya pikir berhasil df.replace(), karena panda 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))

Menghasilkan:

                   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

Seperti yang ditunjukkan oleh Temak , gunakan df.replace(r'^\s+$', np.nan, regex=True)jika data valid Anda berisi spasi putih.

patricksurry
sumber
1
regex adalah bendera boolean. Mungkin maksud Anda pd.Series(["1", "#", "9", " .", None]).replace(r"( +\.)|#", "X", regex=True).valuesyang memberi['1', 'X', '9', 'X', None]
patricksurry
2
2 tahun kemudian, saya telah mengubah jawaban yang diterima untuk ini, sekarang panda mendukungnya. Terima kasih!
Chris Clark
35
CATATAN : jika Anda tidak ingin elemen yang mengandung ruang di tengah diganti dengan penggunaan NaNdf.replace(r'^\s+$', np.nan, regex=True)
Temak
7
Saya mencoba menggunakan ini, tetapi menemukan bahwa r '^ \ s * $' harus menjadi ekspresi yang digunakan. tanpa ^ dan $ itu akan cocok dengan string apa pun dengan dua kekosongan berturut-turut. Juga diubah + ke * untuk memasukkan string kosong "" dalam daftar hal yang akan dikonversi ke NaN
Master Yogurt
1
Saya mencoba solusi Anda dalam kode saya, tetapi tidak berpengaruh. Saya mencoba "energi [" Pasokan Energi "]. Ganti (to_replace =" ... ", value = np.NaN)". Ingin mengubah string "..." ke nilai NaN, tetapi tidak melakukan apa pun dan mengembalikan kerangka data yang sama.
Archan Joshi
50

Jika Anda ingin mengganti string kosong dan catatan dengan spasi saja, jawaban yang benar adalah ::

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

Jawaban yang diterima

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

Tidak mengganti string kosong !, Anda dapat mencoba sendiri dengan contoh yang diberikan yang sedikit diperbarui:

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

Perhatikan, juga bahwa 'untuk' tidak diganti dengan Nan, meskipun mengandung spasi. Catatan selanjutnya, bahwa sederhana:

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

Juga tidak berhasil - coba saja.

Philipp Schwarz
sumber
33

Bagaimana tentang:

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

The applymapFungsi berlaku fungsi untuk setiap sel dataframe tersebut.

BrenBarn
sumber
Sungguh peningkatan yang bagus! Saya seharusnya memikirkan hal ini dalam retrospeksi, tetapi menutup diri untuk melakukan penggantian boolean untuk beberapa alasan. Satu pertanyaan - apakah ada keuntungan untuk melakukan pemeriksaan alas dasar vs hanya str (x) .isspace ()?
Chris Clark
1
@ChrisClark: Tidak ada yang baik, meskipun saya kira itu isinstanceakan sedikit lebih cepat.
BrenBarn
13
Referensi ke "basestring" dalam kode di atas tidak akan berfungsi dalam Python 3 .... dalam hal itu, coba gunakan "str".
Spike Williams
4
Perhatikan bahwa solusi ini tidak menggantikan string kosong ''. Untuk mempertimbangkan juga string kosong, gunakan:d = d.applymap(lambda x: np.nan if isinstance(x, basestring) and (not x or x.isspace()) else x)
tuomastik
18

Saya akan melakukan ini:

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

atau

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

Anda dapat menghapus semua str, lalu ganti str kosong dengan np.nan.

Xiaorong Liao
sumber
lambda x: x.str.strip () harus lambda x: x.strip ()? saran kecil: tambahkan .astype (str) di depan, ini memecahkan masalah data lain untuk saya. Ini berfungsi untuk saya: df = df.apply ['kolom']. Astype (str) .apply (lambda x: x.strip ()). Ganti ('', np.nan)
Wouter
Baris kedua kode menangani kolom int / float dan tipe string. Bagus. Tks!
Kate Stohr
6

Sederhana dari semua solusi:

df = df.replace(r'^\s+$', np.nan, regex=True)
Gil Baggio
sumber
5

Jika Anda mengekspor data dari file CSV, sesederhana ini:

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

Ini akan membuat bingkai data serta mengganti nilai kosong sebagai Na

ibrahim rupawala
sumber
2
Pilihan lain..menggunakan skipinitialspace=Truejuga menghilangkan spasi putih setelah pembatas yang akan menyebabkan spasi putih, string kosong dibaca sebagai nan. Namun jika Anda ingin mempertahankan ruang awal untuk alasan apa pun maka opsi ini bukan pilihan yang baik.
Rajshekar Reddy
1
@RajshekarReddy, tolong beri jawaban ini di suatu tempat, ini brilian!
User2321
2

Untuk solusi yang sangat cepat dan sederhana di mana Anda memeriksa kesetaraan terhadap nilai tunggal, Anda dapat menggunakan maskmetode ini.

df.mask(df == ' ')
Ted Petrou
sumber
1

Ini semua dekat dengan jawaban yang benar, tetapi saya tidak akan mengatakan ada memecahkan masalah sambil tetap paling mudah dibaca oleh orang lain membaca kode Anda. Saya akan mengatakan bahwa jawaban itu adalah kombinasi dari Jawaban BrenBarn dan komentar tuomasttik di bawah jawaban itu . Jawaban BrenBarn menggunakan isspacebuiltin, tetapi tidak mendukung penghapusan string kosong, seperti yang diminta OP, dan saya akan cenderung menganggapnya sebagai kasus penggunaan standar untuk mengganti string dengan null.

Saya menulis ulang dengan .apply, sehingga Anda dapat menyebutnya di pd.Seriesatau pd.DataFrame.


Python 3:

Untuk mengganti string kosong atau string dari seluruh ruang:

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

Untuk mengganti string seluruh ruang:

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

Untuk menggunakan ini di Python 2, Anda harus menggantinya strdengan basestring.

Python 2:

Untuk mengganti string kosong atau string dari seluruh ruang:

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

Untuk mengganti string seluruh ruang:

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

Ini berhasil untuk saya. Ketika saya mengimpor file csv saya, saya menambahkan na_values ​​= ''. Spasi tidak termasuk dalam nilai NaN default.

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

sambrowne
sumber
0

Anda juga dapat menggunakan filter untuk melakukannya.

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)
ERIC
sumber
Setiap baris kode ini (tidak termasuk data) salah.
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
Jayantha
sumber
0

Ini bukan solusi yang elegan, tetapi yang tampaknya berhasil adalah menabung ke XLSX dan kemudian mengimpornya kembali. Solusi lain di halaman ini tidak berhasil untuk saya, tidak yakin mengapa.

data.to_excel(filepath, index=False)
data = pd.read_excel(filepath)
David Kong
sumber