NumPy atau Pandas: Menyimpan tipe array sebagai integer sementara memiliki nilai NaN

160

Apakah ada cara yang disukai untuk menjaga tipe data numpyarray tetap sebagai int(atau int64atau apa pun), sementara masih memiliki elemen di dalamnya terdaftar sebagai numpy.NaN?

Secara khusus, saya mengonversi struktur data in-house menjadi PandF DataFrame. Dalam struktur kami, kami memiliki kolom tipe integer yang masih memiliki NaN (tetapi tipe kolom adalah int). Tampaknya akan menampilkan kembali semuanya sebagai pelampung jika kita menjadikan ini sebagai DataFrame, tetapi kami benar-benar ingin melakukannya int.

Pikiran?

Hal yang dicoba:

Saya mencoba menggunakan from_records()fungsi di bawah pandas.DataFrame, dengan coerce_float=Falsedan ini tidak membantu. Saya juga mencoba menggunakan array bertopeng NumPy, dengan fillNvalue NaN, yang juga tidak berfungsi. Semua ini menyebabkan tipe data kolom menjadi float.

Ely
sumber
Bisakah Anda menggunakan array bertopeng numpy?
mgilson
Saya akan mencobanya. Saya juga mencoba from_recordsfungsi di bawah pandas.DataFrame, dengan coerce_float=False, tetapi tidak berhasil ... masih membuat data baru memiliki tipe float64.
Ely
1
Ya, tidak berhasil. Bahkan dengan array bertopeng, itu masih dikonversi ke float. Sepertinya Pandas berbunyi seperti ini: "Apakah ada NaN di mana saja? ... Lalu semuanya mengambang." Semoga ada jalan keluarnya.
Ely
1
Opsional Dukungan Nullable Integer sekarang secara resmi ditambahkan di panda 0.24.0 - akhirnya :) - silakan temukan jawaban yang diperbarui di bawah. panda 0.24.x catatan rilis
mork

Jawaban:

70

Kemampuan ini telah ditambahkan ke panda (dimulai dengan versi 0.24): https://pandas.pydata.org/pandas-docs/version/0.24/whatsnew/v0.24.0.html#optional-integer-na-support

Pada titik ini, ini memerlukan penggunaan ekstensi dtype Int64 (huruf besar), daripada standar dtype int64 (huruf kecil).

techvslife
sumber
1
Untuk saat ini Anda harus menentukan tipe khusus yang ingin 'Int64'membuatnya berfungsi. Akan lebih baik lagi jika diaktifkan secara default.
Jean Paul
Ini bagus! Namun, ada masalah kecil bahwa PyCharm gagal menampilkan kerangka data di jendela debug jika digunakan dengan cara ini. Anda dapat melihat jawaban saya untuk pertanyaan lain tentang cara memaksa menampilkannya: stackoverflow.com/questions/38956660/… (masalah asli ada yang berbeda, tetapi solusi untuk menampilkan dataframe berfungsi)
Alaa M.
Apakah saya harus menggunakan 'Int64'atau ada sesuatu seperti itu 'Int8'? Ini menggunakan jumlah memori yang gila dibandingkan dengan np.float.
Superdooperhero
'Int8'tampaknya bekerja, tetapi np.floattampaknya memuat dengan cara yang lebih cepat. Masalahnya tampaknya itu tidak melepaskan peralihan memori. Asumsikan pengumpul sampah pada akhirnya akan berjalan.
Superdooperhero
103

NaNtidak dapat disimpan dalam array integer. Ini adalah batasan panda yang diketahui saat ini; Saya telah menunggu kemajuan yang dibuat dengan nilai-nilai NA di NumPy (mirip dengan NAs dalam R), tetapi setidaknya 6 bulan hingga setahun sebelum NumPy mendapatkan fitur-fitur ini, tampaknya:

http://pandas.pydata.org/pandas-docs/stable/gotchas.html#support-for-integer-na

(Fitur ini telah ditambahkan dimulai dengan versi 0.24 dari panda, tetapi perhatikan itu membutuhkan penggunaan ekstensi dtype Int64 (huruf besar), daripada standar dtype int64 (huruf kecil): https://pandas.pydata.org/pandas- docs / version / 0.24 / whatsnew / v0.24.0.html # opsional-integer-na-support )

Wes McKinney
sumber
7
Hai Wes, apakah ada pembaruan tentang ini? Kami mengalami masalah yang menggabungkan kolom dikonversi menjadi int atau mengapung, berdasarkan keberadaan nilai NA di daftar asli. (Menciptakan masalah saat mencoba menggabungkan kerangka data ini)
Carst
1
Tautan yang diperbarui: pandas-docs.github.io/pandas-docs-travis/whatsnew/…
techvslife
8

Jika kinerja bukan masalah utama, Anda dapat menyimpan string sebagai gantinya.

df.col = df.col.dropna().apply(lambda x: str(int(x)) )

Kemudian Anda dapat mencampurnya dengan NaNsebanyak yang Anda inginkan. Jika Anda benar-benar ingin memiliki bilangan bulat, tergantung pada aplikasi Anda, Anda dapat menggunakan -1, atau 0, atau 1234567890, beberapa nilai khusus lainnya untuk mewakiliNaN .

Anda juga dapat menduplikasi kolom untuk sementara: satu seperti yang Anda miliki, dengan float; yang lain eksperimental, dengan int atau string. Kemudian sisipkan assertsdi setiap tempat yang wajar untuk memeriksa apakah keduanya sinkron. Setelah cukup pengujian Anda bisa melepaskan mengapung.

osa
sumber
5

Ini bukan solusi untuk semua kasus, tetapi milik saya (koordinat genomik) Saya telah menggunakan 0 sebagai NaN

a3['MapInfo'] = a3['MapInfo'].fillna(0).astype(int)

Ini setidaknya memungkinkan untuk jenis kolom 'asli' yang tepat untuk digunakan, operasi seperti pengurangan, perbandingan dll berfungsi seperti yang diharapkan

ikan buntal
sumber
5

Pandas v0.24 +

Fungsi untuk mendukung NaNdalam seri integer akan tersedia di v0.24 ke atas. Ada informasi tentang ini di bagian "Apa yang Baru" di v0.24, dan lebih detail di bawah Tipe Data Integer Nullable .

Panda v0.23 dan sebelumnya

Secara umum, yang terbaik untuk bekerja dengan floatseri mana mungkin, bahkan ketika seri ini upcast dari intke floatakibat masuknyaNaN nilai-nilai. Ini memungkinkan perhitungan berbasis NumPy yang vektor di mana, jika tidak, loop tingkat Python akan diproses.

Dokumen memang menyarankan : "Satu kemungkinan adalah menggunakan dtype=objectarray sebagai gantinya." Sebagai contoh:

s = pd.Series([1, 2, 3, np.nan])

print(s.astype(object))

0      1
1      2
2      3
3    NaN
dtype: object

Untuk alasan kosmetik, misalnya keluaran ke file, ini mungkin lebih disukai.

Pandas v0.23 dan sebelumnya: latar belakang

NaNdianggap afloat . The docs saat ini (per v0.23) menentukan alasan mengapa seri integer upcasted ke float:

Dengan tidak adanya dukungan NA kinerja tinggi yang dibangun ke dalam NumPy dari bawah ke atas, korban utama adalah kemampuan untuk mewakili NAs dalam array integer.

Pertukaran ini dibuat sebagian besar karena alasan memori dan kinerja, dan juga agar Seri yang dihasilkan terus menjadi "numerik".

Dokumen juga memberikan aturan untuk upcasting karena NaNdimasukkan:

Typeclass   Promotion dtype for storing NAs
floating    no change
object      no change
integer     cast to float64
boolean     cast to object
jpp
sumber
1

Ini sekarang mungkin, karena panda v 0.24.0

panda 0.24.x catatan rilis Quote: " Pandas telah memperoleh kemampuan untuk menahan dtypes integer dengan nilai yang hilang.

mork
sumber
1

Hanya ingin menambahkan bahwa jika Anda mencoba untuk mengkonversi vektor float (1.143) ke integer (1) yang memiliki NA mengkonversi ke dtype 'Int64' baru akan memberi Anda kesalahan. Untuk menyelesaikan ini, Anda harus membulatkan angka dan kemudian melakukan ".astype ('Int64')"

s1 = pd.Series([1.434, 2.343, np.nan])
#without round() the next line returns an error 
s1.astype('Int64')
#cannot safely cast non-equivalent float64 to int64
##with round() it works
s1.round().astype('Int64')
0      1
1      2
2    NaN
dtype: Int64

Kasus penggunaan saya adalah bahwa saya memiliki seri float yang ingin saya bulatkan ke int, tetapi ketika Anda melakukannya .round () a '* .0' di akhir bilangan tetap, sehingga Anda dapat menjatuhkan 0 itu dari akhir dengan mengkonversi ke int.

Pedro Moisés Camacho Ureña
sumber
0

Jika ada kosong dalam data teks, kolom yang biasanya menjadi bilangan bulat akan dilemparkan ke float64 dtype karena int64 dtype tidak dapat menangani nulls. Ini dapat menyebabkan skema yang tidak konsisten jika Anda memuat beberapa file beberapa dengan kosong (yang akan berakhir sebagai float64 dan lainnya tanpa yang akan berakhir sebagai int64

Kode ini akan mencoba untuk mengkonversi kolom tipe nomor apa pun ke Int64 (sebagai lawan int64) karena Int64 dapat menangani nulls

import pandas as pd
import numpy as np

#show datatypes before transformation
mydf.dtypes

for c in mydf.select_dtypes(np.number).columns:
    try:
        mydf[c] = mydf[c].astype('Int64')
        print('casted {} as Int64'.format(c))
    except:
        print('could not cast {} to Int64'.format(c))

#show datatypes after transformation
mydf.dtypes
Kynrek
sumber